首先,很多人對(duì)this有兩個(gè)誤解 1.this指向的是當(dāng)前函數(shù)本身 什么意思呢,下面看一段代碼。 1 function func(){ 2 this.a ++; 3 } 4 5 var a = 0; 6 func.a = 0; 7 8 for(let i = 0; i < 5; i++){ 9 func(); 10 } 11 12 console.log(a); //->5 13 console.log(func.a);//->0 發(fā)現(xiàn)問題了,因?yàn)楹瘮?shù)本身也是一個(gè)對(duì)象,如果this指向的是當(dāng)前函數(shù)本身,那么在調(diào)用func函數(shù)5次后,func.a應(yīng)該輸出的是5,但是程序結(jié)果輸出的是0,但是全局定義的a變量輸出了結(jié)果5,顯然在這里函數(shù)中this指向的不是函數(shù)本身,而指向了全局對(duì)象. 這是為什么呢?接著往下看。
2.this指向的是當(dāng)前函數(shù)的作用域 先看一段代碼: function foo(){ var a = 0; this.bar(); } function bar(){ console.log(this.a); }
這段代碼在按照“this指向的是當(dāng)前函數(shù)的作用域”這樣的理解下,在理想的執(zhí)行狀態(tài)下是這樣子的: //foo(); function foo(){ var a = 0; console.log(a); } 因?yàn)榘凑丈衔睦斫猓瑃his指向的是當(dāng)前的函數(shù)作用域,所以這段代碼能夠執(zhí)行成功是有問題的,如果this指向的是當(dāng)前的函數(shù)作用域,foo函數(shù)的詞法作用域中并沒有一個(gè)標(biāo)識(shí)符為bar的函數(shù); 在嚴(yán)格模式下,這段代碼是無法執(zhí)行的(報(bào)錯(cuò))。
this到底是一個(gè)什么樣的機(jī)制呢?那么這里下一個(gè)定義:this的指向是取決于函數(shù)調(diào)用的方式,跟函數(shù)怎么寫沒有半毛錢關(guān)系。this的指向并沒有在編寫代碼的時(shí)候就綁定了,而發(fā)生在函數(shù)的調(diào)用時(shí)。 當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)活動(dòng)記錄(執(zhí)行上下文)。這個(gè)記錄會(huì)包含函數(shù)在哪里被調(diào)用(調(diào)用棧)、函數(shù)的調(diào)用方式、傳入的參數(shù)等信息。this就是這個(gè)記錄的一個(gè)屬性,會(huì)在函數(shù)執(zhí)行的過程中用到。
函數(shù)調(diào)用位置的追蹤: function baz(){ //當(dāng)前的調(diào)用棧是:baz //因此,當(dāng)前調(diào)用位置是全局作用域 console.log("baz"); bar(); // <-- bar的調(diào)用位置 } function bar(){ //當(dāng)前調(diào)用棧是:baz -> bar //因此,當(dāng)前調(diào)用位置在baz中 console.log("bar"); foo(); // <-- foo的調(diào)用位置 } function foo(){ //當(dāng)前的調(diào)用棧是:baz -> bar -> foo //因此,當(dāng)前的調(diào)用位置在bar中 console.log("foo"); } baz(); //<-- baz的調(diào)用位置
既然this的綁定取決于函數(shù)的調(diào)用方式,那么this存在下面這四種綁定規(guī)則:
默認(rèn)綁定:簡單地來講,默認(rèn)綁定就是單獨(dú)的,沒有任何修飾符的函數(shù)調(diào)用,就像這樣。 foo(); 這時(shí),this指向全局對(duì)象,下面給這個(gè)函數(shù)加上this來試一試。 function foo(){ console.log(this.num); } var num = 6; foo(); //->6 但是,只有在非嚴(yán)格模式下,this在默認(rèn)綁定下才能綁定到全局對(duì)象上;如果在嚴(yán)格模式下,this會(huì)綁定到undefined。 記住了,就光寫一個(gè)光禿禿的函數(shù)調(diào)用,就是默認(rèn)綁定,這時(shí)this指向的是全局對(duì)象(在非嚴(yán)格模式下)。
隱式綁定:又是簡單地來講,隱式綁定就是當(dāng)這個(gè)函數(shù)作為某個(gè)對(duì)象的方法被調(diào)用時(shí),this就會(huì)指向這個(gè)對(duì)象。
obj.foo();
考慮一下調(diào)用的位置是否有上下文對(duì)象,就是說這個(gè)函數(shù)是否被某個(gè)對(duì)象擁有或包含。 function foo(){ console.log(this.a); } var obj = { a : 2, foo : foo }; obj.foo(); // -> 2 這時(shí)foo函數(shù)的this指向obj這個(gè)對(duì)象 因?yàn)檫@時(shí)this函數(shù)指向的就是包含它的obj對(duì)象,所以此時(shí)this.a === obj.a 這就叫做隱式綁定,不過隱式綁定有一個(gè)問題,最常見的問題就是被隱式綁定的函數(shù)會(huì)丟失綁定對(duì)象,怎么回事呢,來看一下下面的代碼: function foo(){ console.log( this.a ); } var obj = { a : 2, foo : foo }; var bar = obj.foo; var a = "global"; //a是全局對(duì)象的屬性 bar(); // -> "global" 看到?jīng)]?本來foo應(yīng)該指向obj來著,現(xiàn)在指向了全局對(duì)象,是怎么回事呢?那是因?yàn)閛bj的方法foo作為一個(gè)單獨(dú)的函數(shù)被賦值給了bar,bar就成了foo函數(shù)的別名,輸出一下bar,看看里面是什么: 這里可以看到,bar的內(nèi)容是foo函數(shù)的函數(shù)體,所以這里調(diào)用bar,與foo()語句等價(jià),使用了默認(rèn)綁定,this指向全局對(duì)象,所以才會(huì)輸出"global",在編程中,要注意這個(gè)細(xì)節(jié)。
顯式綁定:顯式綁定說白了就是函數(shù)使用call方法或apply顯式地指定了this的指向。 例如: function foo(){ console.log( this.a ); } var obj = { a : 2 }; foo.call( obj ); //->2 說到了call和apply,他倆也沒啥區(qū)別,只是調(diào)用時(shí)寫法有點(diǎn)不一樣: func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2]);
call的函數(shù)參數(shù)一直寫,中間用逗號(hào)隔開,apply的函數(shù)參數(shù)以數(shù)組形式給出。
new綁定:這個(gè)就不用說了,在用new調(diào)用函數(shù)時(shí),函數(shù)內(nèi)的this指向當(dāng)前對(duì)象。 在JavaScript中,沒有明顯的標(biāo)識(shí)符什么的說明構(gòu)造函數(shù)是構(gòu)造函數(shù),實(shí)際上,Javascript不存在構(gòu)造函數(shù),只有被“構(gòu)造調(diào)用”的普通函數(shù)。new關(guān)鍵字將一個(gè)普通函數(shù)“構(gòu)造調(diào)用”,并且將這個(gè)普通函數(shù)的this與創(chuàng)建的對(duì)象相關(guān)聯(lián)。
function Person( name, sex ){ this.name = name; this.sex = sex; } // -> Person的this現(xiàn)在指向xiaoming var xiaoming = new Person("小明","男");
那么基本的this指向問題就這么簡單地說完了,總結(jié)一下,this的指向其實(shí)跟函數(shù)如何編寫沒有什么關(guān)系,只跟函數(shù)調(diào)用的方式有關(guān)系。希望這篇文章可以幫到大家更好地理解this的問題,如果有補(bǔ)充或者疑問,請(qǐng)?jiān)谠u(píng)論區(qū)留言,謝謝!
|
|