深深之从,从ECMAScript规范解读this

by admin on 2019年3月16日

JavaScript 深刻之从 ECMAScript 规范解读 this

2017/05/17 · JavaScript
· this

原稿出处: 冴羽   

前面都是依照函数的采纳情形来驾驭this,分为三种情景:

原稿出处

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

世家好,作者是IT修真院麦纳麦分院第06期学员,一枚正直善良的web程序员。

前言

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

对于种种执行上下文,都有多个重庆大学性质

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

明天重点讲讲this,可是倒霉讲。

……

因为大家要从ECMASciript5正规早先讲起。

先奉上ECMAScript 5.1正经地点:

英文版:

中文版:

让大家开端通晓规范吧!

  • 壹 、作为指标方法调用
  • 二 、作为普通函数调用
  • 三 、构造器调用
  • 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
规范中还有一种只存在高满堂式中的类型,它们的功效是用来描述语言底层行为逻辑

明天给大家享用一下,修真院官网JS任务中也许会选择到的知识点:

Types

先是是第七章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 是2个 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 是2个目的,就赶回true

简述Js中this的指向


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 是3个 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
};

必发88,再举个例证:

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

深深之从,从ECMAScript规范解读this。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 章规范中就讲了3个用于从 Reference
类型获取对应值的格局: GetValue

不难易行模拟 GetValue 的应用:

var foo = 1;

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

GetValue(fooReference) // 1;

GetValue 再次来到对象属性真正的值,但是要注意:
调用 GetValue,重返的将是切实的值,而不再是三个 Reference

1.背景介绍

当JavaScript代码执行一段可进行代码(executable
code)时,会成立对应的推行上下文(execution context)。
对此各类执行上下文,都有多个第1性质:

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

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 是3个目的,就回来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
    

2.知识剖析

什么规定this的值

看规范11.2.3 Function Calls。

此间讲了当函数调用的时候,如何规定this的取值

看率先步 第5步 第9步:

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是否1个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,看规范规定那一个操作是还是不是会回来3个Reference类型。

举最后3个例子:

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是否3个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.

回去了2个Reference类型!

该值为:

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

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

然后这些因为base value是2个指标,所以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

具体分析


让大家一步一步看:

  1. 总结 MemberExpression 的结果赋值给 ref

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

由此简单明了 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)());
ECMASCRubiconIPT 的项目分为语言类型和标准类型。
  • ECMAScript 语言类型是开发者直接使用 ECMAScript
    能够操作的。其实正是我们常说的Undefined, Null, Boolean, String,
    Number, 和 Object。
  • ECMAScript
    规范中还有一种只设有王芸规中的类型,它们的效率是用来描述语言底层行为逻辑。

多说一句

即使大家不或许去分明每2个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.

翻译一下正是:
壹 、将MemberExpression的结果赋值给ref
二 、判断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

foo.bar()

在示例 1 中,MemberExpression 计算的结果是 foo.bar,那么 foo.bar
是否贰个 Reference 呢?

翻看规范 11.2.1 Property
Accessors
,那里显得了1个划算的进度,什么都不管了,就看最终一步:

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

接下去依照 2.1 的判定流程走:

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

该值是 Reference 类型,那么 IsPropertyReference(ref) 的结果是稍稍呢?

眼下大家已经铺垫了 IsPropertyReference 方法,如若 base value
是三个目的,结果回到 true。

base value 为 foo,是多少个指标,所以 IsPropertyReference(ref) 结果为
true。

本条时候我们就能够规定 this 的值了:

this = GetBase(ref)

GetBase 也早已铺垫了,获得 base value 值,这么些事例中就是foo,所以 this
的值便是 foo ,示例1的结果正是 2!

其间第叁是就是行业内部类型中的 Reference 类型。它与 this 的对准有着密切的关联。

此地的 Reference 是八个 Specification Type,也正是“只存在于专业里的架空类型”。它们是为了更好地描述语言的平底行为逻辑才存在的,但并不存在于实际的
js 代码中。
REFERENCE 的组合,由多少个组成都部队分,分别是:

  • base value
  • referenced name
  • strict reference

base value 正是性质所在的靶子或许就是EnvironmentRecord,它的值只恐怕是 undefined, an Object, a Boolean, a
String, a Number, or an environment record 个中的一种。

referenced name 便是性质的称呼。
举个例证:

  var foo = 1;
// 对应的Reference是:
  var fooReference = {
         base: EnvironmentRecord,
         name: 'foo',
         strict: false
    };

并且规范中还提供了获得 REFERENCE 组成都部队分的格局,比如 GETBASE 和
ISPROPESportageTYREFERENCE。

  • GetBase(reference),重返的是reference的值
  • IsPropertyReference(reference),假使 base value
    是三个对象,就回去true。
  • GetValue,GetValue 重返对象属性真正的值,不过要留意:调用
    GetValue,重回的将是具体的值,而不再是1个 Reference

深入类别

JavaScript深刻类别臆度写十五篇左右,目的在于帮大家捋顺JavaScript底层知识,重点教学如原型、成效域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难题概念,与罗列它们的用法区别,这一个体系更看得起通过写demo,捋进程、模拟完毕,结合ES规范等方法来讲课。

全体小说和demo都能够在github上找到。假诺有错误或然不行事极为谨慎的地点,请务必给予指正,10分多谢。要是喜欢照旧有所启发,欢迎star,对笔者也是一种鞭策。

本系列:

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

    1 赞 收藏
    评论

必发88 1

具体分析

(foo.bar)()

看示例2:

console.log((foo.bar)());

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
的结果是一样的。

当函数调用时,如何明确THIS的值?

1.划算MemberExpression的结果赋给ref
2.判定ref是否叁个Reference类型
a.如若ref是Reference,并且ref的base value是八个指标,那么 this 的值为 base
value
如果 ref 是 Reference,并且 base value 值是 Environment Record,
那么this的值为 ImplicitThisValue(ref)即为undefind
b.如果 ref 不是 Reference,那么 this 的值为 undefined
举个例证:

                    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()在示例 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.

咱俩识破该表明式再次回到了1个 Reference
类型!根据在此以前的剧情,大家了然该值为:

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

接下去依照 2.1 的论断流程走:该值是 Reference
类型,然后实施IsPropertyReference(ref),假诺ref的base
value是2个对象则赶回true,那么 this 的值为 base value 即那个foo

实例3,4,5中,MemberExpression总计结果个别为(foo.bar=foo.bar),(false||foo.var),(f00,bar,foo,bar)

基于标准,逗号操作符,逻辑或操作符和赋值操作符都会举行:

3.Let rval be GetValue(ref).

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

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

由此那几个this都指向了大局对象


壹 、将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 其实正是()左侧的片段。

(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
的时候,其值会被隐式转换为大局对象

3.广大难题

1.如何转移this的指向?


② 、判断ref是或不是2个Reference类型

一言九鼎就在于看规范是什么处理各类MemberExpression,再次回到的结果是或不是三个Reference类型。
还举3个事例:

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,那里显得了3个划算的经过,什么都不管了,就看最后一步:

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

(false || foo.bar)()

看示例4,逻辑与算法,查看规范 11.11 Binary Logical Operators
计量第2步:

2.Let lval be GetValue(lref).

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

4.消除方案

答:可以使用call只怕apply的不二法门:

                // 一个对象可以作为call和apply的第一个参数,并且this会被绑定到这个对象。
                var obj = {a: 'Custom'};

                // 这个属性是在global对象定义的。
                var a = 'Global';

                function whatsThis(arg) {
                  console.log(this.a) // this的值取决于函数的调用方式
                }

                whatsThis();          // 'Global'
                whatsThis.call(obj);  // 'Custom'
                whatsThis.apply(obj); // 'Custom'

结果公布

因此结果正是

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 会报错。

(foo.bar, foo.bar)()

看示例5,逗号操作符,查看规范11.14 Comma Operator ( , )

计算第②步:

2.Call GetValue(lref).

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

5.编码实战


最简便易行的气象
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,并不是3个 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。

发布结果

就此最终1个事例的结果是:

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 会报错。

6.扩张思考

以下代码的this的针对性?

                    function Foo(){
                        getName = function(){
                            console.log(1);
                            };
                        return this
                    }

                    function getName(){
                        console.log(5);
                    }

                    Foo().getName();

at last

就算大家得以不难的精晓 this
为调用函数的对象,借使是那样的话,如何解释下边这些例子吗?

var value = 1;

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

别的,又怎样规定调用函数的指标是何人吧?

补充

最末尾,忘记了贰个最最平凡的情况:

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

foo(); 

MemberExpression 是 foo,解析标识符,查看规范 10.3.1 Identifier
Resolution,会再次来到1个 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。

7.参考文献

参考一:JavaScript深远之从ECMAScript规范解读this

参考一:this


8.越来越多斟酌

Q1:构造函数的this指向?
A1:构造函数的this指向实例对象

Q2:除了call,applay还有别的改动this指向的法子吧?
A2:还是能应用new关键字和bing()方法改变this的对准

Q3:reference是什么?
A3:reference类型是js中的规范类型,是用来叙述底层行为逻辑的品种,并不存在于实际的js代码中


视频
PPT

发表评论

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

网站地图xml地图