JavaScript this关键字

背书

JavaScript由于其运行期绑定的特性,this 的具体指向取决于函数的调用方式

  • 作为函数调用
  • 作为对象方法调用
  • 作为构造函数调用
  • 使用 apply 或 call 调用

但是无外乎一个原则:*this指向的永远是当前调用函数的那个对象 *。

下面围绕着这四种调用方式中this的不同含义展开叙述

函数调用

函数最常见的调用方式的方式就是全局调用,此时this就指向全局对象Global(浏览器中为window对象,nodejs中全局对象为global object)

function func1() {
    return this;
}
alert(func1() === window);//true

为了证明this就是全局对象,我们再来一个小例子

var x = 1;  
function test(){  
  this.x = 0;  
}  
test();
console.log(x);//0

上面的代码test()可以理解为window.test(),this.x=0实际上是将window.x进行了修改

严格模式中,函数的this值为undefined,因为严格模式中禁止this关键字指向全局对象。
发生直接函数调用时,函数的this指向全局对象,接下来执行this.x=xx,相当于隐式的声明了一个全局对象x,有时候会带来副作用

我们再来看一个小例子,也是一道经典的笔试题

var value = 2;
var myObj = {  
    value: 3  
};  
var add = function(a,b){  
    return a+b;  
};  
myObj.double = function(){  
  
    var helper = function(){  
        this.value = add(this.value,this.value);  
        alert(this.value)  
    };  
    helper();
};  

myObj.double();

请求,上面这道题的输出是什么的?

如果你的答案是6那么久成功掉进this陷阱了,正确的答案是4,解释一下原因:

以上代码达不到目的,因为函数调用时,helper函数的内部this被绑定到了全局变量(不信你可以打印一下这个this)。这是语言设计上的一个错误,倘若语言设计正确,那么当内部函数被调用时,this应该绑定到外部函数的this变量。这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对象的访问权。

幸运的是,有一个很容易的解决方案:如果该方法定义了一个变量并给他赋值this,那么内部函数就可以通过那个变量访问到this. 按照约定,我们可以把那个变量命名that:

var myObj = {  
    value :3  
};  
var add = function(a,b){  
    return a+b;  
};  
myObj.double = function(){  
    var that = this;//解决办法,将myObj这个正确的this缓存起来  
  
    var helper = function(){  
        that.value = add(that.value,that.value);  
        alert(that.value)  
    };  
    helper();//以函数的形式调用,this指向window全局对象  
};  
//以方法的形式调用,this指向调用者,这里是myObj对象  
myObj.double();//6  

作为对象方法的调用

此时this指向这个上级对象。

var x = 0;
function test(){  
 console.log(this.x);
}  
var o = {};  
o.x = 1;  
o.m = test;  
o.m(); // 1 

作为构造函数调用

如果一个函数前面带上一个new来调用,那么将会创建一个连接到该函数的prototype属性的新对象,同时函数的this会绑定到那个新对象上

function test(){  
  this.x = 1;  
}  
var o = new test();  
console.log(o.x); // 1  

上面调用 new test()的时候,test()中的this会指向一个空对象,这个对象的原型会指向test.prototype。

再来看一个稍微复杂点的例子

function Func1(){  
    this.a = 37;  
}  
var o = new Func1();  

console.log(o.a)//37

function Func2(){
    this.a= 37; 
     
    return{
        a:38
    };  
}   
o = new Func2();   
console.log(o.a)// ?

这个题目比较特殊,因为Func2 中return了一个新的对象,所以this绑定到了新返回的对象中,输出38

一个函数总会有一个返回值,如果没有明确指定则返回undefined

apply调用

apply()是函数对象的一个方法,允许切换函数执行的上下文环境,即 this 绑定的对象。也就是说apply调用可以改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。apply()的参数为空时,默认调用全局对象

var x = 0;  
function test(){  
  console.log(this.x);  
}  

var o={};  
o.x = 1;  
o.m = test;  
o.m(); //1,this指向o
o.m.apply(); //0 this指向window
o.m.apply(o); //1 this指向o

再看个简单的例子

function add(c, d) {
    return this.a + this.b + c + d;
}
var o = {
    a: 1,
    b: 3
};
add.apply(o, [5, 7]); // 1 + 3 + 5 + 7 = 16

形象点,就是说调用add函数的时候,add函数中有些数据无法直接获取,需要其他对象,如O的帮助,所以add临时把自身的身份证交给o,让o临时帮助add完成操作。

一些关于this的陷阱和实践

  • setTimeout

setTimeout的执行环境跟调用它的函数的执行环境是分离的,隐刺setTimeout调用的函数中的this关键字指向的是window或global对象

var name = 'somebody';
var angela = {
    name: 'angela',
    say: function () {
        alert("I'm " + this.name);
    }
};

angela.say();//I'm  angela
setTimeout(angela.say, 1000);  //I'm  somebody

还有,有些DOM的事件回调函数中,可能涉及到setTimeout(),例如:

$('#myElement').click(function() {

    setTimeout(function() {

        // 这个this指向的是settimeout函数内部,而非之前的html元素

         $(this).addClass('aNewClass');

  }, 1000);

});

应对之策还是上文提到的聪明的that

$('#myElement').click(function() {
     var that = this;   //设置一个变量,指向这个需要的this

   setTimeout(function() {

        // 这个this指向的是settimeout函数内部,而非之前的html元素

     $(that).addClass('aNewClass');

    }, 1000);

});

写在后面

本文是在阮老师的文章基础加上了我自己的一些想法,强烈推荐阮老师的系列文章

其他文章推荐

    原文作者:艾伦先生
    原文地址: https://www.jianshu.com/p/c33292f1683c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞