函數在執行時,會在函數體內部自動生成一個this指針。誰直接調用產生這個this指針的函數,this就指向誰。es6
怎麼理解指向呢,我認爲指向就是等於。例如直接在js中輸入下面的等式:數組
console.log(this===window);//true
狀況不一樣,this指向的對象也不一樣。例如:瀏覽器
1. 函數聲明的狀況app
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()。this
2. 函數表達式spa
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這個對象直接調用。prototype
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用變量保存起來,即 var that=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出來的新實例。
更新:
再來一個例子說明一下,通俗的理解一下this指針的指向:誰直接調用產生 this 指針的函數,這函數裏的 this 指針就指向誰。
var factory = function(){ this.a = 'first-A'; this.b = 'first-B'; this.c = { a:'second-A', b:function(){ console.log(this.a) return this.a } } } new factory().c.b(); // second-A
(1) 這個代碼首先考的是運算符的優先級。MDN運算符優先級 能夠先查看優先級再思考。
(2) new的優先級和 點運算符等級同樣,從左至右執行,因此先執行 new factory() 而後在執行 點運算符。
(3) 執行了 new 操做以後。而後發現函數調用的優先級和成員訪問運算符的優先級同樣,而後遵循從左到有的執行方式。所以就先執行 成員訪問運算符 .c
(4) 這時 .c 就是一個對象,而後再取 b 屬性,是個函數。這個時候 this 指針已經產生, 而產生這個this指針的是b函數,並且是經過 c 調用的。所以此時 this 的指向就是 c 對象。因此最後打印出second-A
(5) 若是想要 c 裏面的 b函數中 this指向的是 factory 實例。要麼使用 bind.apply,call等方法來強行改變。 要麼就把 b 函數寫成 es6箭頭函數的方式。這樣 b 函數就沒有this指針,而 b 函數裏面的this,就是上一級的 this。
而後再來講一下回調函數,當即執行函數(IIFE),點擊事件 的 this 的指向。
弄清楚這個以前,咱們要知道,函數傳參是按值傳遞,若是是基本數據類型,則是直接複製數據的值傳過去。若是是引用類型,好比對象,函數這種,傳遞的就是該數據 在堆中存放的地址。
舉個例子來證實:
var obj = { a: 10, getA: function(){ console.log(this.a); } } active(obj.getA); function active(fn) { console.log(fn === obj.getA ) // true fn(); }
(1) 形參 fn 與 obj.getA 全等,就說明了 傳的是存放的地址,而不是內容。由於對象就算內容相同,存放地址不一樣的話,也不會相等。
那麼,回調函數就是傳的 函數在堆中的地址,也就是說,回調函數中 this 的指向,決定於執行回調函數 時的執行上下文環境。
首先是 setTimeout,setInterval 這種類型的回調函數。
setTimeout的回調 --- 例一
setTimeout(function(){ console.log(this) })
(1) 這是最最經常使用的常見的定時器用法,回調函數裏的this指向的是window。
(2) 由setTimeout()
調用的代碼運行在與所在函數徹底分離的執行環境上。這會致使,這些代碼中包含的 this
關鍵字在非嚴格模式會指向 window
(或全局)對象,嚴格模式下爲 undefined,這和所指望的this
的值是不同的。在嚴格模式下,setTimeout( )的回調函數裏面的this仍然默認指向window對象, 並非undefined。 這幾句話是 MDN上,setTimeout中 關於 this 的問題 裏對 this 指向的解釋。
(3) 個人理解是:因爲setTimeout屬於宏任務,它的回調在延時以後才進入到主線程執行,而函數執行的時候才明確 this 的指向。執行的時候,因爲沒有設置內部this的指向。至關因而普通函數調用。因此會默認指向window
setTimeout的回調 --- 例二
var obj = { age:10, getage:function(){ console.log(this.age) } } setTimeout(obj.getage,1000) // undefined setTimeout(function(){ obj.getage() // 10 },1000)
(1) 第一個setTimeout,執行obj.getage 以後,至關於setTimeout的回調是一個匿名函數,執行的時候,函數內部未設置this的指向。至關因而普通函數調用。因此this默認指向window,因此結果是undefined。
(2) 第二個setTimeout,傳給setTimeout的也是一個匿名回調函數,執行匿名函數,執行到 obj.getage() 的時候,getage函數裏的this,指向的就是obj了,因此能打印出10。仍是遵循 誰調用產生 this指針的函數,this就指向誰的規則
對於 數組的遍歷方法:
foreach,map,filter,some,每次 callback
函數被調用的時候,this
都會指向 最後一個參數 thisArg
的這個對象。若是省略了 thisArg
參數,
或者賦值爲 null
或 undefined
,則 this 指向全局對象 。在嚴格模式下則是undefined(未傳值的時候)。若是用箭頭函數的寫法,就要看當前上一層的 this 指向的是哪裏了
reduce 累加器的參數中並無 thisArg 對象能夠傳,可是在回調函數中,this指向的是window。若是用箭頭函數的寫法,就要看當前上一層的 this 指向的是哪裏了
對於 點擊,移入移出等相似事件的回調函數 的 this 指向。
<button type="button" id="btn">點我啊</button> function getDom(){ console.dir(this) } // 第一種調用方法 document.getElementById('btn').addEventListener('click',function(){ getDom(); }) // 第二種調用方法 document.getElementById('btn').onclick = getDom() // 第三種調用方法 document.getElementById('btn').addEventListener('click',getDom) // 第四種調用方法 <button type="button" id="btn" onclick="console.log(this)">點我啊</button>
(1) 第一種調用方法,this指向的是window。雖然在function(){} 回調函數裏的 this 指向的是button這個DOM對象,可是getDom是在這裏面調用的,和普通函數調用沒什麼區別。因此也指向window
(2) 第二種都不用點擊,直接觸發,this指向window。由於直接當作普通函數調用了。
(3) 第三種方法,this指向 button這個DOM對象。回調函數傳入的是函數執行的地址,執行的時候至關因而在window環境下執行,因此getDom的this指向的是window
(4) 第四種方式,this指向 button 這個DOM對象。
當函數被用做事件處理函數時,它的this
指向觸發事件的元素(一些瀏覽器在使用非addEventListener
的函數動態添加監聽函數時不遵照這個約定)。 --- MDN
對於 當即執行函數 IIFE 中 this的指向,指向的是window
(function(){ console.log(this) // window })()
到這裏,我仍是沒搞懂下面這種狀況:
var obj={ age:10, say:function(){ function get(){ console.log(this) // window } get(); } } obj.say();
get函數裏的this指向的是window,由於get函數 獨立調用,並無爲內部 this 明確指向。因此會指向 window 。若是是嚴格模式,則指向undefined。
不明白的是:
(1)既然 this 指向的是window,爲何get函數在window上不能訪問?
(2)這種在函數內部定義並執行的方式,和當即執行函數有沒有區別?
(3)詞法分析的時候,這個函數是被怎樣處理的?