詳解JavaScript中的this
JavaScript中的this總是讓人迷惑,應(yīng)該是js眾所周知的坑之一。個人也覺得js中的this不是一個好的設(shè)計,由于this晚綁定的特性,它可以是全局對象,當(dāng)前對象,或者…有人甚至因為坑大而不用this。
其實如果完全掌握了this的工作原理,自然就不會走進(jìn)這些坑。來看下以下這些情況中的this分別會指向什么:
1.全局代碼中的this
- alert(this)//window
全局范圍內(nèi)的this將會指向全局對象,在瀏覽器中即使window。
2.作為單純的函數(shù)調(diào)用
- function fooCoder(x) {
- this.x = x;
- }
- fooCoder(2);
- alert(x);// 全局變量x值為2
這里this指向了全局對象,即window。在嚴(yán)格模式中,則是undefined。
3.作為對象的方法調(diào)用
- var name = "clever coder";
- var person = {
- name : "foocoder",
- hello : function(sth){
- console.log(this.name + " says " + sth);
- }
- }
- person.hello("hello world");
輸出 foocoder says hello world。this指向person對象,即當(dāng)前對象。
4.作為構(gòu)造函數(shù)
- new FooCoder();
函數(shù)內(nèi)部的this指向新創(chuàng)建的對象。
5.內(nèi)部函數(shù)
- var name = "clever coder";
- var person = {
- name : "foocoder",
- hello : function(sth){
- var sayhello = function(sth) {
- console.log(this.name + " says " + sth);
- };
- sayhello(sth);
- }
- }
- person.hello("hello world");//clever coder says hello world
在內(nèi)部函數(shù)中,this沒有按預(yù)想的綁定到外層函數(shù)對象上,而是綁定到了全局對象。這里普遍被認(rèn)為是JavaScript語言的設(shè)計錯誤,因為沒有人想讓內(nèi)部函數(shù)中的this指向全局對象。一般的處理方式是將this作為變量保存下來,一般約定為that或者self:
- var name = "clever coder";
- var person = {
- name : "foocoder",
- hello : function(sth){
- var that = this;
- var sayhello = function(sth) {
- console.log(that.name + " says " + sth);
- };
- sayhello(sth);
- }
- }
- person.hello("hello world");//foocoder says hello world
6.使用call和apply設(shè)置this
- person.hello.call(person, "world");
apply和call類似,只是后面的參數(shù)是通過一個數(shù)組傳入,而不是分開傳入。兩者的方法定義:
- call( thisArg [,arg1,arg2,… ] ); // 參數(shù)列表,arg1,arg2,...
- apply(thisArg [,argArray] ); // 參數(shù)數(shù)組,argArray
兩者都是將某個函數(shù)綁定到某個具體對象上使用,自然此時的this會被顯式的設(shè)置為第一個參數(shù)。
簡單地總結(jié)
簡單地總結(jié)以上幾點,可以發(fā)現(xiàn),其實只有第六點是讓人疑惑的。
其實就可以總結(jié)為以下幾點:
1.當(dāng)函數(shù)作為對象的方法調(diào)用時,this指向該對象。
2.當(dāng)函數(shù)作為淡出函數(shù)調(diào)用時,this指向全局對象(嚴(yán)格模式時,為undefined)
3.構(gòu)造函數(shù)中的this指向新創(chuàng)建的對象
4.嵌套函數(shù)中的this不會繼承上層函數(shù)的this,如果需要,可以用一個變量保存上層函數(shù)的this。
再總結(jié)的簡單點,如果在函數(shù)中使用了this,只有在該函數(shù)直接被某對象調(diào)用時,該this才指向該對象。
- obj.foocoder();
- foocoder.call(obj, ...);
- foocoder.apply(obj, …);
更進(jìn)一步
我們可能經(jīng)常會寫這樣的代碼:
- $("#some-ele").click = obj.handler;
如果在handler中用了this,this會綁定在obj上么?顯然不是,賦值以后,函數(shù)是在回調(diào)中執(zhí)行的,this會綁定到$(“#some-div”)元素上。這就需要理解函數(shù)的執(zhí)行環(huán)境。本文不打算長篇贅述函數(shù)的執(zhí)行環(huán)境,可以參考《javascript高級程序設(shè)計》中對執(zhí)行環(huán)境和作用域鏈的相關(guān)介紹。這里要指出的時,理解js函數(shù)的執(zhí)行環(huán)境,會更好地理解this。
那我們?nèi)绾文芙鉀Q回調(diào)函數(shù)綁定的問題?ES5中引入了一個新的方法,bind():
- fun.bind(thisArg[, arg1[, arg2[, ...]]])
- thisArg
- 當(dāng)綁定函數(shù)被調(diào)用時,該參數(shù)會作為原函數(shù)運行時的this指向.當(dāng)使用new 操作符調(diào)用綁定函數(shù)時,該參數(shù)無效.
- arg1, arg2, ...
- 當(dāng)綁定函數(shù)被調(diào)用時,這些參數(shù)加上綁定函數(shù)本身的參數(shù)會按照順序作為原函數(shù)運行時的參數(shù).
該方法創(chuàng)建一個新函數(shù),稱為綁定函數(shù),綁定函數(shù)會以創(chuàng)建它時傳入bind方法的第一個參數(shù)作為this,傳入bind方法的第二個以及以后的參數(shù)加上綁定函數(shù)運行時本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來調(diào)用原函數(shù).
顯然bind方法可以很好地解決上述問題。
- $("#some-ele").click(person.hello.bind(person));
- //相應(yīng)元素被點擊時,輸出foocoder says hello world
其實該方法也很容易模擬,我們看下Prototype.js中bind方法的源碼:
- Function.prototype.bind = function(){
- var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
- return function(){
- return fn.apply(object,
- args.concat(Array.prototype.slice.call(arguments)));
- };
- };
明白了么?
相信看完全文以后,this不再是坑~
原文鏈接:http://foocoder.com/blog/xiang-jie-javascriptzhong-de-this.html/