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);
});
写在后面
本文是在阮老师的文章基础加上了我自己的一些想法,强烈推荐阮老师的系列文章
其他文章推荐