ES 基础 —— this
整理自 MDN。
前言
this
是在创建 Execution Context
的阶段中指定的,就根据不同的 Execution Context
的来说明吧。
总览
Execution Context | 调用方法 |
this 指向 |
---|---|---|
Global Execution Context | 全局对象 | |
Function Execution context | 简单调用 |
undefined |
作为对象方法调用 | 当前对象 | |
作为构造函数调用 | 被构造的新对象 | |
通过 apply() / call() / bind() 调用 |
指定的对象 | |
箭头函数 |
词法分析时的上级 this |
Global Execution context
在 Global Execution context 中,this
指向全局对象,无论是不是严格模式。
console.log(this === global) // true
以上代码,请在 Node.js REPL 中运行。Node.js 的模块环境中,
this
指定与模块对应的对象,所以以上代码并不成立。
Function Execution context
在 Function Execution context 中,this
的值取决于函数的调用方式。
直接调用
非严格模式
this
指向全局对象。
function f() {
return this
}
console.log(f() === global) // true
严格模式
this
保持进入 Execution Context 时所设置的值,所以如果 Execution Context 没设置 this
,那么 this
的 值就是 undefined
。
'use strict'
function f() {
return this
}
console.log(f() === undefined) // true
作为对象方法调用
当一个函数作为对象的方法调用时,它的 this
指向这个对象。这种行为不会受函数的定义位置/定义方法影响(当然,箭头函数不算在内)。
举个例子:
const o = {
prop: 37,
f: function () {
return this.prop
}
}
console.log(o.f()) // 37
上个例子中,函数在对象 o
中定义。还可以更早地定义函数,然后再绑定到对象上:
const o = {
prop: 37
}
function i() {
return this.prop
}
o.f = i
console.log(o.f()) // 37
以上两段代码对比,说明了这种行为不会受函数的定义位置/定义方法影响。
另外还有两种比较特殊的情况。虽然情况特殊,但是函数中 this
的行为不会改变,因为它们仍然是作为对象的方法调用。
调用的方法(函数)在对象的原型链上
假如调用的方法在一个对象的原型链上。通过这个对象调用这个方法时,this
仍然指向这个对象,就好像这个方法在这个对象上一样。
const o = {
f: function() {
return this.a + this.b
}
}
const p = Object.create(o);
p.a = 1
p.b = 4
console.log(p.f()) // 5
调用的方法是访问器属性的 Setter / Getter
function sum(){
return this.a + this.b + this.c
}
const o = {
a: 1,
b: 2,
c: 3,
get average(){
return (this.a + this.b + this.c) / 3
}
}
Object.defineProperty(o, 'sum', {
get: sum,
enumerable:true,
configurable:true
})
console.log(o.average, o.sum) // 2 6
作为构造函数调用
函数作为构造函数被调用(使用 new
调用)时,this
指向被构造的新对象。
默认情况下,构造函数返回
this
所指向的对象。但是可以通过return
指定其他对象作为返回值(如果return
指定的不是对象,那么还是会返回this
指向的对象)。
function C(){
this.a = 37
}
var o = new C()
console.log(o.a) // 37
function C2(){
this.a = 37
return { a: 38 }
}
o = new C2()
console.log(o.a) // 38
通过 apply()
/ call()
/ bind()
调用
apply()
/ call()
当一个函数在函数体内部使用了 this
,this
可以通过 apply()
/ call()
绑定到特定的对象上。
function add(c, d) {
return this.a + this.b + c + d
}
const o = {
a: 1,
b: 2
}
add.call(o, 5, 7) // 1 + 2 + 5 + 7 = 15
add.apply(o, [20, 20]) // 1 + 2 + 20 + 20 = 43
值得提起的一点:在使用 apply()
/ call()
时,如果作为 this
传入的值不是对象,会尝试使用内部操作 ToObject
将其转换成对象。所以如果传入的是原始值,比如 7
或者 'foo'
,它们会被通过相关的构造函数转换成对象:
-
7
->new Number(7)
-
'foo'
->new String('foo')
bind()
f.bind()
创建一个和 f
拥有相同函数体和作用域的函数,但是其 this
永远绑定到 bind()
的第一个参数上,无论函数如何调用。
function f() {
return this.a
}
var g = f.bind({
a: bill'
})
console.log(g()) // bill'
var o = {
a: 37,
f,
g
}
console.log(o.f(), o.g()) // 37, 'bill'
箭头函数
箭头函数的 this
是静态设置(set lexically)的。什么叫静态设置?箭头函数没有自己的 this
,当在箭头函数内部使用 this
时,会沿静态作用域链查找,使用最近一层作用域内的 this。并且,无论箭头函数被如何调用,this
的值都不会改变。 比如:
-
箭头函数嵌套在另一个函数中,箭头函数中的
this
指像这个外层的函数的this
所指向的对象 -
箭头函数在全局代码中,
this
指向全局对象
箭头函数嵌套在另一个函数中:
const obj = {
name: 'bill',
sayName: function () {
const helper = () => {
console.log(this.name) // bill
console.log(this === obj) // true
}
helper()
}
}
obj.sayName()
箭头函数在全局代码中:
const globalObject = this;
const foo = () => {
return this
}
console.log(foo() === this) // true
// 无论如何调用 foo(),其中的 this 总是指向全局对象
// 1. 作为对象的方法调用
const obj = {
foo
}
console.log(obj.foo() === globalObject) // true
// 2. 通过 call() 设置 this 调用
console.log(foo.call(obj) === globalObject) // true
// 3. 使用 bind() 绑定 this 调用
foo = foo.bind(obj)
console.log(foo() === globalObject) // true