xxleyi / learning_list

聚集自己的学习笔记
10 stars 3 forks source link

从 ECMA 规范的视角看 JS 中的 this 问题 #241

Open xxleyi opened 3 years ago

xxleyi commented 3 years ago

ECMA 语言规范必须显式指明 this 从何而来,往哪去,如何被使用。

简单看一下,有助于形成更为具体的抽象。

"use strict"

let obj;

obj = {
  go: function() { console.log(this); }
};

obj.go();
(obj.go)();
(obj.go || obj.stop)();
(obj.stop || obj.go)();

问:四处输出分别为啥?为什么?

答:前两个输出是 obj,后两个输出是 undefined

ECMA 规范中除了使用 JS 语言中诸多类型的 values 之外,还定义了一些其它类型的 values,ECMA 规范自己使用。这样可以让表达更清晰凝练。其中和 this 息息相关的类型是 Reference。再次提醒,这仅仅是 ECMA 中使用的概念,不存在于 JS 当中,但却有助于我们站在更高的视角来把握 JS。

换句话说,站在语言实现者的角度,this 背后隐藏着一个有具体类型的值:Reference

有大神给了具体的解释:

Reference values are only constructed by very few operations:

引自 https://stackoverflow.com/questions/29353177/what-is-base-value-of-reference-in-ecmascriptecma-262-5-1

此时再来看 reference type 的具体定义:

A Reference is a resolved name or property binding. A Reference consists of three components, the base value component, the referenced name component, and the Boolean-valued strict reference flag. The base value component is either undefined, an Object, a Boolean, a String, a Symbol, a Number, a BigInt, or an Environment Record. A base value component of undefined indicates that the Reference could not be resolved to a binding. The referenced name component is a String or Symbol value.

要点:

这个时候再来那个 SO 的问题和完整回答:

Q: I've been trying to understand how this value is set in javascript, and found ECMAScript Language Specification pretty much helpful. I was reading section 8.7 reference specification type and found that reference in ECMAScript is made of 3 component, base valuereferenced namestrict reference flagto understand section 11.2.3.

I can assume what are referenced name and strict reference flag from their name, but i don't understand what is the base value. The document says that base value is either undefinedStringBooleanNumber and Object, but it does not say how it is set and what it is. I am guessing it is something similar to context object. Could anyone explain?

A: Yes, the base value is the context in which the referenced name lives.

For an object property, this would be the object (see §8.12 Object internal methods for setter/getter operations). For a variable, this would be the variable environment (§10.2.1 Environment records). For an unresolvable reference (the things that throw reference errors except when supplied to typeof), this would be undefined.

it does not say how it is set

Reference values are only constructed by very few operations:

是不是豁然开朗?

JS 对 this 的核心处理是:对某个值进行函数调用时,先判断它是否是 Reference 类型的值:

这条处理发挥着 this 的核心功能,其余处理,像是否是严格模式,是否是普通函数之类的,都是边缘情况的处理。

为什么对于 this,如果只是站在 js 的层面,几乎只能硬性记住各种情况呢?因为语言的设计和实现会隐藏一些关键点

进阶之路无非是,转换自己作为纯粹使用者的身份,尝试站到设计和实现的角度去。