a.call(b)和a.call.call(b)和a.call.call.call(b)的执行结果的疑惑

140 阅读2分钟

segmentfault.com/a/119000001…

被一个问题困扰:

function a () {console.log(this, 'a')}

function b () {console.log(this, 'b')}

\

a.call(b); //ƒ b() {console.log(this,'b')} "a"

这个数很容易理解,a 调用call方法,当前的this对象为b,是我们对call方法的常规使用

\

那么

a.call.call(b, 'b') //String {"b", fn: ƒ}0: "b"length: 1[[Prototype]]:                 String[[PrimitiveValue]]: "b" "b"

这个结果很让人疑惑,后来找到一个大致的call方法的简单实现,调试了一遍,才理解的透彻。

call方法的实现:

Function.prototype.call_ = function(context,...args){

    context = context ? Object(context):window

    context.fn = this  

    let r = context.fn(...args) // 通过调用context.fn 来改变调用者 实现fn的this指向context 即改变a内部的this

    delete context.fn   //删除属性

    return r  // 返回执行的结果 

}

\

我们先看a.call_(b),执行过程:

a.call_.png

此时call方法中的参数为context 也就是函数b,而this也是call的调用者a,将当前的方法也是this赋值给conetxt, 执行context.fn(...args),也就是将fn的调用者指向context,也是方法b,那么调用context.fn(...args),就执行到a方法中,而此时的调用者也方法中的this对象,也就是方法b,所以打印出来的结果是:ƒ b() {console.log(this,'b')} "a"

\

下面我们来看a.call_.call_(b, 'b'),执行过程:\

image.png

此时的context为函数b,而当前的this指向的是当前的call函数,当我们执行context.fn(...args)之后,会继续进入到call函数中,而当前的conext是字符串'b',this就是方法的调用者b函数,而此时,context会转成String 对象,当执行context.fn(...args)时,其实是String对象调用b方法,那么打印出来的便是:

String {"b", fn: ƒ} "b"

\

当这一便执行完之后,继续执行下面的delete context.fn;return r ;然后输出结果;

\

所以后面无论有多少个call方法,输出的结果都是一样的:

a.call_.call_.call_(b, 'b')

String {"b", fn: ƒ} "b"

\

a.call_.call_.call_.call_(b, 'b')\

String {"b", fn: ƒ} "b"

\