看下面这个例子
function foo(num) {
console.log( "foo: " + num );
// 追踪 `foo` 被调用了多少次
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
foo( i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// `foo` 被调用了多少次?
console.log( foo.count ); // 0
第一种解决方案:创建另一种对象来持有count属性。但这种方法直接忽略了this,采用了词法作用域。
第二种解决方案: 强迫this指向foo
this 实际上是在函数被调用时建立的一个绑定,它指向 什么 是完全由函数被调用的调用点来决定的。
一。绑定规则优先级
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
明确绑定>隐含绑定
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绑定
二。判定this
1.函数是通过 new 被调用的吗(new 绑定)?如果是,this 就是新构建的对象。
var bar = new foo()
2.函数是通过 call 或 apply 被调用(明确绑定),甚至是隐藏在 bind 硬绑定 之中吗?如果是,this 就是那个被明确指定的对象。
var bar = foo.call( obj2 )
3.函数是通过环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this 就是那个环境对象。
var bar = obj1.foo()
否则,使用默认的 this(默认绑定)。如果在 strict mode 下,就是 undefined,否则是 global 对象。
var bar = foo()
注意:对于数组遍历方法(forEach,map,find,,,)如果使用的是普通匿名函数的方式,则其中的this始终是windows。若要改变指向,使用bind()。
三。箭头函数中的this
function foo() {
// 返回一个箭头函数
return (a) => {
// 这里的 `this` 是词法上从 `foo()` 采用的
console.log( this.a );
};
}
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是3!
箭头函数中的this不会被覆盖,由外部环境觉得this的指向。