Javascript:this用法
#Javascript:this用法整理
pingan 于 星期三, 18/12/2013 - 22:32 提交
常用Javascript的人都知道,[this这个关键字在一个函式内究竟指向谁]的这个问题很令人头大,本人在这裡整理了一下Javascript中this的指向的五种不同情况,其中前三种属于基本的情况,而后两种情况可基于前三种情况的方式来进行思考。
1.this指向于调用该函式之物件
如果你有学过C/C++,你可能会记得一个物件内的成员函式裡的this指的即是该成员函式所在之物件,但在Javascript裡则有那麽些许不同,Javascript裡的this看的是究竟是谁调用该函式,而不是看该函式被定义在哪个物件内,这个大原则抓到了,基本上就已经可以探知this的奥秘了。底下写一下这种情况的公式与范例:
公式
1 物件.函式(); //函式内的this指向该物件
范例
var obj = {
x: 20,
f: function(){ console.log(this.x); }
};
obj.f(); //由于调用f函式时,点前面物件为obj,故f内的this指向obj,则输出为20。
obj.innerobj = {
x: 30,
f: function(){ console.log(this.x); }
}
obj.innerobj.f(); //由于调用f函式时,点前面物件为obj.innerobj,故f内的this指向obj.innerobj,则输出为30。
2.this指向全域物件(浏览器:window物件、node.js:GLOBAL物件)
如果调用函式的前方并未有物件,则函式内this就指向全域物件。在浏览器内全域物件为window物件,而在node.js中全域物件则为GLOBAL物件。底下一样写一下这种情况的公式与范例:
公式
函式(); //函式内的this指向全域物件
范例
var x = 10;
var f = function(){
console.log(this.x);
};
f(); //由于调用f函式时,前方并未有[物件.]的形式,故f内的this指向全域物件,则输出全域变数的x(10)。
例外:在使用node.js时,若使用node file.js这样的方式执行js档,并不会让宣告的全域变数挂在全域物件上(意指会利用function将code整个包起来执行),故输出应为undefined。
前两种情况常见误导范例
范例一、物件之成员函式内有函式(感谢NSLin在实务读书会上的范例Code)
example1.js
var x = 10;
var obj = {
x: 20,
f: function(){
console.log(this.x);
var foo = function(){ console.log(this.x); }
foo(); // (2)
}
};
obj.f(); // (1)
这个范例会输出多少呢?别忘记大原则,在Javascript裡的this看的是究竟是谁调用该函式,故并不会输出20 20,而是输出20 10,为什麽呢?因为(1)obj.f()调用时,f前面物件为obj,故f内的this指向obj。但因为调用f内的(2)foo函式时是用foo(),调用的前方并未有物件,故foo内的this指向全域物件,所以输出会是全域变数的x的值。
若要让foo内使用obj.x的值,解法如下:
example1.js
var x = 10;
var obj = {
x: 20,
f: function(){
console.log(this.x);
var that = this; //使用that保留在这个函式内的this
var foo = function(){ console.log(that.x); } //使用that取得obj
foo();
}
};
obj.f();
范例二、借用函式
example2.js
var x = 10;
var obj = {
x: 20,
f: function(){ console.log(this.x); }
};
obj.f(); // (1)
var fOut = obj.f;
fOut(); //(2)
var obj2 = {
x: 30,
f: obj.f
}
obj2.f(); // (3)
范例中三次调用之函式的this所指向的物件为何,不知道各位能不能看得出来。虽然用的是同一个函式,但是因为调用的不同,故this所指向的物件就不同。(1)obj.f()的f所指向的是obj,这比较没有问题,输出的会是20;而(2)fOut()裡的this,则是因为调用时前方无物件,则this所指的是全域物件,输出的会是10;最后(3)obj2.f()则是obj2去呼叫f,故f内的this指向的是obj2,输出的会是30。
3.this指向利用call或apply所指派给this的物件
有个方法可以更改前两种叙述中this指派的值,就是利用call与apply。call与apply都是呼叫该函式并让该函式的this指向给予call或apply的第一个参数。至于call和apply的差别则是在于其后面给予被调用之函式的参数放入的方法不同,一个是直接摊平放在第二个以后的参数;一个是直接放入一个裡面放要给予之参数的阵列。底下一样看一下公式和范例:
公式
(A物件.)函式.call(B物件,参数1,参数2,参数3, ......); //函式的this指向B物件(若B物件为null,则指向全域物件)
(A物件.)函式.apply(B物件,[参数1,参数2,参数3, ......]); //函式的this指向B物件(若B物件为null,则指向全域物件)
范例
var obj = {
x: 20;
f: function(){ console.log(this.x); }
};
var obj2 = {
x: 30;
};
obj1.f.call(obj2); //利用call指派f的this为指向obj2,故输出为30
4.this指向new所产生之新物件
若将函式当作建构式(constructor)来用,则内部的this则指向于new所产生之新物件。
公式
new 建构式(); //建构式内之this指向new所产生之新物件
范例
function Monster(){
this.hp = 100;
};
var monster = new Monster(); //Monster的this指向new出来之新物件并回传回来,new的写法就类似于下面的写法。
var monster = (function(){
var _new = {
constructor: Monster,
__proto__: Monster.prototype
}; //在IE内可能不相似
_new.constructor(); //这也是为何说可以利用前三种情况来变化的原因,constructor呼叫时,this指向的即是_new这个物件。
return _new;
})();
5.callback函式内的this会指向于调用放入该callback的函式之this所指向之物件
先想想在jQuery中,我们若要让#button这个元素被click的时候,内容改为“Clicked”这样的字串,该如何写呢?
clicked.js
$('#button').click(function(){
this.html("Clicked");
})
此时这个this居然会指向$(‘#button’)这个物件,感觉很自然,但实际想想会觉得很神奇。假设你写一个function,它可以传入一个function,并在里面呼叫传入的function,你该怎么写呢?
function-to-function.js
var f = function(innerf){
//前面的处理
innerf(arg1, arg2, arg3, ......);
//后面的处理
}
但如果这样写的话,innerf里的this根据前述规则就应该是全域物件了!那为什么常常别人实作的callback函式可让this指向于调用放入该callback的函式之this所指向之物件呢?这表示大家实际上会遵守一个规则,会将自己的this传给callback当作它的this来用!这也是为什麽我说这个情况其实也是前三种情况的变化而已了!所以上面的code应该改成如下的形式会比较好:
function-to-function-improved.js
var f = function(innerf){
//前面的处理
innerf.call(this, arg1, arg2, arg3, ......);
//或是innerf.apply(this, [arg1, arg2, arg3, ......])
//后面的处理
}