云顶娱乐手机官网-云顶娱乐网址

热门关键词: 云顶娱乐手机官网,云顶娱乐网址

JavaScript 深入之从 ECMAScript 规范解读 this

2019-10-03 作者:前端开发   |   浏览(90)

JavaScript 深切之从 ECMAScript 标准解读 this

2017/05/17 · JavaScript · this

原稿出处: 冴羽   

在此以前都是依靠函数的应用情状来掌握this,分为各类情景:

前言

在《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。

没懂?无妨,大家最主要看其中的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有八个组成都部队分,分别是:

  • 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

除外,紧接着标准中就讲了多个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).

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

在考察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

GetValue

除此之外,紧接着在 8.7.1 章标准中就讲了二个用来从 Reference 类型获取对应值的办法: GetValue。
简单来讲模拟 GetValue 的选取:

var foo = 1;

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

GetValue 再次来到对象属性真正的值,但是要小心:

调用 GetValue,重返的将是现实性的值,而不再是三个 Reference

多说一句

纵然大家不容许去鲜明每二个this的针对都从标准的角度去思虑,经过了相当长的时间,大家就能够计算种种场馆来告诉大家这种情形下this的指向,可是能从行业内部的角度去对待this的针对,相对是多少个不等同的角度,该文有不严格的地方,还请大神指正!

怎样分明this值

至于 Reference 讲了那么多,为何要讲 Reference 呢?到底 Reference 跟本文的大旨 this 有怎么着关系呢?尽管你能耐心看完在此之前的原委,以下开端步入高能阶段。
看规范 11.2.3 Function Calls:
这里讲了当函数调用的时候,怎么样分明 this 的取值。
只看率先步、第六步、第七步:

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. Else, Type(ref) is not Reference.
    a. Let thisValue be undefined.

翻译一下就是:
1、将MemberExpression的结果赋值给ref
2、判定ref是或不是三个Reference类型
3、如果ref是Reference,并且IsPropertyReference(ref)是true,那么this的值为GetBase(ref)
4、如果ref是Reference,并且base value值是Enviroment Recored,那么this值为ImplicitThisValue(ref)
5、如果ref不是reference,那么this的值为undefined

深切类别

JavaScript深刻连串猜想写十五篇左右,意在帮大家捋顺JavaScript底层知识,注重解说如原型、成效域、试行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、承接等困难概念,与罗列它们的用法不一致,这么些连串更珍惜通过写demo,捋进度、模拟达成,结合ES标准等艺术来教学。

享有小说和demo都足以在github上找到。假使有错误或许不当心的地点,请必得给予指正,十二分多谢。倘使喜欢依然有所启发,招待star,对作者也是一种鞭笞。

本系列:

  1. JavaScirpt 深入之从原型到原型链
  2. JavaScript 深切之词法成效域和动态功能域
  3. JavaScript 深切之实践上下文栈
  4. JavaScript 深刻之变量对象
  5. JavaScript 深远之效果域链

    1 赞 收藏 评论

图片 1

具体分析
1、将MemberExpression的结果赋值给ref

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

  • PrimaryExpression
  • 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类型

最重要就在于看标准是怎么管理种种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)());

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.

笔者们领略foo.bar的Reference值为:

var Reference = {
  base: foo,
  name: 'bar',
  strict: false
}

借下来依据2.1的论断流程走。

2.1 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
该值是reference类型,那么 IsPropertyReference(ref) 是有一些吗?
基于前边的只是,假若base value是三个目的,结果再次回到true。base value为foo,是一个对象,所以IsPropertyReference(ref)结果为true。
本条时候我们即可规定this的值了: this = GetBase(ref);
GetBase可以赢得base value值,这几个事例中是foo,所以this的值正是foo,示例1的结果正是2!

(foo.bar)()
看示例2:console.log((foo.bar)());
foo.bar被()包住,查看标准11.1.6The 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.

翻译:

回去施行Expression的结果,它只怕是Reference类型。
这一算法并不会功效GetValue于实行Expression的结果。

实在 () 并从未对 MemberExpression 进行测算,所以实际上跟示例 1 的结果是同样的。

(foo.bar = foo.bar)()
看示例3,有赋值操作符,查看典型 11.13.1 Simple Assignment ( = ):
测算的第三步:

3.Let rval be GetValue(rref).

因为运用了 GetValue,所以回来的值不是 Reference 类型。依据从前的判断逻辑:

2.3 如果 ref 不是Reference,那么 this 的值为 undefined

this 为 undefined,非严苛格局下,this 的值为 undefined 的时候,其值会被隐式转变为大局对象。

(false || foo.bar)()
看示例4,逻辑与算法,查看标准 11.11 Binary Logical Operators:

2.Let lval be GetValue(lref).

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

(foo.bar, foo.bar)()
看示例5,逗号操作符,查看标准11.14 Comma Operator ( , )
算算第二步:

2.Call GetValue(lref).

因为运用了 GetValue,所以回来的不是 Reference 类型,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

注意:以上是在非严酷方式下的结果,严刻方式下因为 this 重回undefined,所以示例 3 会报错。

最简易的处境
function foo() {
    console.log(this)
}

foo(); 

MemberExpression 是 foo,解析标志符,查看标准 10.3.1 Identifier Resolution,会回到二个 Reference 类型的值:
var fooReference = {
base: EnvironmentRecord,
name: 'foo',
strict: false
};
接下去实行判断:

2.1 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)

因为 base value 是 EnvironmentRecord,并不是三个 Object 类型,还记得前面讲过的 base value 的取值恐怕吗? 只可能是 undefined, an Object, a Boolean, a String, a Number, 和 an environment record 中的一种。
IsPropertyReference(ref) 的结果为 false,步入下个剖断:

2.2 如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)

base value 就是 Environment Record,所以会调用 ImplicitThisValue(ref)
查看标准 10.2.1.1.6,ImplicitThisValue 方法的介绍:该函数始终重返undefined。
为此最后 this 的值就是 undefined。

at last

就算大家可以简轻易单的敞亮 this 为调用函数的对象,借使是那样的话,如何分解上面这几个事例吗?

var value = 1;

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

其余,又怎么着显明调用函数的目的是什么人吧?

本文由云顶娱乐手机官网发布于前端开发,转载请注明出处:JavaScript 深入之从 ECMAScript 规范解读 this

关键词:

  • 上一篇:没有了
  • 下一篇:没有了