前端基础 | 结合规范了解this
通过阅读大佬文章,结合es规范,重新学习下 this的问题
建议将上面文章结合规范一起阅读
接上一篇执行上下文
执行上下文分为两个阶段:
- 创建执行上下文
- 执行阶段
创建执行上下文又分为 3 步:
- 确定
this
指向 - LexicalEnvironment(词法环境) 组件被创建
- VariableEnvironment(变量环境) 组件被创建
这里只说说 this
ECMAScript
的类型分为语言类型
和 规范类型
。
我们常说的 Undefined
, Null
, Boolean
, String
, Number
和 Object
属于 语言类型
。
规范类型
是 「只存在于规范里的抽象类型」
。它们是为了更好地描述语言的底层行为逻辑
才存在的,但并不存在于实际的 js 代码中。
规范类型
包括:Reference
, List
, Completion
, Property Descriptor
, Property Identifier
, Lexical Environment
, 和 Environment Record
。
其中 Reference
类型 和 this
的指向有密切联系。
Reference
有且仅有三个组成部分:
base value
: 属性所在的对象或者就是EnvironmentRecord
,值只可能是undefined
,an Object
,a Boolean
,a String
,a Number
, oran environment record
其中的一种
。referenced name
: 属性的名称。strict reference
: 是否是严格模式
通过两个例子来记忆一下:
1 | var foo = 1; |
1 | var foo = { |
Reference
提供了两个方法:
- GetBase: 返回
reference
的base value
。**返回的是一个具体值,而不再是一个Reference
**。 - IsPropertyReference:
base value 是一个对象,就返回true,否则返回 false
这里再引入一个新名词:MemberExpression
查阅ES规范:http://es5.github.io/#x11.2
MemberExpression
的值为:
- PrimaryExpression // 原始表达式
- FunctionExpression // 函数定义表达式
- MemberExpression [ Expression ] // 属性访问表达式
- MemberExpression . IdentifierName // 属性访问表达式
- new MemberExpression Arguments // 对象创建表达式
例如:
1 | function foo() { |
简单理解 MemberExpression
其实就是()
左边的部分。
如何确定 this?
建议仔细理解前面的内容!
查阅 ES规范
:http://es5.github.io/#x11.2.3,简单总结成2步:
- 计算
MemberExpression
的结果赋值给ref
- 判断
ref
是不是一个Reference
类型- 2.1 如果
ref 是 Reference
,并且IsPropertyReference(ref) 是 true
, 那么this 的值为 GetBase(ref)
(由前面可知,GetBase(ref)即为base value
) - 2.2 如果
ref 是 Reference
,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)
。查阅 es规范:http://es5.github.io/#x10.2.1.2.6可知,ImplicitThisValue(ref) 函数始终返回 undefined
- 2.3 如果
ref 不是 Reference
,那么this 的值为 undefined
- 2.1 如果
如何判断是不是 Reference
类型
建议在「英文版规范」中全局搜索关键词
Return a value of type Referen
,然后阅读搜索结果所属部分的内容. 得出如下结论:
两种创建 Reference 的途径:
- 标识符解析
- 属性访问
比如,foo
和 foo.bar
创建了一个 Reference
,而字面量(1,“foo”,[1,3]
等)或函数表达式——(function(){}
)却不会。有一张来自网络的图作了个概括:
例子:
1 | var value = 1; |
根据上述的两个步骤进行判断:
foo.bar()
MemberExpression
的结果为foo.bar
- 确定
foo.bar
是不是Reference
类型(前面介绍了如何判断,不清楚的往前翻一翻
):- 2.1
foo.bar
属于属性访问,所以它是 Reference 类型
。
- 2.1
由前文得出:
1 | var Reference = { |
由前文,判断出 IsPropertyReference(ref) 值为 true
,所以 this 值为 GetBase(ref)
,即为 base value,所以 this 值为 foo
例子:
1 | function foo() { |
分析:
1 | var reference = { |
EnvironmentRecord
不属于 Object,所以 IsPropertyReference(ref)为 false
,则 this 指向 ImplicitThisValue(ref)
,而前面说了,
所以 this
指向 undefined
注意:
如果你在浏览器执行上述代码,你会在控制台发现输出的是
window
对象??
查阅规范http://es5.github.io/#x10.4.3
「Entering Function Code
」,第二条显示。
if thisArg is
null
orundefined
, set the ThisBinding to theglobal object
.