问题复现
有这么一道题:
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()