译者:江吉仪
this 是 JavaScript 最棘手的概念之一,简而言之,this 指向执行上下文。
全局上下文
当不在任何函数或者类(实际上只是函数)内是,this指向是全局执行上下文,当在浏览器时是 window。
this; // window
this.foo = "bar";
window.foo; // "bar"
在 node.js 中.this 在 全局上下文是 exports 对象,默认是指一个空对象。
this; //{}
函数上下文
当 this 在函数中,this 的值是指向函数调用的地方。例如:
const bar = function () { console.log(this) };
const foo = {};
foo.bar = bar;
bar(); // window
foo.bar(); // bar
函数
当一个函数在全局环境被调用,this 指向全局的上下文。
const bar = function() { console.log(this)};
bar(); //window
函数不会影响其主体函数的执行上下文。
const foo = function () { bar();}
foo(); //window
######严格模式
在函数内部,this 的值也取决于严格模式是否开启。非严格模式,this 是全局对象(window),严格模式情况下,它是一个 undefined。
const foo = function () { console.log(this) };
const bar = function () { 'use strict'; console.log(this); };
foo(); // window
bar(); // undefined
构造函数
当函数跟 new 关键字一起使用时,可当做是一个构造函数。构造函数像一个模板一样创建新的对象,并作为一个语法糖加入到类在 ECMAScript 2015( ES6)。
当一个函数被用作构造函数,函数内部的 this 指向正在构建的对象。
function Car(make) {
this.make = make;
}
const myCar = new Car("BMW");
myCar.make; // "BMW"
自从在 es6 中构造函数被语法糖成类。es6 的类构造函数也是如此。
class Car {
constructor(make) {
this.make = make;
}
}
const myCar = new Car("BMW");
myCar.make; // "BMW"
prototype
构造函数也可以在它的prototype对象里面定义一个方法。this 指向调用此方法的对象实例。
Car.prototype.getMake = function () {
return this.make;
}
myCar.getMake(); // "BMW"
同样,prototype对象等同于类的原型方法,因此适用相同的规则。
class Car {
constructor(make) {
this.make = make;
}
getMake() {
return this.make;
}
}
const myCar = new Car("BMW");
myCar.getMake(); // "BMW"
对象方法/静态方法
如果一个函数是对象的方法的话。那么 this 指向这个对象。
const foo = {};
foo.bar = function () { console.log(this); };
foo.bar(); // foo
因为每一个函数都是一个对象,this 相当于类中的静态方法。
class Car {
constructor(make) {
this.make = make;
}
static convertKmToMiles(km) {
return km * this.kmToMilesConversionRate;
}
}
Car.kmToMilesConversionRate = 0.621371;
Car.convertKmToMiles(42); // 26.097582
另一个例子是,当你运行 window.setTimeout时,在 setTimeout 中,this指的是window 对象.
const foo = {};
foo.bar = function () {
window.setTimeout(function () {
console.log(this);
}, 0);
};
foo.bar(); // window
绑定 this
bind
如果你想让 函数内的 this 确切地指向一个确定的对象,你可以使用函数的 bind 方法.
const foo = function () { console.log(this) };
const boundFoo = foo.bind("always me!");
foo(); // window
boundFoo(); // "always me!"
const a = {
b: foo,
boundB: boundFoo
}
a.b(); // a
a.boundB(); // "always me"
bar = function () {};
bar.prototype.x = foo;
bar.prototype.boundX = boundFoo;
const myBar = new bar();
myBar.x(); // myBar
myBar.boundX(); // "always me"
.call 和. apply
Function.prototype.bind 返回一个新的函数绑定到一个对象上,但不会调用这个函数。Function.prototype.call 和Function.prototype.apply 是立即调用这个函数,并把 this 的值传递过去。
.call 和 .apply 的不同之处在于,.call 希望直接传递一个列表进去。.apply 希望传递一个数组。
const foo = function (x, y) {
console.log(this);
console.log(x);
console.log(y);
};
foo(); // window, undefined, undefined
foo.call(); // window, undefined, undefined
foo.call("Yo!"); // "Yo!", undefined, undefined
foo.call("Yo!", 1, 2); // "Yo!", 1, 2
foo.apply("Yo!", [1, 2]); // "Yo!", 1, 2
EE6 / 7
在 es6 中,有两种句法会影响 this 的绑定
- Arrow functions
Reflect.apply()
在 es7 中,有 :: 绑定操作符
箭头函数
箭头函数会自动绑定 this 到函数内部
function Car (make) {
window.setTimeout(function () {
console.log(this); // window
}, 0);
window.setTimeout(() => {
console.log(this); // Car
}, 2000)
}
new Car();
// window
// Car
Reflect.apply()
Reflect.apply() 是Function.prototype.apply的另一种写法。
const foo = function (x, y) {
console.log(this);
console.log(x);
console.log(y);
};
foo.apply('a', ['b', 'c']); // 'a', 'b', 'c'
Reflect.apply(foo, 'a', ['b', 'c']) // 'a', 'b', 'c'
它们等同于一些告诫,其中最相关的是你可以覆盖对象的apply方法,而Reflect.apply将始终等于Function.prototype.apply。
:: 操作符合
:: bind运算符是一个提议的运算符,它将对象的方法绑定到对象本身。
console.log(this); // window
console.log(this).bind(console); // console
::console.log(this); // console