新浦京娱乐场官网-301net-新浦京娱乐www.301net
做最好的网站

《JavaScript深入之执行上下文栈》

JavaScript 深远之从 ECMAScript 标准解读 this

2017/05/17 · JavaScript · this

原稿出处: 冴羽   

前言

在《JavaScript深刻之执行上下文栈》中讲到,当JavaScript代码实施豆蔻梢头段可进行代码(executable code)时,会创建对应的实施上下文(execution context)。

对此每一种推行上下文,都有八个根特性质

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

几如今关键讲讲this,但是倒霉讲。

……

因为我们要从ECMASciript5正规初步讲起。

先奉上ECMAScript 5.1职业地点:

英文版:

中文版:

让大家早先精晓标准吧!

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

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

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 :

  • 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

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)());

在侦察第11中学,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上找到。假如有不当可能不行事极为稳重之处,请必得授予指正,非常多谢。若是喜欢只怕有所启示,接待star,对小编也是生机勃勃种驱策。

本系列:

  1. JavaScirpt 深刻之从原型到原型链
  2. JavaScript 浓烈之词法功能域和动态效能域
  3. JavaScript 深刻之试行上下文栈
  4. JavaScript 浓郁之变量对象
  5. JavaScript 浓郁之效果域链

    1 赞 收藏 评论

图片 1

本文由新浦京娱乐场官网-301net-新浦京娱乐www.301net发布于301net网站建设,转载请注明出处:《JavaScript深入之执行上下文栈》

您可能还会对下面的文章感兴趣: