引子:模拟bind函数
实现一个$bind
函数模拟bind
函数。
// no-log
Function.prototype.$bind = function (obj) {
let self = this
return function () {
self.apply(obj, arguments)
}
}
下面进行测试
// log
let obj1 = { id: 0 }
let obj2 = { id: 1 }
function sayId() {
console.log(this.id)
}
sayId.bind(obj1)() // ES5实现的bind
sayId.$bind(obj2)() // 我们自定义的$bind
很好,达到了我们想要的效果。
有何不同
然后探究我们的$bind
和真实的bind
有何差异。下面两部分代码分别使用了$bind
和bind
,代码的其余部分完全相同,但是其输出却不同。
// log
function foo(something) {
this.a = something
}
let obj1 = {}
let fooWithBind = foo.$bind(obj1) // 这里不同
let obj2 = new fooWithBind(999)
console.log(obj1.a)
console.log(obj2.a)
// log
function foo(something) {
this.a = something
}
let obj1 = {}
let fooWithBind = foo.bind(obj1) // 这里不同
let obj2 = new fooWithBind(999)
console.log(obj1.a)
console.log(obj2.a)
原因:ES5内置的bind
更加复杂,主要体现在 this
的绑定的优先级上:它会判断硬绑定函数是否被new
调用,如果是的话就会使用新创建的this
代替硬绑定的this
。
总结
通过自定义一个残缺的bind函数,回顾了判断this指向的相关知识点。可按照如下步骤进行。
- 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。 如:
var bar = new foo()
- 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是 指定的对象。 如:
var bar = foo.call(obj2)
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。如:
var bar = obj1.foo()
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象 如:
var bar = foo()
参考
[1]你不知道Javascript P94