分为四种情况,首先是第 betway体育app8 章 Types

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

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 规范中还有一种只存在于规范中的类型,它们的作用是用来描述语言底层行为逻辑。

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

当函数调用时,如何确定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.

我们得知该表达式返回了一个 Reference 类型!根据之前的内容,我们知道该值为:

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

接下来按照 2.1 的判断流程走:该值是 Reference 类型,然后执行IsPropertyReference(ref),如果ref的base value是一个对象则返回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都指向了全局对象


参考

tc39/ecma262
proposals/stage-0-proposals.md
ECMAScript This-Binding Syntax
ECMAScript 2017 Language Specification

多说一句

尽管我们不可能去确定每一个this的指向都从规范的角度去思考,久而久之,我们就会总结各种情形来告诉大家这种情形下this的指向,但是能从规范的角度去看待this的指向,绝对是一个不一样的角度,该文有不严谨的地方,还请大神指正!

具体分析

具体分析


让我们一步一步看:

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

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'

2. The Reference Specification Type

The Reference Specification Type,是一个语言规范内置的类型(ECMAScript Specification Types)。

Reference Specification Type包含两种值,ReferenceSuper Reference
(1)Reference是一种数据结构,包括base valuereferenced name
以及strict reference flag三个组成部分。

(2)Super Reference,多了一个thisValue字段,
用于处理super()以及super.method()调用过程。

The base value component is either undefined, an Object, a Boolean, a String, a Symbol, a Number, or an Environment Record.
The referenced name component is a String or Symbol value.

与之相关联有几个函数,下文会用到:
(1)GetBase(V)会返回一个Reference的base value

(2)GetValue用于获取词法环境中绑定的值,或者对象的属性值。

betway体育app 1

(3)GetThisValue,对于Reference则返回base value
否则对于Super Reference则返回它的thisValue字段。

betway体育app 2


前言

在《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 的指向,读完感觉收获很大,赶紧记录下来。

原文出处

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

6.扩展思考

以下代码的this的指向?

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

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

                    Foo().getName();

(1)串联

BindExpression :
<i>    </i>LeftHandSideExpression :: [lookahead ≠ new] MemberExpression

  • Let baseReference be the result of evaluating LeftHandSideExpression.
  • Let baseValue be GetValue(baseReference).
  • Let targetReference be the result of evaluating MemberExpression.
  • Let target be GetValue(targetReference).
  • If IsCallable(target) is false, throw a TypeError exception.
  • Let F be ? BoundFunctionCreate(target, baseValue, «»).
  • Return InitializeBoundFunctionProperties(F, target).

<u></u>

根据BoundFunctionCreate (targetFunction, boundThis, boundArgs)的定义,我们知道,
BoundFunctionCreate的第二个参数就是所创建函数的this值,
因此GetValue(baseReference)的值就是this

根据GetValue的定义可知,
如果baseReference是一个词法变量,则返回它的绑定值,
如果它是一个对象属性,就返回这个属性值。

结论:
如果::是串联使用,那么this会被绑定为::左边的值,
无论左边是一个变量(例如,x::yy中的this绑定为x的值),
还是对象的属性(例如,a.b::yy中的this绑定为a.b的值)。

本文由必威发布于必威-前端,转载请注明出处:分为四种情况,首先是第 betway体育app8 章 Types

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。