调用位置:函数在代码中被调用的
位置(而不是声明的位置)。
调用栈:到达当前执行位置所调用的所有函数。
默认绑定
独立的函数调用
function foo() {
console.log( this.a )
}
var a = 2;
foo() // 2
在 strict mode
模式下 this 绑定到 undefiend,在 non-strict mode
下绑定到 全局对象。
隐式绑定
当 foo() 被调用时,它的落脚点指向 obj 对象。当函数有引用上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
}
obj.foo() // 2
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。
function foo() {
console.log(this.a)
}
var obj2 = {
a: 42,
foo: foo
}
var obj1 = {
a: 2,
obj2: obj2
}
obj1.obj2.foo() // 42
显式绑定
使用 call、bind、apply 来绑定 this 称为显式绑定。
apply 和 call 的区别只是传入的参数不同。
function foo() {
console.log(this.a, this.b)
}
let obj = {
a: 2,
b: 3
}
foo.call(obj) // 2, 3
foo.apply(obj) // 2, 3
bind 返回一个绑定了指定 this 的函数(硬绑定)
var obj = {
a: 2
}
var bar = foo.bind(obj)
var b = bar(3) // 2 3
console.log(b) // 5
new 绑定
new 自动执行以下操作:
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行 [[ 原型 ]] 连接。
- 这个新对象会绑定到函数调用的 this。
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a = a
}
var bar = new foo(2)
console.log(bar.a) // 2
优先级
显式绑定高于隐式绑定
function foo() {
console.log(this.a)
}
var obj1 = {
a: 2,
foo: foo
}
var obj2 = {
a: 3,
foo: foo
}
obj1.foo() // 2
obj2.foo() // 3
obj1.foo.call(obj2) // 3
obj2.foo.call(obj1) // 2
new 绑定高于隐式绑定
function foo(something) {
this.a = something
}
var obj1 = {
foo: foo
}
var obj2 = {}
obj1.foo(2)
console.log(obj1.a) // 2
obj1.foo.call(obj2, 3)
console.log(obj2.a) // 3
var bar = new obj1.foo(4)
console.log(obj1.a) // 2
console.log(bar.a) // 4
new 绑定高于显示绑定
function foo(something) {
this.a = something
}
var obj1 = {}
var bar = foo.bind(obj1)
bar(2)
console.log(obj1.a) // 2
var baz = new bar(3)
console.log(obj1.a) // 2
console.log(baz.a) // 3
判断规则
- 函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。
- 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。
题外
硬绑定这种方式可以把 this 强制绑定到指定的对象(除了使用 new 时),防止函数调用应用默认绑定规则。问题在于,硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显式绑定来修改 this。
如果可以给默认绑定指定一个全局对象和 undefined 以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力。
Function.prototype.softBind = function(obj) {
var fn = this
var curried = [].slice.call(arguments, 1)
var bound = function bound() {
// !this 考虑在 strict-mode 下运行
return fn.apply(
!this ||
(typeof window !== 'undefined' && this === window) ||
(typeof global !== 'undefined' && this === global)
? obj
: this,
curried.concat.apply(curried, arguments)
)
}
bound.prototype = Object.create(fn.prototype)
return bound
}
function foo() {
console.log('name: ' + this.name)
}
var obj = { name: 'obj' }
var fooOBJ = foo.softBind(obj)
fooOBJ() //name:obj
参考