菜单

this

2019年4月14日 - 金沙前端

JavaScript 深切之从 ECMAScript 规范解读 this

2017/05/17 · JavaScript
· 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是或不是3个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 = foo.bar)()

看示例三,有赋值操作符,查看规范11.13.1 Simple Assignment ( = ):

3.Let rval be GetValue(rref).

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

依照事先讲的论断逻辑:

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

this 为 undefined,非严谨情势下,this 的值为 undefined
的时候,其值会被隐式转换为全局对象

一.背景介绍

当JavaScript代码执行一段可实施代码(executable
code)时,会成立对应的执行上下文(execution context)。
对此每种执行上下文,都有多个第3性质:


GetValue

除却,紧接着规范中就讲了2个GetValue方法,在八.柒.一章

粗略模拟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呢?

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,解析标识符,查看规范 拾.三.一 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,并不是2个 Object
类型,还记得后边讲过的 base value 的取值大概啊? 只大概是 undefined, an
Object, a Boolean, a String, a Number, 和 an environment record
中的1种。

IsPropertyReference(ref) 的结果为 false,进入下个判断:

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

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

翻开规范 10.二.一.1.6,ImplicitThisValue 方法的牵线:该函数始终重临undefined

因而最终 this 的值正是 undefined。

内部重点是就是规范类型中的 Reference 类型。它与 this 的针对有着密切的关联。

此地的 Reference 是3个 Specification Type,约等于“只设有于专业里的抽象类型”。它们是为了越来越好地描述语言的平底行为逻辑才存在的,但并不存在于实际的
js 代码中。
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 和
ISPROPEKugaTYREFERENCE。

何以分明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.

让我们描述一下:

一.测算MemberExpression的结果赋值给ref

2.断定ref是还是不是2个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,看规范规定这几个操作是否会回到2个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是否贰个Reference呢?

翻开规范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,试验一的结果正是 贰

嗳呀妈呀,为了求证this指向foo,累死笔者了!

剩余的就异常快了:

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

查阅规范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是一模1样的。

看试验3,有赋值操作符
查阅规范1一.一三.一 Simple Assignment ( = ):

测算的第贰步:

3.Let rval be GetValue(rref).

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

看试验4,逻辑云算法

查看规范11.1一 Binary Logical Operators:

算算第3步:

2.Let lval be GetValue(lref).

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

看试验五,逗号操作符
翻开规范11.14 Comma Operator ( , )

计量第二步:

2.Call GetValue(lref).

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

唯独注意在非严俊形式下,this的值为undefined的时候,其值会被隐式转换为大局对象。

所以最后2个例证的结果是:

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,解析标识符
查阅规范拾.三.一 Identifier Resolution

会回去贰个 Reference类型

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

翻开规范10.二.壹.1.六

一味重返undefined

所以最后this的值是undefined

GetValue

除去,紧接着在 八.7.一 章规范中就讲了一个用以从 Reference
类型获取对应值的章程: GetValue。
简易模拟 GetValue 的选拔:

var foo = 1;

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

GetValue 再次回到对象属性真正的值,可是要留意:

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

如何规定this的值


规范 11.2.3 Function Calls

此处讲了当函数调用的时候,怎么样明确 this 的取值。
只看率先步、第伍步、第8步:

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
    

七.参考文献

参考一:JavaScript浓密之从ECMAScript规范解读this

参考一:this


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

公布结果

为此最终3个事例的结果是:

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,所以示例 叁 会报错。

世家好,笔者是IT修真院卡拉奇分院第06期学员,一枚正直善良的web程序员。

相关文章

发表评论

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

网站地图xml地图