美文网首页IT修真院-前端
简述Js中this的指向

简述Js中this的指向

作者: 风流司郎中 | 来源:发表于2018-03-11 23:23 被阅读6次

    1.背景介绍

    当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。
    对于每个执行上下文,都有三个重要属性:

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

    2.知识剖析

    ECMASCRIPT 的类型分为语言类型和规范类型。
    • ECMAScript 语言类型是开发者直接使用 ECMAScript 可以操作的。其实就是我们常说的Undefined, Null, Boolean, String, Number, 和 Object。
    • ECMAScript 规范中还有一种只存在于规范中的类型,它们的作用是用来描述语言底层行为逻辑。
    其中重点是便是规范类型中的 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 和 ISPROPERTYREFERENCE。

    • GetBase(reference),返回的是reference的值
    • IsPropertyReference(reference),如果 base value 是一个对象,就返回true。
    • GetValue,GetValue 返回对象属性真正的值,但是要注意:调用 GetValue,返回的将是具体的值,而不再是一个 Reference

    当函数调用时,如何确定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都指向了全局对象


    3.常见问题

    1.如何改变this的指向?


    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'
    

    5.编码实战


    6.扩展思考

    以下代码的this的指向?

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

    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

    相关文章

      网友评论

        本文标题:简述Js中this的指向

        本文链接:https://www.haomeiwen.com/subject/ievifftx.html