乐趣区

关于javascript:如何理解fn1callcallfn2的结果

问题复现

有这么一道题:

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()
退出移动版