this的指向

116 阅读3分钟

this 的指向

this永远指向最后调用它的对象。this 的值取决于调用的模式
  • 方法调用模式

    var myObject = {
      value:0,
      increment:function(inc){
        this.value += typeof inc === 'number'?inc:1;
      }
    }
    myObject.increment();
    console.log(myObject.value) //1
    myObject.increment(2);
    console.log(myObject.value) //2
    
  • 函数调用模式

    var add = function(a, b) {
      return a + b;
    };
    myObject.double = function() {
      var helper = function() {
        this.value = add(this.value, this.value);
        console.log(this.value) //NaN
      };
      helper();//函数调用形式
    };
    myObject.double();
    
    console.log(myObject.value) //3
    

    原因:当使用函数调用模式,this 被绑定到全局对象,即使是对象内部函数被调用时,this 仍然绑定到外部函数的 this 变量。这种设计的错误后果就是方法不能利用内部函数来帮助其工作,因为内部函数的 this 被绑定了错误的值。

    解决方法: 该方法定义一个变量并给它赋值为 this, 则内部函数就可以通过那个变量访问到this。

    var add = function(a, b) {
      return a + b;
    };
    myObject.double = function() {
      var that = this;
      var helper = function() {
        this.value = add(this.value, this.value);
        console.log(this.value) //NaN
        console.log(that.value) //3
      };
      helper();
    };
    myObject.double();
    
    console.log(myObject.value)                            //3
    
  • 构造器调用模式

    js 是一门基于原型继承的语言,这意味着对象可以直接从其他原型对象中继承属性。

    如果在一个函数面前加 new 来调用,将会创建一个连接到该函数的 prototype成员的新对象,同时 this 也会绑定到那个新对象上。new前缀也会改变 return 语句的行为。

    var Quo = function(string){
    this.status = string;
    };
    Quo.prototype.get_status = function(){
      return this.status;
    };
    var myQuo = new Quo("confused");
    console.log(myQuo.get_status());                   //confused
    
  • Apply调用模式

    apply方法让我们创建一个参数数组传递给调用函数,它可以选择 this 的值。apply 方法接受两个参数,第一个是要绑定给 this 的值,第二个就是一个参数数组。

    var array = [3, 4];
    var sum = add.apply(null, array); //sum的值为7
    var statusObject = {
      status: 'A-OK'
    };
    //虽然 statusObject并没有继承自Quo.prototype,但我们可以在statusObject上调用get_status方法。
    var status = Quo.prototype.get_status.apply(statusObject);
    //status 的值为'A-OK'
    

    下面这道题是我在面试中遇到的,我不会的点还真的很多啊(下面的解答,大家直接去看原文就好了!

    function fun(){
      getName = function(){
        console.log(1);
      }
      return this
    }
    fun.getName = function(){
      console.log(2)
    }
    fun.prototype.getName = function(){
      console.log(3)
    }
    var getName = function(){
      console.log(4)
    }
    getName = function(){
      console.log(5)
    }
    fun.getName() //2
    getName()//5
    fun().getName() //1
    getName() //1
    new fun.getName() //2
    new fun().getName(); //3
    (new fun()).getName() //3
    (new fun).getName() //3
    new new fun().getName() //3
    
  • var getName = function () { 
        console.log('4');
    };
    function getName() { 
        console.log(5);
    }
    getName();  //4    
    

    点1:函数内部声明的getName 变量,前面是不带 var、let、const,声明的getName是全局范围内的。

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

    点二:var声明的变量和函数声明 function都会被提升,但是函数声明的提升的级别比 var 要高。 实际执行顺序如下:

    function getName() { 
      console.log(5);
    }
    var getName = function () { 
      console.log('4');
    };
    getName();  //4  
    

    点三: 操作符的优先级

    fun().getName()
    

    ()和.优先级相同,从左至右先执行 fun(),全局getName 被覆盖成输出console.log(1),并返回的this 此时代表的是window.随后相当于执行window.getName().

    点四:还是前面的构造器调用模式,在函数前面加new调用,会创建一个函数prototype 成员的新对象,同时 this 会被绑定到那个对象上。

    new fun.getName();   
    

    .操作符的优先级要高于new,实际执行为

    new (fun.getName)();
    
    new fun().getName();  
    

    而上面的应该为

    (new fun()).getName();
    

    new fun()创建一个函数prototype 成员的新对象,同时 this 会被绑定到那个对象上。在 prototype中找到 getName()方法。

    new new fun().getName()
    
    第一步:(new (new fun().getName)()
    第二步:(new ((new fun()).getName)()