半小时搞明白JavaScript中函数的上下文调用模式
Posted 黑马程序员深圳中心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了半小时搞明白JavaScript中函数的上下文调用模式相关的知识,希望对你有一定的参考价值。
通过上一期的没有学习的小伙伴请自行点击文字跳转阅读哈,我们知道了在默认情况下this的指向,在文末也提到了我们是可以通过种种手段来修改this的默认指向,很明显啦,修改javascript中的this指向就是本期的重点。
要实现这个需求,就要使用函数上下文调用模式,接下来就和大家共同探讨下函数上下文调用模式 -- call、apply、bind。
一、我们先从语法上了解下这三种方法
1.1: call();
语法:函数名.call(期望函数内部this指向谁, 参数1,参数2....);
第14行普通方式调用getSum()函数,this指向window;
第15行用call()的方式调用getSum()函数, 第1个参数传入”期望getSum函数中的this指向”,后面的参数依次传入getSum函数的参数即可。此时getSum函数一样被执行,不同的是getSum函数中的this就指向了obj对象,同时也把100,200分别赋值给形参a和b. 执行结果如下:
这样我们就轻松实现了修改函数内部this指向.
1.2:apply();
语法:函数名.apply(this的新指向 , 数组或者伪数组);
注意apply()和call()的区别在于apply()只有2个参数,第一个参数是this的新指向,第二个参数是数组或者伪数组,调用的时候会把第二个参数(数组或伪数组)的元素依次的赋值给被调用函数的形参。
第24行普通方式调用getSum()函数,this指向window;
第25行用apply()的方式调用getSum()函数,此时getSum()函数中的this就指向了obj对象,同时把数组的元素100,200,300依次赋值给getSum()函数的形参a,b,c ; 执行结果如下:
1.3:bind();
语法:函数名.bind( this的新指向 ,可以写参数也可以不写参数 );
需要注意的是函数用bind()的方式调用并不会执行该函数,而是会返回一个函数体一模一样但是修改了this指向后的函数。
第34行普通方式调用getSum()函数,this指向window;
第36行虽然用bind()的方式调用getSum()函数,但是此时并不会执行getSum()函数,而是会返回一个和getSum()函数的函数体一模一样但是this已经修改成obj对象的函数了,这个函数被fn变量接收。 执行结果如下:(只有一次函数体被执行,就是34行的调用)。
此时如果调用fn()函数,就相当于执行getSum()函数,但是getSum函数中的this已经被修改成了obj对象,调用代码和执行结果如下:
前面介绍bind()的语法说除了this的新指向,参数可以写也可以不写,所以36行38行代码也可以写成如下这样:
执行结果一样,如下:
二、上下文调用模式注意细节:
2.1 大家都知道javascript中的函数(普通函数、构造函数)本质上是一个对象,是由Function()构造函数实例化出来的对象,而call、apply、bind这三个方法都是定义在Function.prototype原型中的,那么意味着javascript中的所有函数都可以点出这三个方法来。
2.2 如果使用函数上下文模式调用函数,第一个参数不是指向一个对象,而是指向一个基本数据类型的值,那么函数中this的指向又该指向谁呢?代码如下:
52行53行54行,他们使用call()的方式调用foo()函数,this分别指向Number包装类型对象、String包装类型对象和Boolean包装类型对象;
55行,56行,57行,58行都指向window对象。 执行结果如下: (当然前提是非严格模式下,严格模式下修改this为null或者undefined都不允许指向window,关于严格模式另起篇章再究)。
2.3 哪个函数使用上下文模式调用,修改的this就是哪个函数的,代码如下:
上述这道题不要以为第73行testOne函数被上下文模式调用,所以在testOne函数的函数体中调用testTwo函数,他的this也指向obj对象,不是的。哪个函数被上下文模式调用,那哪个函数的this才会发生改变。
testOne函数被上下文模式调用,所以testOne函数的this指向obj对象; 而在testOne函数的函数体中第67行调用testTwo函数还是普通调用,所以testTwo函数的this还是window对象。执行结果如下:
三、函数上下文调用模式的使用场景究竟
3.1 元素都是整数的数组求最大值。
如果要求出一个整数数组中的最大值,传统的做法是遍历数组,元素两两比较,最后得到最大值,但是这样较繁琐,所以我们联想到js中的Math对象提供的max()方法可以求一堆数中的最大值,所以我们就用apply的方式调用max()方法,不修改它this的指向,只是利用apply()方法的语法特点把arr数组的元素依次的交给Max()方法,这样就能得到arr数组中的最大值。执行结果如下:
3.2 伪数组转换成真数组
原始的做法需要遍历这个伪数组weiArr,然后把元素一个一个的往声明的真数组arr中添加,需要用到遍历所以较复杂。执行结果如下:
用函数上下文调用模式来处理该问题就较容易:
做法1利用apply()方法的特性把weiArr这个伪数组中的元素依次的交给push方法,减去了遍历这个伪数组步骤。执行结果如下:
做法2使用call的方式调用slice方法,修改slice方法中的this为weiArr这个伪数组, 大家在上一篇this的指向中都知道谁调用方法,方法中的this就是谁, 那现在slice方法中的this被修改成了weiArr这个伪数组,那给人的感觉就是weiArr这个伪数组在调用这个方法。(有同学可能会问那为什么weiArr不直接调用slice方法呢?原因是因为他是伪数组,不能直接点出数组的方法来。) 而slice这个方法就是用来做截取的,题中只给一个参数0,意味着从第0个元素开始截取一直到末尾。 所以arr2就是一个拥有和伪数组相同元素的真数组。 执行结果如下:
做法3利用的是数组的concat方法,concat方法允许数组arr3连接一个个的数值作为他的新元素,所以这里用apply的方式调用concat,就是为了把weiArr这个伪数组里面的元素一个个的交给concat函数。执行结果如下:
3.3 借用构造函数继承
在Student构造函数里面,121行(注释)、122行(注释)、123行都可以借用Person构造函数中的赋值代码给自己实例化出来的对象赋值。 执行结果如下:
3.4 检测数据类型
这里都是在借用Object.prototype原型中的toString方法,而这个toString方法明确规定了返回的结果是:”[object type]”,其中type是数据的类型。所以用这种方式可以检测所有数据的数据类型。 执行结果如下:
好啦,本期干货就全部交给大家了,动动小手自己试一下有没有掌握吧!
下一期预告:
《带你揭开BFC的面纱!》
深圳黑马各学科火热报名开启~
黑妹WeChat:heimei0755
黑妹QQ:553319905
前端与移动开发基础班
2018-12-02
Go语言与区块链基础班
2018-12-03
大数据基础班
2018-12-20
人工智能+Python基础班
2018-12-03
新媒体+短视频就业班
2018-11-07
C/C++基础班
2018-12-10
软件测试基础班
2018-12-08
php+H5全栈工程师基础班
2018-11-18
UI/UE设计基础班
2018-12-10
JavaEE基础班
2018-11-30
产品经理
2018-12-10
我是黑马程序员深圳中心黑妹
遇上一个对的人很难
但是,遇到黑妹很简单
识别下方二维码关注我哦!
技术|热点|资源|资讯
点击阅读原文,体验免费试听课程
以上是关于半小时搞明白JavaScript中函数的上下文调用模式的主要内容,如果未能解决你的问题,请参考以下文章