this的四种绑定策略
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
1. 默认绑定
当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象
如果是在严格模式下use strict
,那么this会指向undefined
// 第一个例子
var foo = function() {
console.log(this.a);
}
var a = 2
foo() // 2
// 第二个迷惑性的例子
var foo = function() {
var a = 3
var inner = function() {
console.log(this.a);
}
inner()
}
var a = 2
foo() // 2
第二个例子虽然在foo()作用域内声明了a变量为3, 但它并不是一个对象, 所以this最终还是会指向全局的window对象
2. 隐式绑定
函数被调用时有上下文对象,那么this会绑定这个上下文对象
或者也可以这样说当函数被一个对象包含的时候, 我们称这个函数的this被隐式绑定到这个对象上了
var o = {
a: 2,
foo: function() {
console.log(this.a);
},
}
在一串对象属性引用链中, this绑定的是最内层的对象
var obj = {
a: 1,
obj2: {
a: 2,
obj3: {
a:3,
getA: function () {
console.log(this.a)
}
}
}
}
obj.obj2.obj3.getA(); // 输出3
隐式丢失
最常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象, 也就是它会应用默认绑定, 把this绑定到全局对象或者undefined上
var foo = function() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
var a = 5
var bar = obj.foo
bar() // 5
像这个例子, obj对象把foo函数的引用传给bar的时候, 会丢失this对obj的绑定
回调函数同样也会丢失绑定
var foo = function() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
var a = 5
setTimeout(obj.foo, 100) // 5
这种函数赋值的方式是无法将函数所绑定的this对象也传递过去的
那如果我就是想传递函数并且把所绑定的this对象也传递过去呢?
那么你可以使用显式绑定
3. 显式绑定
显式绑定是通过apply
或者call
函数或者bind
绑定的对象
如果你想传递函数并且把所绑定的this对象也传递过去, 那么就可以使用call()
fn.call(object)
- fn是你调用的函数
- object是你希望绑定的对象
- 作用:即刻调用函数fn(), 调用时这个函数的this指向object
var foo = function() {
console.log(this.a)
}
var obj = {
a: 1,
}
foo.call(obj)
这样做有个缺点, 每次调用都会依赖call
所以可以将它包装成函数
var foo = function() {
console.log(this.a)
}
var obj = {
a: 1,
}
var bar = function() {
foo.call(obj)
}
bar()
如果使用bind会更简单
var foo = function() {
console.log(this.a)
}
var obj = {
a: 1,
}
var bar = foo.bind(obj)
bar()
call和bind的区别是:在绑定this到对象参数的同时:
- call将立即执行该函数
- bind不执行函数,只返回一个可供执行的函数
4. new绑定
来new来调用函数,会自动执行下列操作
- 创建一个全新的对象
- 这个新对象被执行[[prototype]]连接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
function foo(a) {
this.a = a;
}
var bar = new foo(2)
console.log(bar.a) // 2
5. 优先级
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定