一、准备
ECMAScript 的类型分为语言类型和规范类型。
语言类型:开发者直接使用 ECMAScript 可以操作,就是常说的Undefined, Null, Boolean, String, Number, 和 Object。
规范类型:相当于 meta-values,用来用算法描述 ECMAScript 语言结构和 ECMAScript 语言类型的,包括:Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, 和 Environment Record。
今日重点: Reference 类型,它与 this 的指向有着密切的关联。
二、Reference介绍
Reference 是一个 Specification Type,也就是 “只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的 js 代码中。
Reference 由三个部分组成,分别是:
简单理解:
base value :属性所在的对象或者就是 EnvironmentRecord,它的值只可能是 undefined, Object, Boolean, String, Number, EnvironmentRecord 其中的一种。
referenced name :属性的名称。
strict reference:是否严格模式
举个例子:
var foo = 1;
// 对应的Reference是:
var fooReference = {
base: EnvironmentRecord,
name: 'foo',
strict: false
};
var foo = {
bar: function () {
return this;
}
};
foo.bar(); // foo
// bar对应的Reference是:
var BarReference = {
base: foo,
propertyName: 'bar',
strict: false
};
规范中还提供了获取 Reference 组成部分的方法,比如 GetBase 和 IsPropertyReference。
1.GetBase(ref):返回 reference 的 base value。
2.IsPropertyReference(ref):简单的理解----如果 reference 的base value 是一个对象,就返回true。
3.GetValue(ref):返回对象属性真正的值。注意--调用 GetValue,返回的将是具体的值,而不再是一个 Reference。
模拟GetValue()使用:
var foo = 1;
var fooReference = {
base: EnvironmentRecord,
name: 'foo',
strict: false
};
GetValue(fooReference) // 1;
三、重点----如何确定this的值
步骤:
1.计算 MemberExpression 的结果赋值给 ref
MemberExpression :
举个栗子:
function foo() {
console.log(this)
}
foo(); // MemberExpression 是 foo
function foo() {
return function() {
console.log(this)
}
}
foo()(); // MemberExpression 是 foo()
var foo = {
bar: function () {
return this;
}
}
foo.bar(); // MemberExpression 是 foo.bar
简单理解:MemberExpression 其实就是()左边的部分。
2.判断 ref 是不是一个 Reference 类型
2.1 [ref 是 Reference] && [IsPropertyReference(ref) === true] => [this 的值为 GetBase(ref)]
2.2 [ref 是 Reference] && [ref.baseValue === EnvironmentRecord] => [this 的值为 ImplicitThisValue(ref) => undefined]
(ImplicitThisValue函数始终返回 undefined,所以最后 this 的值就是 undefined)
2.3 [ref 不是 Reference] => [this 的值为 undefined]
举个栗子:
// 非严格模式
var value = 1;
var foo = {
value: 2,
bar: function () {
return this.value;
}
}
console.log(foo.bar()); // foo.bar是Reference类型,this指向base value => foo,结果 => 2
console.log((foo.bar)()); // 同上判断,结果 => 2
console.log((foo.bar = foo.bar)()); // 有赋值操作符,返回值为不是Reference类型,this指向 undefined => window, 结果 => 1
console.log((false || foo.bar)()); // 逻辑操作符,返回值为不是Reference类型,结果 => 1
console.log((foo.bar, foo.bar)()); // 逗号操作符,返回值为不是Reference类型,结果 => 1
追根溯源的从 ECMASciript 规范讲解 this 的指向,尽管从这个角度写起来和读起来都比较吃力,但是一旦多读几遍,明白原理,绝对会给你一个全新的视角看待 this 。
本文参考掘金前端大神冴羽的文章:原文链接