JavaScript原理----从 ECMAScript 规范解读 this

一、准备

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

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

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

今日重点: Reference 类型,它与 this 的指向有着密切的关联。

二、Reference介绍

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

Reference 由三个部分组成,分别是:

  • base value
  • referenced name
  • strict reference

简单理解:

base value :属性所在的对象或者就是 EnvironmentRecord,它的值只可能是 undefined, Object, Boolean, String, Number, EnvironmentRecord 其中的一种。

referenced name :属性的名称。

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(ref):返回 reference 的 base value。

2.IsPropertyReference(ref):简单的理解----如果 reference 的base value 是一个对象,就返回true。

3.GetValue(ref):返回对象属性真正的值。注意--调用 GetValue,返回的将是具体的值,而不再是一个 Reference。

模拟GetValue()使用:

var foo = 1;

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

GetValue(fooReference) // 1;

三、重点----如何确定this的值

步骤:

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

MemberExpression :

  • PrimaryExpression // 原始表达式 可以参见《JavaScript权威指南第四章》
  • FunctionExpression // 函数定义表达式
  • MemberExpression [ Expression ] // 属性访问表达式
  • MemberExpression . IdentifierName // 属性访问表达式
  • new MemberExpression Arguments // 对象创建表达式

举个栗子:

function foo() {
    console.log(this)
}

foo(); // MemberExpression 是 foo

function foo() {
    return function() {
        console.log(this)
    }
}

foo()(); // MemberExpression 是 foo()

var foo = {
    bar: function () {
        return this;
    }
}

foo.bar(); // MemberExpression 是 foo.bar

简单理解:MemberExpression 其实就是()左边的部分。

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

2.1 [ref 是 Reference] && [IsPropertyReference(ref) === true] => [this 的值为 GetBase(ref)]

2.2 [ref 是 Reference] && [ref.baseValue === EnvironmentRecord] => [this 的值为 ImplicitThisValue(ref) => undefined]
(ImplicitThisValue函数始终返回 undefined,所以最后 this 的值就是 undefined)

2.3 [ref 不是 Reference] => [this 的值为 undefined]

举个栗子:

// 非严格模式
var value = 1;

var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}


console.log(foo.bar()); // foo.bar是Reference类型,this指向base value => foo,结果 => 2

console.log((foo.bar)()); // 同上判断,结果 => 2

console.log((foo.bar = foo.bar)()); // 有赋值操作符,返回值为不是Reference类型,this指向 undefined => window, 结果 => 1

console.log((false || foo.bar)()); // 逻辑操作符,返回值为不是Reference类型,结果 => 1

console.log((foo.bar, foo.bar)()); // 逗号操作符,返回值为不是Reference类型,结果 => 1

追根溯源的从 ECMASciript 规范讲解 this 的指向,尽管从这个角度写起来和读起来都比较吃力,但是一旦多读几遍,明白原理,绝对会给你一个全新的视角看待 this 。

本文参考掘金前端大神冴羽的文章:原文链接

 

你可能感兴趣的:(js,js)