JS中的this指向问题

作者: 慕时_木雨凡 | 来源:发表于2019-07-26 18:40 被阅读7次

    this是我们日常最经常使用的语法之一。
    通过这篇文章分析一下this的指向问题。
    说到this就一分为二来看(ES6之前的this)和(ES6中的this)。
    由于ES6中的箭头函数对this的指向做了新的底层处理,使得ES6中的this相对更好理解一些。

    this解决了什么问题(没有this会怎么样)

    没有this的话我们就需要显式的传入一个上下文对象

    var me = {
        name: 'Jack'
    }
    var you = {
        name: 'Lucy'
    }
    function identify(context) {
        return context.name.toUpperCase();
    }
    function speak(context) {
      return 'Hello ' + context.name
    }
    
    identify( you ); // LUCY
    speak( me ); //hello Jack
    

    有了this之后我们可以这样写,隐式的传递一个对象的引用,我们在进行复杂的Api设计时this就会使代码变得更加简洁和健壮

    var me = {
        name: 'Jack'
    }
    var you = {
        name: 'Lucy'
    }
    function identify() {
        return this.name.toUpperCase();
    }
    function speak() {
      return 'Hello ' + this.name
    }
    
    identify.call( you ); // LUCY
    speak.call( me ); //hello Jack
    

    简述this的指向

    1. ES6之前
    • this的绑定和函数的声明位置没有任何关系,this的指向取决于函数的调用方式。
    1. ES6(箭头函数)中的this
    • 箭头函数中this是由函数所在的词法作用域决定的,箭头函数会继承外层函数调用的this进行绑定。(函数在哪里执行就指向哪里)
    function foo() { 
        setTimeout(() => {
            // 这里的 this 在词法上继承自外层函数foo()的this指向
            console.log( this.a );
        },100);
    }
    var obj = { 
        a:2
    };
    foo.call( obj ); // 2
    

    ES6之前的this绑定规则

    默认绑定(适用于独立函数的调用)。

    • 默认情况下this指向全局的window对象。
    • 默认绑定只有在 非严格模式下才会生效
    function foo() {
      console.log(this) // Window {}
      console.log(this.a) // 2
    }
    var a = 2
    foo()
    

    隐式绑定。

    • 调用位置是否有上下文对象
    • 是否包含在某个对象中
    function foo() { 
        console.log( this.a );
    }
    var obj = { 
        a: 2,
        foo: foo
    };
    obj.foo(); // 2
    

    foo包含在obj对象中,当我们调用obj.foo()时,foo的上下文就是obj对象所以this就指向了obj对象。再次验证 this和函数的声明位置没有任何关系函数在哪里执行this就指向哪里

    function foo() { 
        console.log( this.a );
    }
    var obj2 = { 
        a: 42,
        foo: foo
    };
    var obj1 = { 
        a: 2,
        obj2: obj2 
    };
    obj1.obj2.foo(); // 42
    

    在链式调用中this会指向最后一层的上下文位置,所以这个例子中this指向了obj2对象。

    隐式绑定丢失

    • 当我们改变函数的调用关系时,也会改变内部的this指向
    function foo() { 
        console.log( this.a );
    }
    var obj = { 
        a: 2,
        foo: foo 
    };
    var bar = obj.foo; // 函数别名!
    var a = "global"; // a是全局对象的属性 
    bar(); // "global"   this指向当前bar所在的上下文
    

    显示绑定

    通过apply()、call()、band()强制改变this的绑定

    function foo() { 
        console.log( this.a );
    }
    var obj = { 
        a: 2,
    };
    foo.call(obj); // 2
    foo.apply(obj); // 2
    var a = 4;
    foo.bind(this)() // 4;
    

    new 绑定

    function foo(a) { 
        this.a = a;
        // 这里相当于隐式的返回  return this
    }
    var bar = new foo(2); 
    console.log( bar.a ); // 2
    

    这里需要说一下new操作符的具体做了什么

    • 创建(或者说构造)一个全新的对象。
    • 连接到 原型
    • 这个新对象会绑定到函数调用的this。
    • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动隐式的返回这个新对象。

    结合以上代码
    使用 new 来调用 foo() 时,我们会构造一个新对象并把它绑定到 foo() 调用中的 this 上

    this绑定规则的优先级问题

    如果以上的this绑定规则中多种同时存在的情况下如何确认this的指向
    new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

    this绑定规则

    1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
      var bar = new foo()

    2. 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
      var bar = foo.call(obj2)

    3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
      obj1.foo()

    4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到 全局对象。

    总结

    • 非箭头函数,如果要判断一个运行中函数的this绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断 this 的绑定对象。

      1. 由new调用?绑定到新创建的对象。
      2. 由call或者apply(或者bind)调用?绑定到指定的对象。
      3. 由上下文对象调用?绑定到那个上下文对象。
      4. 默认:在严格模式下绑定到undefined,否则绑定到全局对象。
    • 箭头函数有特定的绑定规则,根据当前的词法作用域来决定this,
      箭头函数会继承外层函数调用的this(无论 this 绑定到什么)。
      箭头函数的this指向函数所在(定义)位置的上下文对象

    相关文章

      网友评论

        本文标题:JS中的this指向问题

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