對於初學者來說,關鍵字this總是讓人捉摸不透,甚是複雜,但是其實,只要你摸透了其中關鍵所在,便也無甚煩惱了;
其實this在函式中指向的是什麼,都是圍繞兩個原則,就是誰引用他他就指向誰與就近原則;
一般this的指向性問題分為下面幾類,我們來看具體例項
一、普通函式中的this關鍵字
//示例一
function f1(){
this.a = 5;
}
f1();
console.log(window.a) // 5;
//示例二
var b = 10;
function f2(){
this.b = 20;
}
f2();
console.log(window.b) //20;
複製程式碼
上面兩個例子都是普通函式,他們指向的都是window,所以我們的結論是:
普通函式中this的指向都是window
可能你會問,上面不是說,this指向的都是呼叫他的那個物件嗎?那函式自執行,this的指向不應該是函式本身嗎?
其實啊,函式自己呼叫自己的時候,就相當於是window在呼叫他,像示例一種,f1()相當於是window.f1();
既然是window呼叫他,那麼this的指向就是window了
二、物件中的this關鍵字
同樣我們先看下面例子:
//示例一
var obj1 = {
a : 5,
f1 : function(){
console.log(this.a) // 5
console.log(this === obj1) //true
}
}
obj1.f1();
複製程式碼
從給出的例子我們可以看出來,當函式作為物件的屬性來呼叫時,this的指向便指向了物件自身,這就是說既然你函式都是我物件的一個屬性了,那麼你裡面的this當然指向我,這種指向是一種隱式繫結this的現象,但是如果我們解除了這種隱式繫結呢?
接下來我們將例子變一下:
//變異示例一
var obj2 = {
a : 5,
f2 : function(){
console.log(this.a); //undefined
console.log(this === obj2) //false
}
}
var init = obj2.f2 //重點看這裡
init();
複製程式碼
還是那句話,誰呼叫指向誰,從變異示例一可以看出,obj.f2這裡並沒有呼叫函式f2,他只是將函式賦值給了變數init而已,所以這裡呼叫函式f2的不再是物件obj2了,因為已經將f2的隱式繫結給解除了
所以必然this指向的不在是obj2,那麼obj2中的屬性自然是呼叫不到了,this也不可能等於obj2了,那麼這裡的this究竟指向了誰呢?
一起看init()這裡,在這裡呼叫了函式,變數的是全域性變數,他的頂級物件在這裡是window,所以init() === window.init()是一樣的,所以最後this指向的是window;
這裡我們要注意,一定要看清楚函式是在哪裡呼叫的,這樣我們才能去判斷this指向的是什麼
再將上面的示例變一下:
var obj3 = {
a : 5,
obj4 : {
a : 10,
f3 : function(){
console.log(this.a) //10
}
}
}
obj3.obj4.f3();
複製程式碼
這裡輸出的為什麼是10呢?按最終呼叫的應該是物件obj3,那麼他下面的a不應該是5嗎?
還記得我們上面說過一個詞叫就近原則嗎?雖然最終呼叫的是obj3,但是在遵循就近原則的情況下,呼叫他的是obj4,所以this指向的是obj4,那麼必然列印出來的就是10了
最後我們附加一個示例,這也是之前有個人有些疑問的地方,我覺得值得說一下
var obj5 = {
a : 5,
f5 : function(){
console.log(this.a) // 5
console.log(this === obj5) //true
}
}
var init = obj5;
init.f5();
複製程式碼
在這裡直接將物件obj5賦值給變數init,然後由變數去呼叫f5,實則和obj5去呼叫f5是一樣的,this指向的還是物件obj5,所以在這裡一定要注意,千萬不要認為是window呼叫函式f5
三、call中的this關鍵字
call() 、bind()、apply()這類方法繫結this關鍵字又稱為顯示繫結,明面上我們完全可以看到他指向了誰
function f7(){
this.x = function(){
console.log(this.a) // 20
}
}
var obj5 = {
a : 20
}
f7.call(obj5);
obj5.x();
複製程式碼
通過call()方法,函式f7被封裝為物件obj5的一個屬性,this的值指向了呼叫函式的物件obj5了,
通過對call()方法的使用,我們知道,如果第一個引數為null,或者不寫,那麼其間的物件就是window,我們看下面示例
function f8(){
this.x = function(){
console.log(this.a) // 15
}
}
var obj6 = {
a : 20
}
var a = 15;
f8.call(null);
x();
//我們可以看到,這段函式裡面並沒有定義過函式x,但是在這裡可以直接呼叫
//這說明,我們的this指向了window,x() === window.x();
複製程式碼
四、建構函式中的this關鍵字
先看示例:
function F1(){
this.a = 10;
this.b = 20;
this.f6 = function(){
this.c = this.a+this.b;
console.log(this.c); //30
console.log(this === F1) //false
}
}
var oInit = new F1();
oInit.f6();
複製程式碼
這裡就很奇怪了,明明所有症狀都表現為this指向的就是建構函式F1,但是為什麼this沒有指向F1呢,在這裡,我們需要了解,在new F1()的這個過程中,new到底做了什麼?
看下面程式碼:
//new 過程如下
{}
//new 先建立了一個空物件{}(第一步)
{}.__proto__ = F1.prototype
//空物件的隱式原型指向F1的原型物件(第二步)
F1.call({});
//利用call方法繼承了F1中所有屬性與方法,這一步將F1內部的this指向指向了這個空物件
// 繼而會執行這個空物件繼承而來的內容,在最後
return this;
//在我們不主動改變return 內容的時候,預設會將this返回出去 (第四步)
//而Oinit = {}
// 自然而然的最後this就指向了Oinit
複製程式碼
我看到很多人說,建構函式中的this指向的是建構函式本身,這種說法是不正確的,
他就算瞎指也不會指向函式本身,誰去呼叫他是吧,他指向的new的例項,所以一定要注意這裡
在建構函式中使用return有個特殊情況,我們來看看
//示例一
function f9(){
this.a = 10;
var obj10 = {};
obj10.a = 20;
return this.a
}
var init2 = new f9();
init2.a;
//示例二
function f9(){
this.a = 10;
var obj10 = {};
obj10.a = 20;
return obj10;
}
var init2 = new f9();
init2.a;
複製程式碼
這兩個示例有點特殊,特殊在於return出來的值,這裡先不說this指向的問題,說說return返回的值的問題,在函式中如果返回的是一個物件(包括函式,陣列,物件),即返回出來的是物件本身,就會覆蓋原來的this物件,如果返回的是值型別, 則是返回this物件;
所以從表面上看是覺得建構函式的例項化物件在呼叫引數,其實不然,內部正真呼叫的是返回的物件,那麼自然this值指向了返回的物件
五、事件中的this關鍵字
1、事件中直接使用this
<div class="clickMe">click me</div>
<script>
var clickDom = document.querySelector(`.clickMe`);
clickDom.onclick = function(){
console.log(this.className) //clickMe
}
</script>
複製程式碼
這裡this指向了操作事件的元素,所以如果直接在事件中使用this,那麼this則指向操作事件的元素;
2、事件中的函式使用this;
<div class="clickMe">click me</div>
<script>
var clickDom = document.querySelector(`.clickMe`);
clickDom.onclick = function(){
function test(){
console.log(this.className) //undefined
console.log(this) //[object Window]
}
test();
}
</script>
複製程式碼
這裡指向了window,我們是不是可以得出結論,如果在事件中的函式去呼叫this,那麼就看該函式是誰呼叫的呢?先不急,我們再看下面一個示例:
<div class="clickMe">click me</div>
<script>
var clickDom = document.querySelector(`.clickMe`);
clickDom.onclick = function(){
var obj7 = {
a : 10,
f7 : function(){
console.log(this.a); //10
}
}
obj7.f7();
}
</script>
複製程式碼
這裡我們完全可以確定,就算事件中,this的指向還是遵循一個原則,誰呼叫指向誰 + 就近原則;
事件後面跟的是一個匿名函式,而呼叫這個匿名函式的則是事件中的元素,所以自然是指向了呼叫事件的元素了,實則上面的示例無不在佐證這一原則
好了,關於this的指向性問題,就講到這裡了,還是那句話,誰呼叫指向誰 + 就近原則 ,萬般皆虛幻,透過現象看本質,才能正真知道this指向的是誰
原創不易,總結不易,手打不易,轉載時請註明出處,謝謝