this
的绑定规则总共有下面5种。
- 1、默认绑定(严格/非严格模式)
- 2、隐式绑定
- 3、显式绑定
- 4、new绑定
- 5、箭头函数绑定
# 一、默认绑定
- 非严格模式下,函数独立调用,默认绑定到全局对象,即this指向
window
。
var num = 10;
function demo() {
console.log(this.num);
}
demo(); //此时this指向window,windwo.num = 10
- 严格模式下,this会绑定到
undefined
。只有函数运行在非严格模式下,默认绑定才能绑定到全局对象
var num = 10;
function demo() { // 运行在严格模式下,this会绑定到undefined
"use strict";
console.log(this.num);
}
demo(); //控制台报错 Uncaught TypeError: Cannot read property 'num' of undefined
- 但是在严格模式下调用函数则不影响默认绑定。
var num = 10;
function demo() {
console.log(this.num);
}
(function () { // 严格模式下调用函数则不影响默认绑定
"use strict";
demo(); //此时this仍指向全局变量window,windwo.num = 10
})()
# 二、隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
特殊情况:隐式丢失
被隐式绑定的函数特定情况下会丢失绑定对象,应用默认绑定,把this绑定到全局对象或者undefined上。
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo;
// 虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身。
//故调用bar()是一个不带任何修饰的函数调用,对应默认绑定,this指向window
var a = "这是window下的a"; // a是全局对象的属性
bar(); //输出:这是window下的a
参数传递就是一种隐式赋值,传入函数时也会被隐式赋值。回调函数丢失this绑定是非常常见的。
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn其实引用的是foo
fn();
}
var obj = {
a: 2,
foo: foo
};
var a = "这是window下的a"; // a是全局对象的属性
doFoo( obj.foo ); //输出:这是window下的a
# 三、显式绑定
通过call(..)
或者 apply(..)
方法。第一个参数是一个对象,在调用函数时将这个对象绑定到this。因为直接指定this的绑定对象,称之为显示绑定。
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2 调用foo时强制把foo的this绑定到obj上
注意:显示绑定无法解决丢失绑定问题
解决方案:
1.硬绑定
创建函数bar(),并在它的内部手动调用foo.call(obj),强制把foo的this绑定到了obj。
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定的bar不可能再修改它的this
bar.call( window ); // 2
# 四、new绑定
- 在JS中,
构造函数
只是使用new
操作符时被调用的普通
函数,他们不属于某个类,也不会实例化一个类。 - 包括内置对象函数(比如
Number(..)
)在内的所有函数都可以用new
来调用,这种函数调用被称为构造函数调用。 - 实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。
使用new
来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
- 1、创建(或者说构造)一个新对象。
- 2、这个新对象会被执行
[[Prototype]]
连接。 - 3、这个新对象会绑定到函数调用的
this
。 - 4、如果函数没有返回其他对象,那么
new
表达式中的函数调用会自动返回这个新对象。
使用new
来调用foo(..)
时,会构造一个新对象并把它(bar
)绑定到foo(..)
调用中的this。
function foo(a) {
this.a = a;
}
var bar = new foo(2); // bar和foo(..)调用中的this进行绑定
console.log( bar.a ); // 2
# 五、箭头函数绑定
ES6新增一种特殊函数类型:箭头函数,箭头函数无法使用上述四条规则,而是根据外层(函数或者全局)作用域(词法作用域)来决定this。
foo()
内部创建的箭头函数会捕获调用时foo()
的this。由于foo()
的this绑定到obj1
,bar
(引用箭头函数)的this也会绑定到obj1
,箭头函数的绑定无法被修改(new
也不行)。
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!
ES6之前和箭头函数类似的模式,采用的是词法作用域取代了传统的this机制。
function foo() {
var self = this; // lexical capture of this
setTimeout( function() {
console.log( self.a ); // self只是继承了foo()函数的this绑定
}, 100 );
}
var obj = {
a: 2
};
foo.call(obj); // 2