Usually an Environment Record is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement. Each time such code is evaluated, a new Environment Record is created to record the identifier bindings that are created by that code.
编译代码时,当遇到函数声明或者块级声明或者 catch 从句时等,就会新建一个 Environment Record (envRec),用来记录这一块代码里面涉及到的标识符在代码执行时应如何取值。
Every Environment Record has an OuterEnv field, which is either null or a reference to an outer Environment Record. This is used to model the logical nesting of Environment Record values. The outer reference of an (inner) Environment Record is a reference to the Environment Record that logically surrounds the inner Environment Record.
The Reference Record type is used to explain the behaviour of such operators as delete, typeof, the assignment operators, the super keyword and other language features. For example, the left-hand operand of an assignment is expected to produce a Reference Record.
呃 ... 只能说当你想知道某条语句背后到底都做了些啥的时候,去看看规范会了解得更清晰点,虽然在不太熟的情况下需要反复看来回看才会理解。
参考资料
Environment Record
先简单了解一下 9.1 Environment Records 。
编译代码时,当遇到函数声明或者块级声明或者 catch 从句时等,就会新建一个 Environment Record (envRec),用来记录这一块代码里面涉及到的标识符在代码执行时应如何取值。
OuterEnv 是指代码结构上的外层 Environment Record 。
The this Keyword
13.2.1 The this Keyword
9.3.4 ResolveThisBinding ( )
9.3.3 GetThisEnvironment ( )
让 env 是当前执行上下文的词法环境,如果 env.HasThisBinding() 返回 true ,就返回 env 。否则让 env 值变为
env.[[OuterEnv]]
,重复这个判断过程。HasThisBinding 和 GetThisBinding
不同类型的 Environment Record 的 HasThisBinding 的值不一样。
envRec.[[GlobalThisValue]]
,也就是 JS 中的变量globalThis
envRec.[[ThisBindingStatus]]
是 lexical 就返回 false ,否则返回 truefunction Environment Record 的 ThisBindingStatus 是 lexical 说明是这个函数是箭头函数。所以对于箭头函数来说,HasThisBinding() 会返回 false ,然后再看
[[OuterEnv]]
的 HasThisBinding() 返回值,而对于其它函数则是会有自己的 this 值。function Environment Record 的 GetThisBinding ( ) 是返回
envRec.[[ThisValue]]
。所以问题变成了这个 ThisValue 的值是咋赋的值。
在 这个表格 里我们可以看到对于 function Environment Record ,有个 BindThisValue(V) 来设置 ThisValue 。
这里的第 2 点说明了 this 值只能被设置一次,然后就不能改变了。
BindThisValue(V)
以下场景会调用 BindThisValue(V) :
注意看第 5 点和第 6 点,如果是严格模式,thisValue 就是 thisArgument 。如果不是严格模式,并且 thisArgument 是 undefined 或者 null ,那么 thisValue 就是
globalThis
。OrdinaryCallBindThis
会在以下场景被调用:7.3.13 Call 会执行
F.[[Call]](V, argumentsList)
所以现在就是关心调用 Call 时传进来的 V 就好了。
Reference Record
在继续往下分析前先了解一下 6.2.4 The Reference Record Specification Type ,请结合本文开头的参考资料一起食用。
6.2.4.1 IsPropertyReference ( V )
6.2.4.4 GetValue ( V )
要注意的是,GetValue(V) 的返回值就不是 Reference Record 了。
6.2.4.6 GetThisValue ( V )
Function Calls
13.3.6 Function Calls
传给 EvaluateCall 函数的 ref 是
the result of evaluating CallExpression
。13.3.6.2 EvaluateCall ( func, ref, arguments, tailPosition )
如果 ref 不是 Reference Record ,那么 thisValue 就是 undefined ,然后在非严格模式下也就是
globalThis
。如果 ref 是 Reference Record ,并且 IsPropertyReference(ref) 返回 true ,thisValue 就是 GetThisValue(ref) ,否则是返回
ref.[[Base]].WithBaseObject()
WithBaseObject() 这个函数除了 with 语句的场景其它都是返回 undefined ,所以对应的 this 在非严格模式下也就是
globalThis
。对于
(obj.func = obj.func)()
以及(1, obj.func)()
,由于()
前面的那一部分并不是 Reference Record ,所以 this 最终在非严格模式下是globalThis
。13.15.2 Runtime Semantics: Evaluation
对于赋值操作有执行 GetValue ,所以返回不是 Reference Record 。
13.16.1 Runtime Semantics: Evaluation
对于逗号操作也是有执行 GetValue ,所以返回不是 Reference Record 。
而如果只是
(obj.func)()
这样,this 值就会是 obj ,因为 The Grouping Operator 不会执行 GetValue 。13.2.9.2 Runtime Semantics: Evaluation
其它的一些场景在规范中也可找到。