1. 概念准备
ECMAScript 的类型分为语言类型和规范类型。
语言类型: Undefined, Null, Boolean, String, Number, 和 Object。
规范类型: Reference, List, Completion, Property Descriptor, Property Identifier, Lexical Environment, 和 Environment Record。
解释一下, 规范类型,也就是 “只存在于规范里的抽象类型”。
它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的 js 代码中。
其中, 这里涉及到了 Reference, 字面理解为 某个属性的参照
Reference 的构成,由三个组成部分,分别是:
- base value (属性所在的对象, 否则就是 EnvironmentRecord)
EnvironmentRecord我的理解是, 假如这个变量/属性不属于某一对象, 那么他属于某个环境
- referenced name (属性的名称)
- strict reference (是否严格模式)
举个例子:
var foo = 1;
// 对应的Reference是:
foo -> Reference = {
base: EnvironmentRecord, //不属于某一对象(当然, 非严格模式下会被挂到全局对象window)
name: 'foo', // 属性名 'foo'
strict: false
};
再举个例子:
var foo = {
bar: function () {
return this;
}
};
// bar对应的Reference是:
bar -> Reference = {
base: foo, // 明显 bar 是 foo 的属性
name: 'bar', // 属性名 'bar'
strict: false
};
看完上面的例子, 你可能会问, 怎么拿到某个属性/变量的 Reference 中属性的值呢?
ECMA规定了以下方法 (当然 , js层面没法调用):
- Reference base 相关:
- GetBase -> 获取Reference base值
- IsPropertyReference -> Reference base值是对象则返回
true
, 从函数名来看, 用于判断是否Reference
- 属性值
- GetValue -> 获取该属性的值(GetValue返回不再是一个 Reference)
2. 再来确定this的值
现在来看看确定this的整个过程
-
计算 MemberExpression 的结果赋值给一个变量 ref (简单理解 MemberExpression 其实就是()左边的部分。)
-
判断 ref 是不是一个 Reference 类型
结果:
-
若 ref 是 Reference,且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
-
若ref是 Reference,且 base 值是 EnvironmentRecord, 那this的值为 ImplicitThisValue(ref)undefined
-
如果 ref 不是 Reference,那么 this 的值为 undefined
上面的话很晕
通俗的来说, 过程如下:
var foo = {
bar: function () {
return this;
}
};
foo.bar(); // foo
- 拿到 () 左边的部分 foo.bar, 赋值给 ref
- 判断 ref 是否 Reference , IsPropertyReference(ref), 明显 ref -> Reference > base === foo
- foo是对象, 则IsPropertyReference返回true, ref是Reference类型
- 那么, this的指向就是GetBase(ref), 也就是 foo