问题复现

有这么一道题:

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, ...)
  • 执行步骤
  1. 改边函数的this指向
  2. 传参并执行函数

所以对于

fn1.call(fn2); // fn1 function fn2(){console.log(`fn2 ${this}`)}

这样的输入我并不意外,因为这段代码的意思我了解为扭转将fn1this指向fn2并执行。实质上执行的还是执行fn1
而对于

fn1.call.call(fn2);// fn2 [object Window]

这个样的输入就比拟困惑了,那么这种该怎了解呢?

问题解决

要了解这个输入咱们须要晓得这两点:

key_1: call办法是哪里来的?

首先callFunction基类原型上的办法,也就是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)实例剖析就是:

  1. 依据key_1,咱们晓得所以,fn1.call.call(fn2)的第一个callFunction.prototype.call,第二个call是通过Function.prototype.call的原型链找到的。
  2. 依据下面失去的信息,咱们把能够把fn1.call.call(fn2)看做(fn1.call).call(fn2)。联合key_2咱们能够晓得,第二个call起了这些作用:

    1. 首选明确这个callthis_callerfn1.call
    2. fn1.call外面的this指向变成了fn2
    3. 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()