this
這個東西相信對於不少新人來講是個糊塗帳,甚至老手來說,在一些稀奇古怪的狀況下都不必定完徹底全的講清楚.特別是面對腦經急轉彎同樣的面試題,讓人苦笑不得.java
this
既不指向函數自身,也不指向函數的詞法做用域.this
其實是在函數被調用時發生的綁定,它的指向只取決於函數被誰調用了或者函數在哪裏被調用.es6
this
在全局環境下指向window (默認綁定)在全局環境下,咱們寫的全部的變量和函數都是window對象裏面的屬性和方法.面試
在瀏覽器中打印以上代碼,獲得這些結果.數組
this
函數被誰調用,this
在哪裏被調用.(隱式綁定)這句好像不太好理解,我貼一下代碼,而後逐句解釋一下.瀏覽器
var a ={ //聲明一個a對象,裏面有個isThis方法
isThis(){
console.log(this)
}
}
a.isThis()//調用一下這個方法
複製代碼
結果以下:app
如今理解一下這句話,this
被誰調用, a對象裏面有一個函數isThis, a調用isThis, isThis被a調用, 故this指向了a對象.
咱們再來看第一條:window對象中有個d方法,因此d方法被window調用的時候,this天然指向了window對象.函數
this
函數在哪裏被調用 this
便指向誰ui
var a = 'a'//全局聲明一個變量a
function foo(){
return this.a
}
var b = {//全局聲明一個變量b
a:'this is b.a',
log(){
console.log(this.foo())
}
}
b.foo = foo
將foo方法賦給b
b.log()//調用log方法
foo()//全局調用log方法
複製代碼
打印結果:this
首先由於foo賦值給了b,故在b調用本身對象中的方法時,this指向b自身,因此foo函數中的this指向b,在b.log()的時候,log函數是b自己的方法且被b調用,故this指向b,且執行了this.foo,foo被b調用,或者說foo在b中調用,故this指向b,logspa
而在全局中調用foo 由於是window調用的b(b在window中被調用)因此this指向window
再看一個例子:
function bar(){
console.log(this.a)
}
var obj1 ={
a:1,
bar
}
var obj2 = {
a:2,
obj1
}
obj2.obj1.bar()//
打印結果:1
複製代碼
在以上的代碼中,bar函數被obj1調用,與obj2無關,因此this指向obj1,因此打印出來了.
繼續理解!!
如下方代碼爲例:
var a ={ // 聲明一個a對象,包含一個isThis方法
isThis(){
console.log(this)
}
}
a.isThis() //a調用此方法,打印出this是a本身
window.isThis = a.isThis // 將a中的isThis方法賦給window對象中
isThis()//在全局中調用該方法
實質上等於 window.isThis()
複製代碼
看一下結果:
在以上操做中,咱們將a中的isThis方法賦給window對象中,再用window調用,this便指向了window對象.
再看如下代碼:
首先我聲明瞭一個b對象,再將a中的isThis方法賦給b,再用b去調用該方法,即打印出來了b對象本身.
咱們不管是把isThis方法賦給b仍是window對象,對於b對象和window對象而言,他們之中都有了一個isThis方法,再結合這一句 this
函數在哪裏被調用 this
便指向誰.是否是感受很清晰.
call
apply
中的this(顯式綁定)call和apply都接受兩個參數,第一個參數是當該函數運行的時候的this環境(thisObj),第二個參數是函數接收的參數,call接受單個/多個參數,apply只能接受數組參數.
忽略第二個參數,咱們單說第一個參數.
代碼以下:
var a ={ //聲明一個a變量
a:'this is a',
isThis(){
console.log(this)
}
}
var b = {//聲明一個b變量
b:'this is b'
}
var c = {//聲明一個c變量
c:'this is c'
}
function isThis(){
console.log(this)
}
a.isThis() //調用一下
isThis()
isThis.call(b)
isThis.apply(c)
a.isThis.call(b)//call一下b
a.isThis.apply(c)//apply 一下c
複製代碼
看一下打印結果:
在以上代碼中,不管是call仍是apply,不管是在哪裏調用,或者被誰調用, 接受的第一個參數(thisObj),就是函數中this指向的對象,或者說第一個參數就是這個函數運行時候的this
因此,當一個函數以call或者apply方式調用的時候,call或者apply接收的第一個參數,就是函數運行時候的this
這即是顯式綁定
bind
中的this(顯式綁定)如今咱們認識一下bind函數.
bind
接收兩個參數,與call apply很類似。第一個參數是當該函數運行的時候的this環境(thisObj),第二個參數是函數接收的參數,可是不一樣的是的,當一個函數調用了 call apply 方法返回的該函數被call apply以後的結果。即:
function bar(){
return this.a
}
var baz= {
a:3349
}
bar.call(baz)
返回結果是 3349
複製代碼
而bind 不同,當一個函數調用了bind方法,並傳入了參數返回的一個新的函數,而且該函數的this被綁定了,誰都改變不了。一樣咱們不討論第二個參數的意義,咱們只研究this的問題
function bar(){
return this.a
};
var a = 0;
var b = {
a:1
};
var c = {
a:2
};
var d = {
a:3
};
bar()
bar.call(b)
bar.apply(c)
複製代碼
結果顯而易見。
那麼咱們如今要作這樣的操做,剛纔已經說過了,bind會返回一個新的函數。
因此,在下列代碼中,baz賦值爲bar經過bind(d)以後返回的函數
再調用一下bar,咱們來看看結果
function bar(){
return this.a
};
var a = 0;
var b = {
a:1
};
var c = {
a:2
};
var d = {
a:3
};
var baz = bar.bind(d);
baz()
複製代碼
結果爲3 ,顯然,在baz這個函數被調用的時候,函數裏面的this指向了d,下面一組代碼將展現什麼叫作硬綁定。
function bar(){
return this.a
};
var a = 0;
var b = {
a:1
};
var c = {
a:2
};
var d = {
a:3
};
var baz = bar.bind(d);
baz.call(a);
baz.call(b);
baz.call(c);
baz()
var e = {
baz
};
e.baz();
複製代碼
這即是硬綁定,baz函數是bar函數經過bind返回的新函數,而bar函數進行了綁定對象d的操做,因此返回的baz函數始終指向d。
換言之,一個bar函數經過調用bind綁定了一個d對象,bar.bind(d)這個調用將會返回一個新函數baz,而新函數baz的this始終都指向d這個對象,不管使用call仍是apply,仍是被其餘對象調用或全局調用,this都不會發生改變。
new
優先級最高的this關於js是否有類這個概念,各有說辭,有的說有,有的說沒有,我比較認同一個觀點是js中確實沒有類,可是有類的概念,包括es6的class也不是真的類,只是語法糖,一樣說道構造函數,我比較認同《你不知道的javaScript》上卷中的一段話。
「在js中,構造函數只是一些使用new操做符時被調用的函數,他們並不會屬於某個類,也不會實例化一個類。實際上,它們甚至都不能說是一種特殊的函數類型,它們只是被new操做符調用的普通函數而已」
言歸正傳,咱們今天討論的是this的指向。
一下面代碼爲例:
function bar(a){
this.a = a
}
var baz = new bar(2)
console.log(baz,baz.a)
複製代碼
以上操做:
1,創造一個全新的對象(也能夠認爲是實例)
2,這個新對象會被執行到原型鏈上,即:baz.__proto__ === bar.prototype
3,這個新對象(實例)的this會指向他本身,而再也不指向函數的this。
最後一句話是否是很難理解。
咱們看看一下代碼。
function bar(){
this.a = 2
}
var foo ={
bar
}
var baz = new foo.bar()
foo.bar()
複製代碼
第一個操做中咱們是foo調用了bar,可是結果並非咱們預期所想的,foo這個對象會由於調用了bar方法而向本身賦值了a=2這個屬性,顯然,在被new調用的時候,bar方法裏面的this指向了新建立的對象baz,並給baz複製了a=2這個屬性,在第二個操做中,bar函數的this才指向了foo,而且給foo賦值a=2這個屬性。
其實在咱們第一段的代碼中,全局調用了var baz = new bar(2)
bar函數中的this也沒有指向window 而是指向了心建立的對象baz
因此可得new的優先級是高於 上文提出的情況1和情況2的
由於new和call/apply沒法一塊兒使用,咱們直接比較new和bind之間的優先級,只要比bind還高,那一樣就比call/apply高
function bar(a){
this.a = a
}
var a = {
bar
}
var b = {
}
var c = a.bar.bind(b,'a')
c()
複製代碼
以上結果很符合bind的直覺對不對,那麼下來咱們繼續操做
function bar(a){
this.a = a
}
var a = {
bar
}
var b = {
}
var c = a.bar.bind(b,'a')
var d = new c()
複製代碼
在上面的操做中,咱們經過new操做符,調用了a對象的bar函數經過綁定(bind) b對象後返回的c方法,但是c方法並無像咱們以前直接調用c方法那樣,this指向了b對象,而且給b對象賦值a='a'這個屬性,而是直接將this指向了新建立的d對象(實例),而且給d對象賦值了a='a'這個屬性,因此new操做符在this的操做中是優先級最高的。