JavaScript 深入之从 ECMAScript 规范解读 this

2017/05/17 · JavaScript
· this

原文出处: 冴羽   

之前都是根据函数的使用情况来理解this,分为四种情况:

原文出处

JavaScript深入之从ECMAScript规范解读this
ECMA-262-3 in detail. Chapter 3.
This
ECMAScript5.1中文版
ECMAScript5.1英文版

前言

在《JavaScript深入之执行上下文栈》中讲到,当JavaScript代码执行一段可执行代码(executable
code)时,会创建对应的执行上下文(execution context)。

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

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

今天重点讲讲this,然而不好讲。

……

因为我们要从ECMASciript5规范开始讲起。

先奉上ECMAScript 5.1规范地址:

英文版:

中文版:

让我们开始了解规范吧!

  • 1、作为对象方法调用
  • 2、作为普通函数调用
  • 3、构造器调用
  • 4、Function.prototype.call或Function.prototype.apply调用
    但是今天看到了一篇文章,是追根溯源的从 ECMASciript 规范讲解 this
    的指向,读完感觉收获很大,赶紧记录下来。

Types


首先是第 8 章 Types:

Types are further subclassified into ECMAScript language types and
specification types.

An ECMAScript language type corresponds to values that are directly
manipulated by an ECMAScript programmer using the ECMAScript language.
The ECMAScript language types are Undefined, Null, Boolean, String,
Number, and Object.

A specification type corresponds to meta-values that are used within
algorithms to describe the semantics of ECMAScript language constructs
and ECMAScript language types. The specification types are Reference,
List, Completion, Property Descriptor, Property Identifier, Lexical
Environment, and Environment Record.

我们简单的翻译一下:

ECMAScript 的类型分为语言类型和规范类型。

ECMAScript 语言类型是开发者直接使用 ECMAScript
可以操作的。其实就是我们常说的Undefined, Null, Boolean, String, Number,
和 Object。

而规范类型相当于 meta-values,是用来用算法描述 ECMAScript 语言结构和
ECMAScript 语言类型的。规范类型包括:Reference, List, Completion,
Property Descriptor, Property Identifier, Lexical Environment, 和
Environment Record。

没懂?没关系,我们只要知道在 ECMAScript
规范中还有一种只存在于规范中的类型,它们的作用是用来描述语言底层行为逻辑

Types

首先是第8章Types:

Types are further subclassified into ECMAScript language types and
specification types.

An ECMAScript language type corresponds to values that are directly
manipulated by an ECMAScript programmer using the ECMAScript language.
The ECMAScript language types are Undefined, Null, Boolean, String,
Number, and Object.

A specification type corresponds to meta-values that are used within
algorithms to describe the semantics of ECMAScript language constructs
and ECMAScript language types. The specification types are Reference,
List, Completion, Property Descriptor, Property Identifier, Lexical
Environment, and Environment Record.

我们简单的翻译一下:

ECMAScript的类型分为语言类型和规范类型。

ECMAScript语言类型是开发者直接使用ECMAScript可以操作的。其实就是我们常说的Undefined,
Null, Boolean, String, Number, 和 Object。

而规范类型相当于meta-values,是用来用算法描述ECMAScript语言结构和ECMAScript语言类型的。规范类型包括:Reference,
List, Completion, Property Descriptor, Property Identifier, Lexical
Environment, 和 Environment Record。

没懂?没关系,我们重点看其中的Reference类型。

了解规范

英文版:http://es5.github.io/#x15.1
中文版:http://yanhaijing.com/es5/#115

Reference


那什么又是 Reference ?

让我们看 8.7 章 The Reference Specification Type

The Reference type is used to explain the behaviour of such operators
as delete, typeof, and the assignment operators.

所以 Reference 类型就是用来解释诸如 delete、typeof
以及赋值等操作行为的。

抄袭尤雨溪大大的话,就是:

这里的 Reference 是一个 Specification Type,也就是
“只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的
js 代码中

再看接下来的这段具体介绍 Reference 的内容:

A Reference is a resolved name binding.

A Reference consists of three components, the base value, the
referenced name and the Boolean valued strict reference flag.

The base value is either undefined, an Object, a Boolean, a String, a
Number, or an environment record (10.2.1).

A base value of undefined indicates that the reference could not be
resolved to a binding. The referenced name is a String.

这段讲述了 Reference 的构成,由三个组成部分,分别是:

  1. base value: 就是属性所在的对象或者就是
    EnvironmentRecord,它的值只可能是 undefined, an Object, a Boolean, a
    String。

  2. referenced name: 就是属性的名称。

  3. strict 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
};

而且规范中还提供了获取 Reference 组成部分的方法,比如 GetBase 和
IsPropertyReference

这两个方法很简单,简单看一看:

1.GetBase

GetBase(V). Returns the base value component of the reference V.

返回 reference 的 base value

  1. IsPropertyReference

IsPropertyReference(V). Returns true if either the base value is an
object or HasPrimitiveBase(V) is true; otherwise returns false.

简单的理解:如果 base value 是一个对象,就返回true

Reference

那什么又是Reference?

让我们看8.7章 The Reference Specification Type:

The Reference type is used to explain the behaviour of such operators
as delete, typeof, and the assignment operators.

所以Reference类型就是用来解释诸如delete、typeof以及赋值等操作行为的。

抄袭尤雨溪大大的话,就是:

这里的 Reference 是一个 Specification Type,也就是
“只存在于规范里的抽象类型”。它们是为了更好地描述语言的底层行为逻辑才存在的,但并不存在于实际的
js 代码中。

再看接下来的这段具体介绍Reference的内容:

A Reference is a resolved name binding.

A Reference consists of three components, the base value, the
referenced name and the Boolean valued strict reference flag.

The base value is either undefined, an Object, a Boolean, a String, a
Number, or an environment record (10.2.1).

A base value of undefined indicates that the reference could not be
resolved to a binding. The referenced name is a String.

这段讲了Reference有三个组成部分,分别是:

  • base value
  • referenced name
  • strict reference

而且base value是undefined, an Object, a Boolean, a String, a Number, or
an environment record其中的一种

reference name是字符串。

可是这些到底是什么呢?

让我们简洁的理解base
value是属性所在的对象或者就是EnvironmentRecord,referenced
name就是属性的名称

嗯,举个例子:

var foo = 1; var fooReference = { base: EnvironmentRecord, name: ‘foo’,
strict: false };

1
2
3
4
5
6
7
var foo = 1;
 
var fooReference = {
  base: EnvironmentRecord,
  name: ‘foo’,
  strict: false
};

再举个例子:

var foo = { bar: function () { return this; } }; foo.bar(); // foo var
fooBarReference = { base: foo, propertyName: ‘bar’, strict: false };

1
2
3
4
5
6
7
8
9
10
11
12
13
var foo = {
  bar: function () {
    return this;
  }
};
foo.bar(); // foo
 
var fooBarReference = {
  base: foo,
  propertyName: ‘bar’,
  strict: false
};

而且规范中还提供了可以获取Reference组成部分的方法,比如 GetBase 和
IsPropertyReference

这两个方法很简单,简单看一看:

1.GetBase

GetBase(V). Returns the base value component of the reference V.

返回reference的base value

2.IsPropertyReference

IsPropertyReference(V). Returns true if either the base value is an
object or HasPrimitiveBase(V) is true; otherwise returns false.

简单的理解:base value是object,就返回true

1、Types

Types are further subclassified into ECMAScript language types and
specification types.

An ECMAScript language type corresponds to values that are directly
manipulated by an ECMAScript programmer using the ECMAScript language.
The ECMAScript language types are Undefined, Null, Boolean, String,
Number, and Object.

A specification type corresponds to meta-values that are used within
algorithms to describe the semantics of ECMAScript language constructs
and ECMAScript language types. The specification types are Reference,
List, Completion, Property Descriptor, Property Identifier, Lexical
Environment, and Environment Record.

翻译过来就是:

类型又再分为ECMAScript语言类型和规范类型。

ECMAScript语言类型是开发者使用ECMAScript直接操作的类型。ECMAScript语言类型是Undefined,Null,Boolean,String,
Number, 和Object。

规范类型相当于meta-values,用来用算法来描述ECMAScript
语言结构和语言类型的。规范类型是:Reference,List,Completion,Property
Descriptor,Property Identifier, Lexical Environment, and Environment
Record。

我们需要知道在 ECMAScript
规范中还有一种只存在于规范中的类型,它们的作用是用来描述语言底层行为逻辑。

GetValue


除此之外,紧接着在8.7.1 章规范中就讲了一个用于从 Reference
类型获取对应值的方法: GetValue

简单模拟 GetValue 的使用:

var foo = 1;

var fooReference = {
    base: EnvironmentRecord,
    name: 'foo',
    strict: false
};

GetValue(fooReference) // 1;

GetValue 返回对象属性真正的值,但是要注意:
调用 GetValue,返回的将是具体的值,而不再是一个 Reference

GetValue

除此之外,紧接着规范中就讲了一个GetValue方法,在8.7.1章

简单模拟GetValue的使用:

var foo = 1; var fooReference = { base: EnvironmentRecord, name: ‘foo’,
strict: false }; GetValue(fooReference) // 1;

1
2
3
4
5
6
7
8
9
var foo = 1;
 
var fooReference = {
  base: EnvironmentRecord,
  name: ‘foo’,
  strict: false
};
 
GetValue(fooReference) // 1;

GetValue返回对象属性真正的值,但是要注意,调用GetValue,返回的将是具体的值,而不再是一个Reference,这个很重要。

那为什么要讲References呢?

2、Reference

8.7 章 The Reference Specification Type:

The Reference type is used to explain the behaviour of such operators
as delete, typeof, and the assignment operators.

Reference 类型是用来解释删除,typeof和赋值操作等行为的。
Reference由三部分组成:

  • base value
  • referenced name
  • strict reference
    简单理解,base value 就是属性所在的对象或者就是
    EnvironmentRecord,它的值只可能是 undefined, an Object, a Boolean, a
    String, a Number, or an environment record 其中的一种。

base value 就是属性所在的对象或者就是 EnvironmentRecord,它的值只可能是
undefined, an Object, a Boolean, a String, a Number, or an environment
record 其中的一种。
举两个栗子:

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
};

规范中还提供了获取 Reference 组成部分的方法,比如 GetBase 和
IsPropertyReference。
GetBase,返回 reference 的 base value。
IsPropertyReference,如果 base value 是一个对象,就返回true。

如何确定this的值


规范 11.2.3 Function Calls

这里讲了当函数调用的时候,如何确定 this 的取值。
只看第一步、第六步、第七步:

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.

让我们描述一下:

  1. 计算 MemberExpression 的结果赋值给 ref

  2. 判断 ref 是不是一个 Reference 类型

     2.1 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
     2.2 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)
     2.3 如果 ref 不是 Reference,那么 this 的值为 undefined
    

发表评论

电子邮件地址不会被公开。 必填项已用*标注