摘要:以避免全局變量的污染。如果是使用關(guān)鍵字來(lái)調(diào)用函數(shù)的,則會(huì)綁定到新創(chuàng)建的那個(gè)對(duì)象上如果是在事件函數(shù)內(nèi),則會(huì)綁定到觸發(fā)事件的那個(gè)元素上。
Javascript 當(dāng)中的 this 與其他語(yǔ)言是完全不同的機(jī)制,很有可能會(huì)讓一些編寫其他語(yǔ)言的工程師迷惑。
1. 誤以為 this 指向函數(shù)自身根據(jù) this 的英語(yǔ)語(yǔ)法,很容易將函數(shù)中出現(xiàn)的 this 理解為函數(shù)自身。在 javascript 當(dāng)中函數(shù)作為一等公民,確實(shí)可以在調(diào)用的時(shí)候?qū)傩灾荡鎯?chǔ)起來(lái)。但是如果使用方法不對(duì),就會(huì)發(fā)生與實(shí)際預(yù)期不一致的情況。具體情況,請(qǐng)看下面代碼
function fn(num){ this.count++; } fn.count = 0; for(var i=0;i<3;i++){ fn(i); } console.log(fn.count); // 0
如果 fn 函數(shù)里面的 this 指向自身函數(shù),那么 count 屬性的屬性值就應(yīng)該產(chǎn)生變化,但實(shí)際上卻是紋絲不動(dòng)。對(duì)于這個(gè)問(wèn)題,有些人會(huì)利用作用域來(lái)解決,比如這么寫
var data = { count:0 }; function fn(num){ data.count++; } for(var i=0;i<3;i++){ fn(i); } console.log(data.count); //3
又或者更直接的這么寫
function fn(num){ fn.count++; } fn.count = 0; for(var i=0;i<3;i++){ fn(i); } console.log(fn.count);//3
雖然這兩種方式都輸出了正確的結(jié)果,但是卻避開(kāi)了 this 到底綁定在哪里的問(wèn)題。如果對(duì)一個(gè)事物的工作原理不清晰,就往往會(huì)產(chǎn)生頭痛治頭,腳痛治腳的問(wèn)題,從而導(dǎo)致代碼變得的丑陋,而且維護(hù)性也會(huì)變得很差。
2. this神奇的綁定規(guī)則 2.1 默認(rèn)綁定規(guī)則第一種是最常見(jiàn)的 this 的綁定,看一下下面的代碼
function fn(){ console.log(window === this); //瀏覽器環(huán)境 } fn(); //true
函數(shù) fn 是直接在全局作用域下調(diào)用的,沒(méi)有帶其他任何修飾,這種情況下,函數(shù)調(diào)用的時(shí)候使用了 this 的默認(rèn)綁定,指向了全局對(duì)象。
這樣就清楚了第一個(gè)例子中的 this 指向, fn 函數(shù)中的 this 指向了全局變量,所以 this.count++ 相當(dāng)于 window.count++(瀏覽器環(huán)境下),當(dāng)然不會(huì)對(duì) fn 函數(shù)的count屬性產(chǎn)生影響。
有一點(diǎn)要說(shuō)明的是,上面種情況只能在非嚴(yán)格模式(strict mode)下才能發(fā)生,在嚴(yán)格模式下,會(huì)將 this 默認(rèn)綁定為 undefined。以避免全局變量的污染。
2.2 隱式綁定規(guī)則如果函數(shù)在以對(duì)象為上下文進(jìn)行調(diào)用,那么 this 的綁定就會(huì)產(chǎn)生變化。this 會(huì)綁定到調(diào)用這個(gè)函數(shù)的對(duì)象,查看下面代碼:
var obj = { a:1, fn:function(){ console.log(this.a); } } obj.fn(); //1
即使函數(shù)聲明不在對(duì)象當(dāng)中,this 指向仍會(huì)產(chǎn)生變化
function fn(){ console.log(this.a); } var obj = { a:1, fn:fn } obj.fn(); //1
由此可見(jiàn),this 的綁定,不與函數(shù)定義的位置有關(guān),而是與調(diào)用者和調(diào)用方式有關(guān)。
在隱式的綁定規(guī)則下,有一些特殊的地方,需要注意。
2.2.1 多層對(duì)象調(diào)用 this 的指向function fn(){ console.log(this.a); } var obj3 = { a:3, fn:fn } var obj2 = { a:2, obj3:obj3 } var obj = { a:1, obj2:obj2 } obj.obj2.obj3.fn(); //3
在多層對(duì)象引用下,this 指向的是調(diào)用的函數(shù)的那個(gè)對(duì)象。
2.2.2 隱式賦值可能存在丟失現(xiàn)象查看下面代碼
function fn(){ console.log(this); } var obj = { fn:fn } var fun = obj.fn; fun(); //window
雖然 fn 引用了 obj.fun ,但是函數(shù)的調(diào)用方式,仍是不帶任何修飾的,所以 this 還是綁定在了 window 上。
還有一種情況,容易讓大家忽略,那就是傳參的時(shí)候,其實(shí)會(huì)進(jìn)行隱式賦值。
function fn(){ console.log(this); } function doFn(fn){ fn(); } var obj = { fn:fn } doFn(obj.fn); //window
隱式綁定 this 不是一種很推薦的方式,因?yàn)楹苡锌赡芫桶l(fā)生丟失的情況,如果業(yè)務(wù)當(dāng)中對(duì) this 的綁定有要求,建議還是使用顯示綁定的方式。
2.3 顯式綁定規(guī)則顯示綁定就是利用函數(shù)原型上的 apply 與 call 方法來(lái)對(duì) this 進(jìn)行綁定。用法就是把想要綁定的對(duì)象作為第一個(gè)參數(shù)傳進(jìn)去。
function fn(){ console.log(this); } var obj = {}; fn.call(obj); //{}
有些時(shí)候會(huì)想將函數(shù)的 this 綁定在某個(gè)對(duì)象上,但是不需要立即調(diào)用,這樣的話,直接利用 call 或者 apply 是無(wú)法做的。
function fn(){ console.log(this); } function bind(fn){ fn(); } var obj = { fn:fn } bind.call(obj,fn); //window
上面這個(gè)例子,看似好像可以,但實(shí)際上是 bind 函數(shù)的 this 綁定到了 obj 這個(gè)對(duì)象,但是 fn 仍然是沒(méi)有任何修飾的調(diào)用,所以 fn 仍然是默認(rèn)的綁定方式。
function fn(){ console.log(this); } function bind(fn,obj){ return function(){ fn.apply(obj,arguments); } } var obj = { fn:fn } var fun = bind(fn,obj); fun(); //obj
這樣調(diào)用,就可以將靈活多變的 this ,牢牢的控制住了,因?yàn)?fn 的調(diào)用方式為 apply 調(diào)用。所以,this 就被綁定在傳入的 obj 對(duì)象上,在 ES5 當(dāng)中,函數(shù)的原型方法上多了一個(gè) bind。效果與上面的函數(shù)基本一致,具體用法限于篇幅就不多說(shuō)了。
2.4 new 綁定new 是一個(gè)被很多人誤解的一個(gè)關(guān)鍵字,但實(shí)際上 javascript 的 new 與傳統(tǒng)面向?qū)ο蟮恼Z(yǔ)言完全不同。
個(gè)人把 new 理解為一種特殊的函數(shù)調(diào)用,當(dāng)使用 new 關(guān)鍵字來(lái)調(diào)用函數(shù)的時(shí)候,會(huì)執(zhí)行下面操作,
創(chuàng)建一個(gè)全新的對(duì)象
將空對(duì)象的 __proto__ 指向構(gòu)造函數(shù)的 prototype
將新對(duì)象的 this 綁定到調(diào)用的函數(shù)上
如果函數(shù)返回值為基本類型或者為 this又或者不返回任何值,那么將會(huì)返回這個(gè)創(chuàng)建的新對(duì)象,如果返回了一個(gè)對(duì)象,那么則會(huì)返回這個(gè)對(duì)象,而不會(huì)返回創(chuàng)建的新對(duì)象。
function fn(a){ this.a = a; } fn.prototype.hi = function(){ console.log("hi") } var obj = new fn(2); console.log(obj);
function fn(a){ this.a = a; return {}; } var obj = new fn(2); console.log(obj); //{}2.5 特殊的傳參
null 和 undefined 也是可以作為 this 的綁定對(duì)象的,但是實(shí)際上應(yīng)用的是默認(rèn)的綁定。
但是這種傳參的實(shí)際效用是什么呢?
常見(jiàn)的用法是將一個(gè)數(shù)組展開(kāi),作為參數(shù)傳入?yún)?shù)。比如
function fn(a,b){ console.log("a:",a,"b:",b); } fn.apply(null,[1,2]); // a: 1 b: 2
但是這種用法會(huì)有一個(gè)坑,那就是如果函數(shù)存在了 this ,那么就會(huì)應(yīng)用默認(rèn)的綁定規(guī)則,將 this 綁定在全局對(duì)象上,發(fā)生于預(yù)期不一致的情況。為了代碼更加穩(wěn)健,可以使創(chuàng)建一個(gè)比空對(duì)象更空的對(duì)象。
var obj = Object.create(null); console.log(obj.__proto__); //undefined var obj2 = {} console.log(obj2.__proto__); //Object {}
Object原型上有一個(gè) create 方法,這個(gè)方法會(huì)創(chuàng)建一個(gè)對(duì)象,然后將對(duì)象的原型指向傳入的參數(shù),所以傳入 null 的話,產(chǎn)生一個(gè)沒(méi)有 prototype 的對(duì)象,所以會(huì)比空對(duì)象更加"空"。
所以傳入這個(gè)對(duì)象,會(huì)比傳入 null 更加安全。
var obj = Object.create(null); fn.apply(obj,[1,2]);2.6 根據(jù)作用域來(lái)決定 this 的綁定
在 ES6 當(dāng)中,出現(xiàn)了一個(gè)新的函數(shù)類型,箭頭函數(shù)。
如果使用箭頭函數(shù),那么就不會(huì)使用上面提到的四種 this 綁定方式,而是根據(jù)作用域來(lái)決定
比較常見(jiàn)的是用于事件函數(shù)和定時(shí)器的情況。
下面是比較常見(jiàn)的傳統(tǒng) this 寫法
function fn(){ var _this = this; setTimeout(function(){ console.log(_this.a); },100) } var obj = { a:2 } fn.call(obj); //2
如果使用箭頭函數(shù)則可以這么寫
function fn(){ setTimeout(()=>{ //this 來(lái)源于 fn 函數(shù)的作用域 console.log(this.a); },100) } var obj = { a:2 } fn.call(obj); //22.7 事件函數(shù)當(dāng)中 this 的綁定機(jī)制
如果是在事件函數(shù)當(dāng)中,this 的綁定是指向觸發(fā)事件的 DOM 元素的,
$("body")[0].addEventListener("click",function(){ console.log(this); },false);
點(diǎn)擊 body 元素之后,控制臺(tái)則會(huì)顯示 body 元素
3. 小結(jié)如果想判斷一個(gè)函數(shù)的 this 綁定在哪里,首先是找到函數(shù)的調(diào)用位置,之后是按照規(guī)則來(lái)判斷。
如果函數(shù)調(diào)用時(shí)沒(méi)有任何修飾條件,那么在嚴(yán)格模式下則會(huì)綁定到 undefined ,非嚴(yán)格模式下會(huì)綁定到全局。
如果是用對(duì)象做上下文,來(lái)對(duì)函數(shù)進(jìn)行調(diào)用,那么則會(huì)綁定到調(diào)用的這個(gè)對(duì)象上。
如果是用 call 或者 apply 方法來(lái)進(jìn)行調(diào)用的,則會(huì)綁定到第一個(gè)傳入?yún)?shù)上。
如果是使用 new 關(guān)鍵字來(lái)調(diào)用函數(shù)的,則會(huì)綁定到新創(chuàng)建的那個(gè)對(duì)象上.
如果是在事件函數(shù)內(nèi),則會(huì)綁定到觸發(fā)事件的那個(gè)DOM元素上。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/91614.html
摘要:在內(nèi)部,理所當(dāng)然能訪問(wèn)到局部變量,但當(dāng)作為的返回值賦給外的全局變量時(shí),神奇的事情發(fā)生了在全局作用域中訪問(wèn)到了,這就是閉包。而閉包最神奇的地方就是能在一個(gè)函數(shù)外訪問(wèn)函數(shù)中的局部變量,把這些變量用閉包的形式放在函數(shù)中便能避免污染。 一、閉包是什么? 《JavaScript高級(jí)程序設(shè)計(jì)》中寫道:閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù),如果用下定義的觀點(diǎn)看,這句話就是說(shuō)閉包是函數(shù),我...
摘要:之圖片裁剪壓縮上傳圖片裁剪壓縮上傳預(yù)覽是常見(jiàn)功能幸運(yùn)的是我們有這款利器上傳選擇圖片文件裁剪挺好用的圖片處理插件另外還有較好的插件有待研究 Croppie.js之圖片裁剪壓縮上傳 h5圖片裁剪, 壓縮, 上傳, 預(yù)覽是常見(jiàn)功能, 幸運(yùn)的是我們有cropp.js這款利器. 1. style .actions button, .actions a.btn { ...
摘要:的關(guān)鍵字總是讓人捉摸不透,關(guān)鍵字代表函數(shù)運(yùn)行時(shí),自動(dòng)生成的一個(gè)內(nèi)部對(duì)象,只能在函數(shù)內(nèi)部使用,因?yàn)楹瘮?shù)的調(diào)用場(chǎng)景不同,的指向也不同。其實(shí)只要理解語(yǔ)言的特性就很好理解。個(gè)人對(duì)中的關(guān)鍵字的理解如上,如有不正,望指正,謝謝。 javascript的this關(guān)鍵字總是讓人捉摸不透,this關(guān)鍵字代表函數(shù)運(yùn)行時(shí),自動(dòng)生成的一個(gè)內(nèi)部對(duì)象,只能在函數(shù)內(nèi)部使用,因?yàn)楹瘮?shù)的調(diào)用場(chǎng)景不同,this的指向也不...
這幾天看koa源碼的時(shí)候,經(jīng)常看到if(~notfound.indexOf(err.code)){ doSomeing... }這種在一個(gè)表達(dá)式前面加~號(hào)的,今天就來(lái)扒一扒這已黑魔法。 ~ 取反操作符 不熟悉原碼,反碼,補(bǔ)碼的小伙伴可以先看一下這篇文章原碼、反碼、補(bǔ)碼,計(jì)算機(jī)中負(fù)數(shù)的表示 在javascript中,假設(shè)有一個(gè)變量var a = 1, 那么~a + a = -1, 也就是說(shuō)現(xiàn)在~...
摘要:輔助方法這個(gè)方法遞歸遍歷的子節(jié)點(diǎn),將節(jié)點(diǎn)交由回調(diào)函數(shù)處理。對(duì)集合進(jìn)行遍歷,調(diào)用方法,如果為函數(shù),則將回調(diào)函數(shù)返回的結(jié)果作為參數(shù)傳給否則,如果為,則將也即包裹元素的副本傳給,否則直接將傳給。 這篇依然是跟 dom 相關(guān)的方法,側(cè)重點(diǎn)是操作 dom 的方法。 讀Zepto源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的源碼為 zepto...
閱讀 2460·2021-11-23 09:51
閱讀 2061·2021-10-14 09:43
閱讀 2846·2021-09-27 13:35
閱讀 1225·2021-09-22 15:54
閱讀 2607·2021-09-13 10:36
閱讀 3959·2019-08-30 15:56
閱讀 3482·2019-08-30 14:09
閱讀 1800·2019-08-30 12:57