菜单

JavaScript深入的于ECMAScript规范解读this

2018年11月15日 - JavaScript

JavaScript 深入之起 ECMAScript 规范解读 this

2017/05/17 · JavaScript
· this

原稿出处: 冴羽   

已去简书,原因参见
http://www.jianshu.com/p/0f12350a6b66。

前言

在《JavaScript深入的实施上下文栈》倍受说话到,当JavaScript代码执行一段可实施代码(executable
code)时,会创对应之实施上下文(execution context)。

对于每个执行上下文,都出三只至关重要性质

今天关键出口讲this,然而不好摆。

……

因为我们如果从ECMASciript5标准起来说起。

先期奉上ECMAScript 5.1专业地方:

英文版:http://es5.github.io/#x15.1

中文版:http://yanhaijing.com/es5/#115

给我们开始询问规范吧!

即便人微言轻,但也要发生协调的态度。

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类型。

章好以自己的 Github
https://github.com/mqyqingfeng/Blog
查看

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是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

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呢?

如何确定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).

1
2
3
4
  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.

JavaScript

a. Let thisValue be undefined.

1
  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值是Environment Record, 那么this =
ImplicitThisValue(ref),
2.3.如果ref不是Reference,那么 this = undefined

让咱们同步一步看:

  1. 计算MemberExpression

什么是MemberExpression?看规范11.2 Left-Hand-Side Expressions:

MemberExpression :

推选个例:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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其实就是()左边的一对

接通下就判定MemberExpression的结果是匪是Reference,这时候就要扣押规范是安处理各种MemberExpression,看规范规定这些操作是勿是碰头回来一个Reference类型。

推选最后一个例:

var value = 1; var foo = { value: 2, bar: function () { return
this.value; } } //试验1 console.log(foo.bar()); //试验2
console.log((foo.bar)()); //试验3 console.log((foo.bar = foo.bar)());
//试验4 console.log((false || foo.bar)()); //试验5 console.log((foo.bar,
foo.bar)());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var value = 1;
 
var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}
 
//试验1
console.log(foo.bar());
//试验2
console.log((foo.bar)());
//试验3
console.log((foo.bar = foo.bar)());
//试验4
console.log((false || foo.bar)());
//试验5
console.log((foo.bar, foo.bar)());

以试1遭到,MemberExpression计算的结果是foo.bar,那么foo.bar是无是一个Reference呢?

查阅规范11.2.1 Property
Accessors,这里显得了一个乘除的长河,什么还无了,就扣留最后一步

Return a value of type Reference whose base value is baseValue and
whose referenced name is propertyNameString, and whose strict mode
flag is strict.

回来了一个Reference类型!

该值为:

var Reference = { base: foo, name: ‘bar’, strict: false };

1
2
3
4
5
var Reference = {
  base: foo,
  name: ‘bar’,
  strict: false
};

下一场是为base value是一个目标,所以IsPropertyReference(ref)是true,

那么this = GetBase(ref),也就算是foo, 所以this指于foo,试验1的结果就是是 2

嗳呀妈呀,为了证实this指为foo,累够呛我了!

剩下的便很快了:

看试验2,使用了()包住了foo.bar

翻看规范11.1.6 The Grouping Operator

Return the result of evaluating Expression. This may be of type
Reference.

NOTE This algorithm does not apply GetValue to the result of
evaluating Expression.

实际上()并没有对准MemberExpression进行测算,所以跟试验1是千篇一律的。

看试验3,有赋值操作符
翻开规范11.13.1 Simple Assignment ( = ):

算算的老三步:

3.Let rval be GetValue(rref).

因以了GetValue,所以回来的匪是reference类型,this为undefined

圈试验4,逻辑云算法

翻开规范11.11 Binary Logical Operators:

算算第二步:

2.Let lval be GetValue(lref).

因为使用了GetValue,所以回来的不是reference类型,this为undefined

圈试验5,逗号操作符
翻规范11.14 Comma Operator ( , )

算算第二步:

2.Call GetValue(lref).

因为用了GetValue,所以回来的免是reference类型,this为undefined

然而注意在非严格模式下,this的价为undefined的时刻,其值会为隐式转换为大局对象。

据此最后一个例的结果是:

var value = 1; var foo = { value: 2, bar: function () { return
this.value; } } //试验1 console.log(foo.bar()); //2 //试验2
console.log((foo.bar)()); //2 //试验3 console.log((foo.bar =
foo.bar)()); //1 //试验4 console.log((false || foo.bar)()); //1 //试验5
console.log((foo.bar, foo.bar)()); //1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var value = 1;
 
var foo = {
  value: 2,
  bar: function () {
    return this.value;
  }
}
 
//试验1
console.log(foo.bar()); //2
//试验2
console.log((foo.bar)()); //2
//试验3
console.log((foo.bar = foo.bar)()); //1
//试验4
console.log((false || foo.bar)()); //1
//试验5
console.log((foo.bar, foo.bar)()); //1

专注:严格模式下盖this返回undefined,所以试验3见面报错

末了,忘记了一个最极端普通的情况:

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

1
2
3
4
5
function foo() {
    console.log(this)
}
 
foo();

MemberExpression是foo,解析标识符
查规范10.3.1 Identifier Resolution

会回一个 Reference类型

而 base value是 Environment Record,所以会调用ImplicitThisValue(ref)

查阅规范10.2.1.1.6

一直返回undefined

据此最终this的价是undefined

基本上说一样句

尽管我们不可能夺确定各级一个this的指向都由规范的角度去思,久而久之,我们就是会见总结各种场面来喻大家这种状态下this的对,但是能够从正规的角度去对this的指向,绝对是一个免平等的角度,该文有非小心的地方,还伸手大神指正!

深刻系列

JavaScript深入系列预计写十五篇左右,旨在救助大家捋顺JavaScript底层知识,重点教学如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念,与陈它们的用法不同,这个系列还珍惜通过描写demo,捋过程、模拟实现,结合ES规范等办法来教学。

不无文章与demo都得以在github上https://github.com/mqyqingfeng/Blog找到。如果生不当或未兢兢业业的地方,请务必与指正,十分感谢。如果爱或具有启发,欢迎star,对作者也是如出一辙种植鞭策。

本系列:

  1. JavaScirpt 深入的起原型到原型链
  2. JavaScript
    深入之词法作用域和动态作用域
  3. JavaScript 深入之行上下文栈
  4. JavaScript 深入的变量对象
  5. JavaScript 深入之作用域链

    1 赞 收藏
    评论

图片 1

相关文章

发表评论

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

网站地图xml地图