一次看到个有趣的问题是如果多次 bind 呢,会有什么效果?
const people1 = {
age: 18
}
const people2 = {
age: 19
}
const people3 = {
age: 20
}
const girl = {
getAge: function() {
return this.age
}
}
const callFn = girl.getAge.bind(people1)
const callFn1 = girl.getAge.bind(people1).bind(people2)
const callFn2 = girl.getAge.bind(people1).bind(people2).bind(people3)
console.log(callFn(), callFn1(), callFn2())
// 18 18 18
这里都输出 18 ,而没有期待中的 19 和 20 ,原因是在 Javascript 中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次及以后的 bind 是无法生效的
再看一段示例
const tempFn = function () {
console.log(this, [...arguments])
}
const cont1 = tempFn.bind({
name: '渣渣逆天'
}, 1)
cont1.call({
age: 24
}, 2)
// {name: "渣渣逆天"} [1]
const cont2 = cont1.bind({
apper: 'bueaty'
}, 2)
cont2()
// {name: "渣渣逆天"} [1, 2]
const cont3 = cont2.bind({
fat: 'thin'
}, 3)
cont3()
// {name: "渣渣逆天"} [1, 2, 3]
从上面的代码执行结果中我们发现一点,第一次 bind 绑定的对象是固定的,也就是后面通过 bind 或者 call 再次绑定的时候,就无法修改这个 this 了,从 ES5 文档中我们能找到答案
When the [[Call]] internal method of a function object, F, which was created using the bind function is called with a this value and a list of arguments ExtraArgs, the following steps are taken:
Let boundArgs be the value of F’s [[BoundArgs]] internal property.
Let boundThis be the value of F’s [[BoundThis]] internal property.
Let target be the value of F’s [[TargetFunction]] internal property.
Let args be a new list containing the same values as the list boundArgs in the same order followed by the same values as the list ExtraArgs in the same order.
Return the result of calling the [[Call]] internal method of target providing boundThis as the this value and providing args as the arguments.
这段话中说到如果我们在一个由 bind 创建的函数中调用 call,假设是 x.call(obj, y, z, …) 并且传入 this,和参数列表的时候会执行下面的步骤:
首先用三个参数分别保存函数x函数的内部属性中存的this值、目标函数和参数 列表。
然后执行目标函数的内部 call 函数,也就是执行目标函数的代码,并且传入1中保存的 this 和实参(这里的实参是目标函数本来就有的也就是 bind 时传入的实参加上调用 call 时传的实参)
重点在1中,从 ES5 的 bind 函数说明中我们知道,当我们用一个函数调用 bind 的时候,返回的函数中会保存这三个参数。所以最后调用 call 的时候执行的函数是目标函数,也就是调用了 bind 的函数,传入的 this 也是 bind 调用时传入的,这些都是无法被修改的了,但是参数是调用 bind 和 call 时的叠加,这是我们唯一可以修改的地方。执行两次 bind 的原理可以参考 bind 的源码,和 call 的差不多,也是目标函数和 this 是被固定的了,只有参数列表会叠加