引言
最近在學(xué)習(xí)的過程中發(fā)現(xiàn)越來越多的地方使用到this
,在此之前我對this的指向知之甚少周叮,由此決定深究一一番欺矫,做下記錄以備今后復(fù)習(xí)之用,文章內(nèi)所有代碼執(zhí)行結(jié)果只針對瀏覽器環(huán)境。
this在做代詞時翻譯成中文的意思是:
(指較近的人或事物)這霹肝,這個;(指已提到過的人或事物)這;
記住這個對于理解this有幫助。
心得
- 一般情況下this的指向只有在調(diào)用時才能確定塑煎,箭頭函數(shù)是個例外在定義是就已經(jīng)確定沫换;
- 函數(shù)中的this指向的是函數(shù)的最終調(diào)用者(可以結(jié)合this的中文翻譯理解);
- 函數(shù)一定是某個對象調(diào)用了才會執(zhí)行,也就是說this指向的一定是一個對象Object讯赏;
案例引入
看完幾篇大佬們講解this指向的博客垮兑,都用到了一個經(jīng)典的案例,我也班門弄斧一番漱挎,掏出這個案例琢磨一二系枪。
var a = 5
var obj = {
a : 10,
foo: function(){
console.log(this.a)
}
}
var bar = obj.foo
obj.foo() // 10
bar() // 5
案例分析
- 案例中
obj.foo()
這一行的輸出結(jié)果理解起來還是非常容易的,函數(shù)的調(diào)用者是obj
磕谅,所以函數(shù)中的this指向的是obj這個對象私爷,很容易想到輸出的是obj中的a值; -
bar()
這一行的輸出結(jié)果就很容讓人頭腦犯暈膊夹,在定義bar
是時候明明用的是obj.foo
為什么輸出結(jié)果不是obj中的a衬浑,其實(shí)var bar = obj.foo
這一行只是個煙幕彈,把代碼改成下面這個樣子就比較容易理解放刨;
//這一步就類似上一段代碼中的var bar=obj.foo
var bar = function(){
console.log(this.a)
}
- 在定義bar的時候并沒有執(zhí)行
obj.foo
工秩,只是將foo這個函數(shù)體賦值給了變量bar,像上面代碼這個樣子(這樣說比較好理解进统,實(shí)際賦值的是foo函數(shù)對象的地址值)助币,這個時候執(zhí)行bar調(diào)用者是window
,跟obj一點(diǎn)關(guān)系都沒有螟碎,所以輸出的是window中的a值奠支;
案例小結(jié)
通過上面這個簡單的案例(看著簡單,內(nèi)含量很大)抚芦,不難發(fā)現(xiàn)函數(shù)中this指向的是函數(shù)的調(diào)用者倍谜,關(guān)鍵點(diǎn)就是確定函數(shù)是誰調(diào)用的,那么就涉及到函數(shù)的四種綁定方式叉抡。
函數(shù)綁定
函數(shù)綁定的實(shí)質(zhì)就是函數(shù)調(diào)用尔崔,說成函數(shù)綁定顯得高大上,函數(shù)綁定有四種方式分別是:默認(rèn)綁定褥民、隱式綁定季春、現(xiàn)實(shí)綁定、new綁定消返,下面是我對這四種綁定的理解载弄。
默認(rèn)綁定
顧名思義默認(rèn)綁定就是javaScript內(nèi)部默認(rèn)的函數(shù)調(diào)用者(這句話等于沒說),發(fā)生在函數(shù)調(diào)用時沒有任何修飾情況下撵颊,不多說先來段案例代碼
var name = 'Tom'
function foo(){
console.log('foo---'+this.name);
}
setTimeout(function(){
var name = 'Jerry'
console.log('定時器---'+this.name)//定時器---Tom
foo()//foo---Tom
},1000)
foo()//foo--tom
案例分析
- 案例中最先輸出的是全局下調(diào)用的
foo
宇攻,非常好理解它的調(diào)用者是window
,所以輸出的是window中的name值倡勇; - 接下來輸出的是定時器中的
console.log('定時器---'+this.name)
這行代碼的結(jié)果逞刷,為什么this.name
輸出的不是Jerry
,答案是javaScript中默認(rèn)定時器中回調(diào)函數(shù)的this是window; - 可以這樣理解:定時器就是一個函數(shù)夸浅,它接收兩個參數(shù)仑最,在定時器中寫函數(shù)只是一個傳參的過程,中間沒有涉及到任何改變this指向的操作(開頭提到過this指向的一定是一個對象Object)帆喇,將寫在定時器中的函數(shù)提取出來會更容易理解警医,像是下面這樣;
var name = 'Tom'
function time(){
var name = 'Jerry'
console.log('定時器---'+this.name)//定時器---Tom
}
setTimeout(time,1000)
- 最后輸出的是定時器中調(diào)用的foo,寫這一行的目的是加強(qiáng)對默認(rèn)綁定發(fā)生在函數(shù)調(diào)用時沒有任何修飾的情況下這句話的理解坯钦。
隱式綁定
隱式綁定可以理解成書寫代碼過程中發(fā)生了函數(shù)綁定但還不自知(特別像我這個小白)预皇,還是通過案例代碼理解
var name = 'Tom'
var obj = {
name: 'Jerry',
f1: function () {
console.log('f1---' + this.name);
},
child: {
name: 'Speike',//Speike是只狗
f2: function () {
console.log('f2---' + this.name);
},
}
}
obj.f1()//f1---Jerry
obj.child.f2()//f2---Speike
案例分析
- 像案例中這種通過對象調(diào)用函數(shù)的方式就是隱式綁定,函數(shù)中的this指向函數(shù)最后的調(diào)用者葫笼,還記的this的中文翻譯嗎?
(指較近的人或事物)這
,在這里函數(shù)的最后調(diào)用者就是調(diào)用函數(shù)時離函數(shù)名最近的那個對象深啤; - 豁然開朗
obj.f1()
的調(diào)用者就是obj
拗馒,obj.child.f2()
調(diào)用者就是obj.child
,分析到這里輸出結(jié)果就很好理解了路星。
顯示綁定
顯示綁定就是代碼中明確的指出了函數(shù)的調(diào)用對象,通過三個方法實(shí)現(xiàn)分別是call()
诱桂、apply()
洋丐、bind()
call()和apply()
- call()和apply()這兩個方法使用起來幾乎一樣,它們都會會在使用時立即執(zhí)行函數(shù)挥等,區(qū)別在于這兩個方法的傳參方式不一樣友绝;
- call()方法接收多個參數(shù),第一個參數(shù)是this指向的對象肝劲,后續(xù)參數(shù)是函數(shù)中需要用到的所有參數(shù)迁客;
- apply()方法只接受兩個參數(shù),第一個參數(shù)是this指向?qū)ο蟠腔保诙€參數(shù)是函數(shù)中需要用到的所有參數(shù)組成的數(shù)組掷漱。
var name = 'Tom'
var obj = {
name: 'Jerry',
f1: function (a,b) {
console.log(a+b+'f1---' + this.name);
},
child: {
name: 'Speike',//Speike是只狗
f2: function (a,b) {
console.log(a+b+'f2---' + this.name);
},
}
}
obj.child.f2('1','2')//12f2---Speike
obj.child.f2.call(obj,'1','2')//12f2---jerry
obj.child.f2.apply(obj,['1','2'])//12f2---Jerry
bind()
bind()
與call()
和apply()
比較,區(qū)別在于bind()在使用時不會立即執(zhí)行函數(shù)榄檬,bind只是修改函數(shù)和的this指向卜范;
var name = 'Tom'
var obj = {
name: 'Jerry',
f1: function (a,b) {
console.log(a+b+'f1---' + this.name);
},
child: {
name: 'Speike',//Speike是只狗
f2: function (a,b) {
console.log(a+b+'f2---' + this.name);
},
}
}
var bar = obj.child.f2.bind(obj,'1','2')//使用一個變量去接收返回的結(jié)果
bar()//12f2---Jerry
new綁定
new
是在通過構(gòu)造函數(shù)創(chuàng)建實(shí)例對象時使用,此時this指向的是新建的實(shí)例對象鹿榜,還是來一個案例吧比較簡潔明了海雪;
function Cat(name, age) {
this.name = name
this.age = age
this.showInfo = function () {
console.log(this.name + '---' + this.age)
}
}
var c1 = new Cat('Tom', 5)
c1.showInfo()//Tom---5
- 要理解new綁定,需要知道new創(chuàng)建實(shí)例對象時做了些什么
- 創(chuàng)建一個空對象obj
- 將空對象的隱式原型屬性
__proto__
指向構(gòu)造函數(shù)的顯示原型prototype
- 將this的指向obj
- 返回得到的實(shí)例對象
手寫函數(shù)實(shí)現(xiàn)new的功能
function myNew(p,n,a) {
//創(chuàng)建一個空對象
var obj = {}
var Construct = Array.prototype.shift.call(arguments)
//var Construct=[].shift.call(arguments)
//上面兩段代碼效果是等效的舱殿,都是為了得到Cat這個構(gòu)造函數(shù)奥裸,這一步是難點(diǎn)
//這里arguments的細(xì)節(jié)不是很明白,需要去弄明白
obj.__proto__ = Construct.prototype
Construct.apply(obj,arguments)//更改this的指向
return obj//返回得到的新對象
}
function Cat(name, age) {
this.name = name
this.age = age
this.showInfo = function () {
console.log(this.name + '---' + this.age)
}
}
var c1 = myNew(Cat,'Tom',20)
console.log(c1)//Cat {name: "Tom", age: 20, showInfo: ?}得到了與new一樣的效果
案例小結(jié)
經(jīng)過手寫函數(shù)實(shí)現(xiàn)new的功能沪袭,就能夠理解new綁定this指向的是新建的實(shí)例對象刺彩,在這個過程中發(fā)現(xiàn)對于arguments
理解不透徹,留著下次總結(jié)。
函數(shù)綁定的優(yōu)先級
經(jīng)過上面這一番折騰创倔,可算是把函數(shù)綁定這件是說清楚了嗡害,還有非常重要一點(diǎn)上面這四種函數(shù)綁定方式存在優(yōu)先級:new綁定>顯示綁定>隱式綁定>默認(rèn)綁定
箭頭函數(shù)
箭頭函數(shù)是ES6中新增的一種定義函數(shù)的方式,對于箭頭函數(shù)中的this指向記住以下幾個要點(diǎn):
- 箭頭函數(shù)中沒有自己的this畦攘,箭頭函數(shù)中的this是的定義函數(shù)時上一層函數(shù)中的this霸妹;
- 箭頭函數(shù)中的this在書寫代碼時就已經(jīng)確定,這有別于之前提到的函數(shù)中的this是在調(diào)用時確定的知押;
- 箭頭函數(shù)中的沒有call()叹螟、apply()、bind()這三個方法台盯,當(dāng)然也就不能更改箭頭函數(shù)中this的指向罢绽;
- 箭頭函數(shù)中沒有構(gòu)造函數(shù),不能使用new綁定静盅。
var name = 'Tom'
var obj = {
name: 'Jerry'
}
; (function () {
setTimeout(() => {
console.log(this.name);
}, 1000)
}).call(obj)//Jerry
//使用call方法改變了上一層函數(shù)中this的指向
關(guān)于this指向問題的總結(jié)就到這里了良价,歡迎評論指正,共同學(xué)習(xí)互相勉勵]锏C鞴浮!
新手小白市咽,大佬勿噴痊银,謝謝!施绎!