问题复现
有这么一道题:
function fn1(){ console.log(`fn1 ${this}`)} function fn2(){ console.log(`fn2 ${this}`)} fn1.call(fn2);// fn1 function fn2(){console.log(`fn2 ${this}`)}fn1.call.call(fn2);// fn2 [object Window]fn1.call.call.call.call(fn2);// fn2 [object Window]
问题剖析
对于call
咱们个别晓得这些:
- 用处
次要作用是扭转函数的this
指向,能够用于实现继承 - 传参:
function.call(thisArg, arg1, arg2, ...)
- 执行步骤
- 改边函数的
this
指向 - 传参并执行函数
所以对于
fn1.call(fn2); // fn1 function fn2(){console.log(`fn2 ${this}`)}
这样的输入我并不意外,因为这段代码的意思我了解为扭转将fn1
的this
指向fn2
并执行。实质上执行的还是执行fn1
。
而对于
fn1.call.call(fn2);// fn2 [object Window]
这个样的输入就比拟困惑了,那么这种该怎了解呢?
问题解决
要了解这个输入咱们须要晓得这两点:
key_1: call
办法是哪里来的?
首先call
是Function
基类原型上的办法,也就是Function.prototype.call
。
所以fn1.call.call(fn2)
相当于Function.prototype.call.call(fn2)
key_2: call
的执行过程是什么样的
用伪代码示意,call
的执行过程大略是这样(不思考传参的状况)的
Function.prototype.call = function (context) { // 1. 首先明确第一个this,因为这是个原型上的办法,是公共的办法,所以咱们须要晓得是谁在调用call这个办法,谁在调用这个this就是谁,我给这里的this起个别名就是this_caller // 2. 扭转this_caller中的this指向context // 3. 执行this_caller() this();}
晓得这两点之后,联合fn1.call.call(fn2)
实例剖析就是:
- 依据
key_1
,咱们晓得所以,fn1.call.call(fn2)
的第一个call
是Function.prototype.call
,第二个call
是通过Function.prototype.call
的原型链找到的。 依据下面失去的信息,咱们把能够把
fn1.call.call(fn2)
看做(fn1.call).call(fn2)
。联合key_2
咱们能够晓得,第二个call
起了这些作用:- 首选明确这个
call
的this_caller
是fn1.call
fn1.call
外面的this
指向变成了fn2
fn1.call
的变成了fn2
- 首选明确这个
3.整体来看,就是fn1.call.call(fn2)
变成了fn2()
下面的问题关键在于:fn1
外面尽管没有this
,不受call
扭转this指向的影响,然而fn1.call
外面有this
,受call
扭转this
指向的影响。
如果可能了解这一点的话,那这个问题就比拟好了解了。
参考文档
- JavaScript中的call、apply、bind深刻了解
- JavaScript中对于call函数的一道面试题
- Function.prototype.call()