JavaScript中的this使用詳解
其實(shí)this是一個(gè)老生常談的問(wèn)題了。關(guān)于this的文章非常多,其實(shí)我本以為自己早弄明白了它,不過(guò)昨天在做項(xiàng)目的過(guò)程中,還是出現(xiàn)了一絲疑惑,想到大概之前在JavaScript weekly里收藏待看的一篇詳解this的文章(后有鏈接,也附上了稀土上的中文譯文)和另一篇一位前輩推薦的文章,就把它們看了看,對(duì)this的認(rèn)識(shí)確實(shí)提升了一些。
JavaScript 中的'this‘是動(dòng)態(tài)的,它在函數(shù)運(yùn)行時(shí)被確定而非在函數(shù)聲明時(shí)被確定。所有的函數(shù)都可以調(diào)用'this',這無(wú)關(guān)于該函數(shù)是否屬于某個(gè)對(duì)象。關(guān)于this,主要有以下四種情況。
1.被當(dāng)做對(duì)象的方法被調(diào)用
如果該函數(shù)是被當(dāng)做某一個(gè)對(duì)象的方法,那么該函數(shù)的this指向該對(duì)象;
var john = { firstName: "John" } function func() { alert(this.firstName + ": hi!") } john.sayHi = func john.sayHi() // this = john
這里有一點(diǎn)值得注意,當(dāng)一個(gè)對(duì)象的方法被取出來(lái)賦值給一個(gè)變量時(shí),該方法變?yōu)楹瘮?shù)觸發(fā),this指向window或underfind(嚴(yán)格模式)。
2.函數(shù)之內(nèi)調(diào)用
當(dāng)函數(shù)中有 this,其實(shí)就意味著它被當(dāng)做方法調(diào)用,之間調(diào)用相當(dāng)于把他當(dāng)做window對(duì)象的方法,this指向window,值得注意的是ES5其實(shí)是規(guī)定這種情況this=undefined的,只瀏覽器大多還是按照老的方法執(zhí)行(本人在最新版的Chrome,Safari,F(xiàn)irefox中測(cè)試都指向window(201607)),在火狐下使用嚴(yán)格模式指向undefined;
func() function func() { alert(this) // [object Window] or [object global] or kind of.. }
為了傳遞this,()之前應(yīng)該為引用類(lèi)型,類(lèi)似于obj.a 或者 obj['a'],不能是別的了。
這里還存在一個(gè)小坑,當(dāng)對(duì)象的方法中還存在函數(shù)時(shí),該函數(shù)其實(shí)是當(dāng)做函數(shù)模式觸發(fā),所以其this默認(rèn)為window(嚴(yán)格模式下為undefined)解決辦法是給該函數(shù)綁定this。
var numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { // this is window or undefined in strict mode console.log(this === numbers); // => false return this.numberA + this.numberB; } return calculate(); } }; numbers.sum(); // => NaN or throws TypeError in strict mode var numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { console.log(this === numbers); // => true return this.numberA + this.numberB; } // use .call() method to modify the context return calculate.call(this); } }; numbers.sum(); // => 15
3.在new中調(diào)用
一個(gè)引用對(duì)象的變量實(shí)際上保存了對(duì)該對(duì)象的引用,也就是說(shuō)變量實(shí)際保存的是對(duì)真實(shí)數(shù)據(jù)的一個(gè)指針。
使用new關(guān)鍵字時(shí)this的改變其實(shí)有以下幾步:
創(chuàng)建 this = {}.
new執(zhí)行的過(guò)程中可能改變this,然后添加屬性和方法;
返回被改變的this.
function Animal(name) { this.name = name this.canWalk = true } var animal = new Animal("beastie") alert(animal.name)
需要注意的是如果構(gòu)造函數(shù)返回一個(gè)對(duì)象,那么this指向返回的那個(gè)對(duì)象;
function Animal() { this.name = 'Mousie'; this.age = '18'; return { name: 'Godzilla' } // <-- will be returned } var animal = new Animal() console.log(animal.name) // Godzilla console.log(animal.age)//undefined
這里需要注意的是不要忘記使用new,否則不會(huì)創(chuàng)建一個(gè)新的函數(shù)。而是只是執(zhí)行了函數(shù),相當(dāng)于函數(shù)調(diào)用,this其實(shí)指向window
function Vehicle(type, wheelsCount) { this.type = type; this.wheelsCount = wheelsCount; return this; } // Function invocation var car = Vehicle('Car', 4); car.type; // => 'Car' car.wheelsCount // => 4 car === window // => true
4.明確調(diào)用this,使用call和apply
這是最具JavaScript特色的地方。
如下代碼:
func.call(obj, arg1, arg2,...)
第一個(gè)參數(shù)將作為this的指代對(duì)象,之后的參數(shù)將被作為函數(shù)的參數(shù),解決方法是使用bind。
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = function() { console.log(this === myCat); // => true console.log('The ' + this.type + ' has ' + this.legs + ' legs'); }; } var myCat = new Animal('Cat', 4); // logs "The Cat has 4 legs" setTimeout(myCat.logInfo.bind(myCat), 1000); // setTimeout?? var john = { firstName: "John", surname: "Smith" } function func(a, b) { alert( this[a] + ' ' + this[b] ) } func.call(john, 'firstName', 'surname') // "John Smith"
至于apply,其只是以數(shù)組的方傳入?yún)?shù),其它部分是一樣的,如下:
func.call(john, 'firstName', 'surname') func.apply(john, ['firstName', 'surname'])
它們也可用于在 ES5 中的類(lèi)繼承中,調(diào)用父級(jí)構(gòu)造器。
function Runner(name) { console.log(this instanceof Rabbit); // => true this.name = name; } function Rabbit(name, countLegs) { console.log(this instanceof Rabbit); // => true // 間接調(diào)用,調(diào)用了父級(jí)構(gòu)造器 Runner.call(this, name); this.countLegs = countLegs; } var myRabbit = new Rabbit('White Rabbit', 4); myRabbit; // { name: 'White Rabbit', countLegs: 4 }
5..bind()
對(duì)比方法 .apply() 和 .call(),它倆都立即執(zhí)行了函數(shù),而 .bind() 函數(shù)返回了一個(gè)新方法,綁定了預(yù)先指定好的 this ,并可以延后調(diào)用。
.bind() 方法的作用是創(chuàng)建一個(gè)新的函數(shù),執(zhí)行時(shí)的上下文環(huán)境為 .bind() 傳遞的第一個(gè)參數(shù),它允許創(chuàng)建預(yù)先設(shè)置好 this 的函數(shù)。
var numbers = { array: [3, 5, 10], getNumbers: function() { return this.array; } }; // Create a bound function var boundGetNumbers = numbers.getNumbers.bind(numbers); boundGetNumbers(); // => [3, 5, 10] // Extract method from object var simpleGetNumbers = numbers.getNumbers; simpleGetNumbers(); // => undefined or throws an error in strict mode
使用.bind()時(shí)應(yīng)該注意,.bind() 創(chuàng)建了一個(gè)永恒的上下文鏈并不可修改。一個(gè)綁定函數(shù)即使使用 .call() 或者 .apply()傳入其他不同的上下文環(huán)境,也不會(huì)更改它之前連接的上下文環(huán)境,重新綁定也不會(huì)起任何作用。
只有在構(gòu)造器調(diào)用時(shí),綁定函數(shù)可以改變上下文,然而這并不是特別推薦的做法。
6.箭頭函數(shù)
箭頭函數(shù)并不創(chuàng)建它自身執(zhí)行的上下文,使得 this 取決于它在定義時(shí)的外部函數(shù)。
箭頭函數(shù)一次綁定上下文后便不可更改,即使使用了上下文更改的方法:
var numbers = [1, 2]; (function() { var get = () => { console.log(this === numbers); // => true return this; }; console.log(this === numbers); // => true get(); // => [1, 2] // 箭頭函數(shù)使用 .apply() 和 .call() get.call([0]); // => [1, 2] get.apply([0]); // => [1, 2] // Bind get.bind([0])(); // => [1, 2] }).call(numbers);
這是因?yàn)榧^函數(shù)擁有靜態(tài)的上下文環(huán)境,不會(huì)因?yàn)椴煌恼{(diào)用而改變。因此不要使用箭頭函數(shù)定義方法
function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = () => { console.log(this === window); // => true return this.hours + ' hours and ' + this.minutes + ' minutes'; }; var walkPeriod = new Period(2, 30); walkPeriod.format(); // => 'undefined hours and undefined minutes'
參考
Gentle explanation of 'this' keyword in JavaScript
強(qiáng)烈推薦覺(jué)得沒(méi)弄明白的同學(xué)看看上面三篇文章,其中第三篇是第二篇的譯文。如果大家對(duì)this還有疑問(wèn),也歡迎大家一起討論,交流促進(jìn)思考,共同進(jìn)步。
相關(guān)文章
JS實(shí)現(xiàn)點(diǎn)擊下拉菜單把選擇的內(nèi)容同步到input輸入框內(nèi)的實(shí)例
下面小編就為大家分享一篇JS實(shí)現(xiàn)點(diǎn)擊下拉菜單把選擇的內(nèi)容同步到input輸入框內(nèi)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01調(diào)用jQuery滑出效果時(shí)閃爍的解決方法
這篇文章主要介紹了在調(diào)用jQuery 滑出效果時(shí),層會(huì)現(xiàn)次閃爍一下的解決方法,需要的朋友可以參考下2014-03-03javascript事件處理模型實(shí)例說(shuō)明
本文主要介紹IE和火狐在添加刪除事件上的區(qū)別,并給出通用的解決方法,需要的朋友可以參考下。2016-05-05JavaScript 保護(hù)變量不被隨意修改的實(shí)現(xiàn)代碼
本文通過(guò)實(shí)例代碼給大家分享JavaScript 保護(hù)變量不被隨意修改的實(shí)現(xiàn)方法,需要的朋友參考下吧2017-09-09js實(shí)現(xiàn)雙擊單元格變成文本輸入框效果代碼
單擊單元格,即可將其變?yōu)槲谋究?,方便編輯測(cè)試2008-04-04JavaScript使用小插件實(shí)現(xiàn)倒計(jì)時(shí)的方法講解
今天小編就為大家分享一篇關(guān)于JavaScript使用小插件實(shí)現(xiàn)倒計(jì)時(shí)的方法講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03