您的位置:澳门新葡8455最新网站 > Web前端 > 从ECMAScript规范解读this,深入之从

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

发布时间:2019-11-05 11:17编辑:Web前端浏览(75)

    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.

    澳门新葡8455最新网站,翻译过来正是:

    品类又再分为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)());

    在试验第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

    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 赞 收藏 评论

    澳门新葡8455最新网站 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
    

    其余,又怎么规定调用函数的目的是何人呢?

    本文由澳门新葡8455最新网站发布于Web前端,转载请注明出处:从ECMAScript规范解读this,深入之从

    关键词:

上一篇:没有了

下一篇:没有了