目錄
this的理解
this的原理
事件綁定中的this
行內綁定
動態綁定
window定時器中的this
相信小伙伴們看完這篇文章,對于this的對象可以有一個很大的提升!
this的理解
對于this指針,可以先記住以下兩點:
- this永遠指向一個對象
- this的指向完全取決于函數調用的位置
對于第一點,不管在什么地方使用this,它必然會指向某個對象。
由于在JavaScript中,一切皆對象,運行環境也是對象,所以函數都是在某個對象下運行,而this就是“函數運行時所在的對象(環境)”
但因為JavaScript支持運行環境動態切換,即:this的指向是動態的,很難確定this到底指向哪個對象,這是最讓我們感到困惑的地方。
this的原理
function fun()
{console.log(this.s);
}var obj = {s:'1',f:fun
}var s = '2';obj.f(); // 1
fun(); // 2
在JS中,數組、函數、對象都是引用類型,在參數傳遞時也就是引用傳遞(傳遞內存地址)。
在上面代碼中,obj有兩個屬性,但是f屬性存儲了一個函數名(函數的內存地址),于是在調用f方法時,通過f的值找到對應的內存地址就調用到了對應的函數。
下圖是obj在內存中的表示:
在調動時就變成了下面這個樣子:
下面,我們一條一條解釋,相信看完這些解釋,你會恍然大悟!
關于obj.f()的調用:
- 調用obj的f方法時,js引擎先找到obj這個對象,隨后在obj內存地址中找到f方法的地址,再調用f方法地址中的值(在這里這個值就是functiuon fun()這個函數的內存地址),于是成功調用這個函數。但由于該函數是經過:“js引擎 -> obj對象 ->f()”這么一個順序調用的,因此此時fun()的this指向它的運行環境,即obj對象
關于fun()的調用:
- 調用fun()時,js引擎要在整個<script>標簽下搜尋“函數名為fun的函數”,因為是在整個<script>標簽搜尋,故搜索到fun()函數時,運行環境固然在window對象下,該函數經過:“js引擎 ->?整個<script>環境(window對象) -> fun()”這個一個順序調用的
var A = {name:'張三',f:function(){console.log('姓名:'+this.name);}
};var B = {name:"李四"
};B.f = A.f;
B.f(); // 姓名:李四
A.f(); // 姓名:張三
上面代碼,仍然可以用我上面所說的來解釋:
由于“函數”的傳遞是“引用傳遞”(傳遞內存地址),即此時B.f的值就是匿名函數的內存地址,故B.f()和A.f()實際上調用的是“同一個內存地址的同一個函數”
- 調用B.f()時,js引擎根據B.f的值,找到對應的匿名函數,此時匿名函數的執行環境在B對象之下,故打印“李四”
- 調用A.f()時,js引擎根據A.f的值,找到對應的匿名函數,此時匿名函數的執行環境在A對象之下,故打印“張三”
function foo()
{console.log(this.a);
}var obj2 = {a:2,f:foo
};var obj1 = {a:1,o:obj2
};obj1.o.f(); // 2
對于上面代碼,仍仍仍然可以使用剛才所說的來解釋:
- 調用obj1.o.f()時,js引擎根據obj1.o的值(obj2的內存地址)先找到obj2對象,再根據f的值(foo的內存地址)找到foo,最后執行foo。此時整個調用關系為:“js引擎 -> obj1對象 -> obj2對象 -> foo()”,故foo運行在obj2對象之下,因此打印2
事件綁定中的this
事件綁定有三種方式:行內綁定、動態綁定、事件監聽。
行內綁定
<input type="button" value="按鈕" onclick="clickFun()">
<script>function clickFun(){this // window}
</script>
?
<input type="button" value="按鈕" onclick="this">
<!-- 本節點對象 -->
節點事件屬性的值可以是:“可執行的JS代碼段”或“函數名(函數調用)”
對于οnclick="clickFun()":
- JS引擎發現onclick的值是函數調用,因此轉去<script>標簽下(window環境)尋找函數名為“clickFun”的函數,找到clickFun函數后成功執行。此時執行順序為:“JS引擎 -> <script>標簽 -> clickFun()”
對于οnclick="this":
- JS引擎發現onclick的值是一段可執行的js代碼,因此JS引擎直接在該DOM節點下執行該代碼,對應的this就指向該節點了
動態綁定
<input type="button" value="按鈕" id="btn">
<script>var btn = document.getElementById('btn');btn.onclick = function(){this ; // this指向btn節點對象}
</script>
這里的操作,本質上是直接對btn節點對象的onclick屬性附一個值。
當執行時,JS引擎發現onclick屬性的值是一個匿名函數,因此直接就執行該匿名函數。
又因為該匿名函數的是在btn對象之下執行的,因此this執行btn節點對象
window定時器中的this
var obj = {fun:function(){this ;}
}
?
setInterval(obj.fun,1000); // window對象
setInterval('obj.fun()',1000); // obj對象
對于obj.fun:
注意:在這里有一個很重要的點,這里傳入的是obj.fun,而不是obj.fun(),傳遞obj.fun是傳入的fun這個方法函數(可以理解為傳入fun的值,而fun的值是一個匿名函數,即匿名函數的地址)。而obj.fun是直接調用obj.fun()方法
- 在經過1s后,JS引擎發現setInerval的第一個參數值是一個函數地址,因此轉而去執行該函數。但由于setInterval是一個異步函數,在等待時,會將該代碼段掛載到全局對象(window對象)下的一個棧中,因此執行過程為:“JS引擎 ->?window對象下的棧 ->?匿名函數”,故this指向window對象
對于'obj.fun()':
- 在經過1s后,JS引擎發現setInterval的第一個參數值是一段可執行的JS代碼,轉而執行這個代碼,這個代碼是調用obj.fun()對象,因此JS引擎去<script>標簽(window)下找到obj對象,在找到fun方法,最后執行匿名函數。因此執行過程為:“JS引擎 ->?window對象 -> obj對象 ->?匿名函數”,故this指向obj對象