A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 传鹏 黑马粉丝团   /  2019-3-10 10:35  /  1330 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 传鹏 于 2019-3-10 10:36 编辑

浅谈函数上下文调用模式

通过上一期的《JavaScript中的this到底指向谁?》,我们知道了在默认情况下this的指向,在文末也提到了我们是可以通过种种手段来修改this的默认指向,很明显啦,修改JavaScript中的this指向就是本期的重点。要实现这个需求,就要使用函数上下文调用模式,接下来就和大家共同探讨下函数上下文调用模式 -- callapplybind

一、我们先从语法上了解下这三种方法。
1.1call();
语法:函数名.call(期望函数内部this指向谁, 参数1,参数2....);
14行普通方式调用getSum()函数,this指向window;
15行用call()的方式调用getSum()函数, 1个参数传入”期望getSum函数中的this指向”,后面的参数依次传入getSum函数的参数即可。此时getSum函数一样被执行,不同的是getSum函数中的this就指向了obj对象,同时也把100,200分别赋值给形参ab. 执行结果如下:
这样我们就轻松实现了修改函数内部this指向.

1.2apply();
语法:函数名.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.3bind();
语法:函数名.bind( this的新指向 ,可以写参数也可以不写参数 );
需要注意的是函数用bind()的方式调用并不会执行该函数,而是会返回一个函数体一模一样但是修改了this指向后的函数。
34行普通方式调用getSum()函数,this指向window;
36行虽然用bind()的方式调用getSum()函数,但是此时并不会执行getSum()函数,而是会返回一个和getSum()函数的函数体一模一样但是this已经修改成obj对象的函数了,这个函数被fn变量接收。 执行结果如下:(只有一次函数体被执行,就是34行的调用)。
此时如果调用fn()函数,就相当于执行getSum()函数,但是getSum函数中的this已经被修改成了obj对象,调用代码和执行结果如下:
前面介绍bind()的语法说除了this的新指向,参数可以写也可以不写,所以3638行代码也可以写成如下这样:
执行结果一样,如下:


二、上下文调用模式注意细节:
2.1 大家都知道javascript中的函数(普通函数、构造函数)本质上是一个对象,是由Function()构造函数实例化出来的对象,而callapplybind这三个方法都是定义在Function.prototype原型中的,那么意味着javascript中的所有函数都可以点出这三个方法来。

2.2 如果使用函数上下文模式调用函数,第一个参数不是指向一个对象,而是指向一个基本数据类型的值,那么函数中this的指向又该指向谁呢?代码如下:
525354行,他们使用call()的方式调用foo()函数,this分别指向Number包装类型对象、String包装类型对象和Boolean包装类型对象;
55行,56行,57行,58行都指向window对象。 执行结果如下: (当然前提是非严格模式下,严格模式下修改thisnull或者undefined都不允许指向window,关于严格模式另起篇章再究)。
        


2.3 哪个函数使用上下文模式调用,修改的this就是哪个函数的,代码如下:
上述这道题不要以为第73testOne函数被上下文模式调用,所以在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方法中的thisweiArr这个伪数组, 大家在上一篇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的面纱!》


1 个回复

倒序浏览





















回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马