被一个问题困扰:
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),执行过程:
此时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'),执行过程:\
此时的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"
\