深入理解JS函數中this指針的指向

函數在執行時,會在函數體內部自動生成一個this指針。誰直接調用

產生這個this指針的函數,this就指向誰。

怎麼理解指向呢,我認為指向就是等於。例如直接在js中輸入下面的等式:

console.log(this===window);//true

情況不同,this指向的對象也不同。例如:

var bj=10;
function add(){
var bj=20;
console.log(this);//window
console.log(this.bj);//10
console.log(bj);//20
console.log(this.bj+bj);//30
}
add();
window.add();

(1) 執行了add()之後,此時的this指向的是window對象,為什麼呢?因為這時候add是全局函數,是通過window直接調用的。所以下面我專門寫了個window.add()就是為了說明,全局函數的this都是指向的window。

(2) 就像alert()自帶的警告彈窗一樣,window.alert()執行之後也是一樣的效果。所以只要是 window點 這種調用方式都可以省略掉,因此警告彈窗可以直接使用alert()。

2. 函數表達式

var bj=10;
var zjj=function(){
var bj=30;
console.log(this);//window
console.log(this.bj);//10
console.log(bj);//30
console.log(this.bj+bj);//40
}
console.log(typeof zjj);//function
zjj();
window.zjj();

(1) 執行了zjj()之後,函數中的this也是指向window對象。原因和第一個是一樣的,都是通過window這個對象直接調用

3. 函數作為對象的屬性去調用------例一

var bj=10;
var obj={
name:"八戒",
age:"500",
say:function(){
var bj=40;
console.log(this);//就是obj這個對象
console.log(this.bj);//undefined
console.log(this.name);//八戒
}
}
obj.say();
window.obj.say();

(1) 當obj.say()被執行的時候,此時的this指向的是 obj 這個對象,為什麼呢?因為say函數是通過obj這個對象直接調用

的。

(2) 那有人可能會問了,obj對象實際上也是通過window對象調用的,為什麼this不指向window呢?我認為是因為say這個函數是通過 obj 對象直接調用的,而沒有通過 window 對象直接調用,因此this不會指向window。看下面的例子就明白了。

3.1 函數作為對象的屬性去調用------例二

var bj=10;
var obj={
name:"八戒",
age:500,
say:function(){
console.log(this);//是obj這個對象
console.log(this.bj);//undefined
console.log(this.name)//八戒
},
action:{
name:"悟空",
age:1000,
say:function(){
console.log(this);//是action這個對象
console.log(this.bj);//undefined
console.log(this.name)//悟空
}
}
}
obj.say();
obj.action.say();
window.obj.action.say();

(1) obj.say()執行之後,此時這個函數里的this指向的是obj對象,原因是因為say函數是通過obj

直接調用的。

(2) obj.action.say()執行之後,此時這個函數里的this指向的是action對象,原因是因為say函數是通過action對象直接調用的。並沒有通過obj直接調用。也沒有通過 window 直接調用,所以此時action對象中say函數里的的this指向並不會是obj或者window。

3.2 函數作為對象的屬性去調用------例三

var bj=10;
var obj={
name:"八戒",
age:500,
say:function(){
console.log(this);//就是obj這個對象
console.log(this.bj);//undefined
console.log(this.name)//八戒
function wk(){
console.log(this);//window
console.log(this.bj);//10
console.log(this.name);//這裡顯示的是為空
}
wk();
},
}
obj.say();

(1) 這種情況下,say函數里的this指針還是指向的obj,原因是因為say函數是通過obj直接調用

(2) 但是這時候wk函數中的this就是指向的是window了。為什麼呢?因為 wk()函數在 say()函數中,是屬於普通函數調用,但是並沒有通過say或者obj直接調用,只是自執行,這個時候,wk就是一個全局函數,因此該函數的this指向的就是window。

(3) 那為什麼this.name是顯示的為空呢?因為 window 對象中本身就有一個 name 值,並不是某處添加的,如果把name換成age,得到的就是undefined了。

(4) 那怎樣讓wk()函數中的this指向obj呢。一種方式就是在say函數中把say()函數的this用變量保存起來,即 varthat=this; 然後wk()函數使用that就能達到指向obj的目的了。另外的方式是通過apply或者call來改變。

(5) 那wk()在這裡能不能寫成window.wk()呢?這樣是不行的,會報錯,window.wk is not a function。為什麼不行呢,this不是指向window嗎,為什麼widow對象裡滅有wk()這個函數。。這個嘛,我也不知道,先留個坑,後面再來填 ×××

3.3 函數作為對象的屬性去調用------例四

var bj=10;
var obj={

name:"八戒",
age:"500",
say:function(){
var bj=40;
console.log(this);//window
console.log(this.bj);//10
console.log(this.name);//這裡沒有輸出內容
}
}
var elseObj=obj.say;
elseObj();

(1) 執行了elseObj()函數之後,為什麼say函數中的this卻指向了window呢?首先要理解這句話:誰直接調用產生這個this指針的函數,this就指向誰。當obj.say賦值給elseObj的時候,elseObj只是一個函數,而並沒有執行,因此this指針的指向並不明確,這個時候執行到 var elseObj=obj.say的 時候,整程序相當於:

var bj=10;
var elseObj=function(){
var bj=40;
console.log(this);
console.log(this.bj);
console.log(this.name);
}
elseObj();

這就和 第2種 函數表達式的情況一樣了。所以,當執行elseObj()的時候,this就指向window,this.obj為10,因為這時候elseObj()是通過 window 直接調用

(2) this.name為空是因為 window 對象中本身就有一個 name 值,並不是某處添加的,如果把name換成其它的比如age,得到的就是undefined了,因為全局並沒有age屬性。

3.4 函數作為對象的屬性去調用------例五

var bj=10;
var obj={
name:"八戒",
age:500,
say:function(){
return function(){
console.log(this);//window
console.log(this.bj);//10
console.log(this.age);//undefined
}
}
}
obj.say()();
// var elseObj=obj.say();
// elseObj();

(1) obj.say()()為什麼會有兩個括號?因為obj.say()執行之後返回的是一個函數,並沒有執行,再加一個括號就是執行返回的那個匿名函數。

(2) 如果不習慣也可以使用上面註釋的那種方式,是一樣的效果。

(3) 執行了函數之後,為什麼返回的函數中this是指向window的呢?那是因為執行obj.say()的時候,只是一個函數,相當於就是註釋裡的第一行代碼,這時候返回的函數並未被執行。當再加一個括號的時候,就是執行了返回的那個函數,這個時候返回的函數就相當於是一個全局函數,是通過window直接調用

,因此this就是指向的是window。

4. 工廠模式中this的指向------例一

var bj=10;
function fun(a,b){
   console.log(this);//window對象
var bj=20;
var sun=new Object();
sun.one=a;
sun.two=b;
sun.say=function(){
console.log(this);//是sun對象,{one: 2, two: 3, say: ƒ()}
console.log(this.bj);//undefined
console.log(this.one);//2
}
return sun;
}
var wk=fun(2,3);
wk.say();

(1) 話說為什麼叫工廠模式,我搞不太清楚,不過這個不重要,重要的是通過這個模式,在每次調用函數的時候,雖然每次都返回的是sun這個對象,但是每個對象都是不相似的,即使內容一樣,比如 var sf=fun(2,3); console.log(sf===wk);//false 。

(2) 那為什麼say()函數執行之後,this是指向返回的那個對象呢?這個很明顯嘛,say()是通過wk這個對象直接調用的,而wk是fun函數返回sun對象。所以這裡的this就指向的是返回的對象。所以this.bj為undefined,因為返回的對象中沒有bj屬性。

(3) 我認為這種模式最重要的還是 renturn sun這個返回語句,這個是必不可少的。

(4) fun(a,b)這個函數中的this指向的是window,原因是執行 var wk=fun(2,3); 的時候,fun函數已經被執行了,並且直接調用它的就是window,所以這時的this是指向的window。

4.1 工廠模式中this的指向------例二

var bj=10;
function fun(a,b){
   console.log(this);//window對象
var bj=20;
var sun=new Object();
sun.one=a;
sun.two=b;
sun.say=function(){
console.log(this);//是sun對象,{one: 2, two: 3, say: ƒ()}
return function(){
console.log(this);//是window對象
}
}
return sun;
}
var wk=fun(2,3);
var ss=wk.say();
ss();

(1) 為什麼say函數中return 的函數中this是指向的window對象呢?首先,執行到 var wk=fun(2,3); 的時候,wk是一個對象。繼續執行下一句代碼,ss這時候是一個函數,就是通過say函數返回之後賦值的。這時候返回的函數還未執行,this指向並不明確。當執行到最後一句代碼,ss()函數執行了。這時候,ss函數就是一個全局函數,是通過window

直接調用的。所以這時的this指向的是window。

(2) 如果say中返回的是一個對象,對象中又有個函數,像下面一樣:

sun.say=function(){
console.log(this);//是sun對象,{one: 2, two: 3, say: ƒ()}
return {
wk:"1",
say:function(){
console.log(this);
}
}
}

這時候執行到ss.say()的時候,this指向的就是ss這個對象,即通過say函數返回的那個對象。原因還是一樣,say函數是通過ss直接調用的,而ss對象是wk.say()返回的對象。

5. 構造函數中this的指向

var bj=10;
function Add(){
var bj=20;
this.bj=30;
this.say=function(){
console.log(this);//Add {bj: 30, say: ƒ()}
console.log(this.bj);//30
}
console.log(this) ;//Add {bj: 30, say: ƒ()}
}
var obj=new Add();
console.log(typeof obj);//object
obj.say();

(1) 要明白構造函數的this指向,我們需要明白調用構造函數經歷的步驟:

a。創建一個新對象。

b。將構造函數的作用域賦給新對象(因此this就指向了這個新對象)。

c。執行構造函數中的代碼(為這個新對象添加屬性)。

d。返回新對象。

摘至js高程 6.2.2節。

(2) 構造函數與工廠模式相比:(原諒照搬js高程的話)。

a。沒有顯示的創建對象。

b。沒有return語句。

c。直接將屬性和方法賦值給 this 對象。

摘至js高程 6.2.2節。

(3) 首先,obj.say()執行之後,say函數中this的指向是obj對象,這個很明顯,不再贅述。在不用new操作符的時候,Add()函數里的this指向的就是window;但是使用了new操作符之後,Add()函數中 console.log(this) 這個this為什麼是obj對象,而不是window呢?

(4) 這個原因我認為在js權威指南4.6節對象創建表達式和8.2.3構造函數使用中,有所說明。使用new操作符的時候,js先創建一個新的空對象,然後,js傳入指定的參數並將這個新對象當做this的值來調用一個指定的函數。這個函數可以使用this來初始化這個新創建對象的屬性。所以當使用new操作符之後,函數中的this指向的是新創建的對象。所以構造函數中的this就是指向new出來的那個對象。

(5) 如果構造函數中有return語句,那麼此時 var obj=new Add(); obj就是return出來的內容,但是Add函數中的this還是指向的創建的新對象Add;

6. 原型對象中this的指向

var bj=10;
function Add(){
  console.log(this);//Add{}
};
Add.prototype.bj=10;
Add.prototype.say=function(){
console.log(this);//Add{}
return function(){
console.log(this);//window
}
}
var obj=new Add;//沒傳參數可以省略括號
obj.say()();

(1) obj.say()()執行的時候,this指向的是window,這個還是因為obj.say()執行時返回的是一個函數,然後再加一個括號,就執行返回的這個函數,此時這個函數屬於全局函數,所以,this會指向window

(2) Add()這個構造函數中的this指向的是Add{},原因和上面構造函數中this的指向一樣。

(3) Add.prototype.say=function(){ console.log(this) } 這裡面的this 也是指向的是Add{},至於原因,我認為是因為say()這個函數是通過obj直接調用的,所以this指向的是obj,所以是Add{}。

總結:

要想判斷函數中this的指向,只要知道誰直接調用產生this指針的函數,this就指向誰了。只是要注意使用了new 操作符之後,構造函數內部的this指向的是新對象,通俗點講就是new出來的新實例。

深入理解JS函數中this指針的指向


分享到:


相關文章: