上下文调用

98 阅读7分钟
浅谈函数上下文调用模式

通过上一期的《JavaScript中的this到底指向谁?》,我们知道了在默认情况下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的面纱!》