Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【JavaScript】用 ES 规范去解释 this #54

Open
Tracked by #6
swiftwind0405 opened this issue May 5, 2020 · 5 comments
Open
Tracked by #6

【JavaScript】用 ES 规范去解释 this #54

swiftwind0405 opened this issue May 5, 2020 · 5 comments

Comments

@swiftwind0405
Copy link
Owner

swiftwind0405 commented May 5, 2020

this关键字与面向对象程序开发紧密相关,其完全指向由构造器新创建的对象。在ECMAScript规范中也是这样实现的,但正如我们将看到那样,在ECMAScript中,this并不限于只用来指向新创建的对象。

理论

当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。

对于每个执行上下文,都有三个重要属性:

  • 变量对象(Variable object,VO)
  • 作用域链(Scope chain)
  • this

规范中之处ECMAScript有三种可执行代码:

  • 全局代码(Global code)
  • eval代码(Eval code)
  • 函数代码(Function code)

其中,对于全局代码直接指向global object,eval代码由于已经不推荐使用暂不做讨论,我们主要关注函数代码中的 this 如何指定。

进入函数代码(ES5-10.4.3

The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList:

  1. If the function code is strict code, set the ThisBinding to thisArg.
  2. Else if thisArg is null or undefined, set the ThisBinding to the global object.
  3. Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
  4. Else set the ThisBinding to thisArg.

规范指出,当执行流进入函数代码时,由函数调用者提供 thisArg 和 argumentsList。所以我们需要继续寻找,查看函数调用时候this是如何传递进去的。
(这里只需知道第2点:函数调停者提供的 thisArg 为 null 或 undefined 的时候, this 绑定的是全局对象即可)

函数调用(ES5-11.2.3

The production CallExpression : MemberExpression Arguments is evaluated as follows:

1.Let ref be the result of evaluating MemberExpression.

6.If Type(ref) is Reference, then
  a. If IsPropertyReference(ref) is true, then
   i. Let thisValue be GetBase(ref).
  b. Else, the base of ref is an Environment Record
   i. Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
7.Else, Type(ref) is not Reference.
  a. Let thisValue be undefined.

从上述规范中,在函数调用发生时,首先会对函数名部分进行计算并赋值给 ref ,6、7步中几个if else就决定了一个函数调用发生时,this会指向何方。

image

Reference type(ES5-8.7

Reference type按字面翻译就是引用类型,但是它并不是我们常说的JavaScript中的引用类型,它是一个规范类型(实际并不存在),也就是说是为了解释规范某些行为而存在的,比如delete、typeof、赋值语句等。规范类型设计用于解析命名绑定的,它由三部分组成:

  • base value,指向引用的原值(属性所在的对象或者就是 EnvironmentRecord,它的值只可能是 undefined, an Object, a Boolean, a String, a Number, or an environment record 其中的一种)
  • referenced name,引用的名称
  • strict reference flag,标示是否严格模式

用大白话讲,就是规范中定义了一种类型叫做Reference用来引用其他变量,它有一个规定的数据结构。由于是规范类型,所以什么情况下会返回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
};

实战解析

  • foo();
  • foo.bar();
  • (f = foo.bar)();

foo();

1

foo.bar();

2

(f = foo.bar)();

3

速查表

最关键的就是判断返回值是不是 reference ,如果不是,直接可以推出等于window,如果是,只需要看是不是属性 reference。

Example Reference? Notes
“foo” No
123 No
/x/ No
({}) No
(function(){}) No
foo Yes Could be unresolved reference if foo is not defined
foo.bar Yes Property reference
(123).toString Yes Property reference
(function(){}).toString Yes Property reference
(1,foo.bar) No Already evaluated, BUT see grouping operator exception
(f = foo.bar) No Already evaluated, BUT see grouping operator exception
(foo) Yes Grouping operator does not evaluate reference
(foo.bar) Yes Ditto with property reference

参考文档

@swiftwind0405
Copy link
Owner Author

这个主题感觉前前后后花了很久,每次因为有时间没继续的时候,下次再切回来的时候,又要花好多时间精力才能接着进行。
单纯看ES规范文档真的很枯燥,也很容易放弃。我这里的经验是顺腾摸瓜,从对应的某条规范开始看起。必须看英文文档,实在看不懂的地方再去看相应的中文文档。

@alexzhao8326
Copy link

如果调用GetValue(V)一定不是Reference吗

@swiftwind0405
Copy link
Owner Author

如果调用GetValue(V)一定不是Reference吗

是的,GetValue 返回对象属性真正的值,而不再是一个Reference。

8.7.1 GetValue (V)

@MBearo
Copy link

MBearo commented Nov 5, 2020

大佬,这里写的都是 ES5 的规范,那 ES6 里有没有变化呢?

@swiftwind0405
Copy link
Owner Author

大佬,这里写的都是 ES5 的规范,那 ES6 里有没有变化呢?

老铁,别这样,我也不咋懂。。。据我现在的了解,应该是没有变化吧?如果有见解,可以讨论讨论。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants