js中的this作用域全解析
this作用域問題
一般來說,誰調(diào)指誰是一個(gè)基本原則,但是js和其他面向?qū)ο笳Z言稍微有些區(qū)別,雖然從某種程度上,這有助于幫我們判斷this指向問題,但是并不能涵蓋所有的情況,建議從函數(shù)調(diào)用的5種方式展開
- 1.函數(shù)式調(diào)用
- 2.方法調(diào)用模式
- 3.構(gòu)造函數(shù)調(diào)用模式(this指向?qū)嵗?/li>
- 4.call、apply、bind
- 5.特殊情況——箭頭函數(shù)
函數(shù)式調(diào)用
var age = 10 var person = { age:12, say(){ function f(){ console.log(this.age) } f() } } person.say()// 10
以此模式調(diào)用函數(shù)時(shí),this被綁定到全局對(duì)象。這是語言設(shè)計(jì)上的一個(gè)錯(cuò)誤。倘若語言設(shè)計(jì)正確,那么當(dāng)內(nèi)部函數(shù)被調(diào)用時(shí),this應(yīng)該仍然綁定到外部函數(shù)的this變量。這個(gè)設(shè)計(jì)錯(cuò)誤的后果就是方法不能利用內(nèi)部函數(shù)來幫助它工作,因?yàn)閮?nèi)部函數(shù)的this被綁定了錯(cuò)誤的值(全局對(duì)象),所以不能共享該方法對(duì)對(duì)象的訪問權(quán)。
這種情況是不遵從所謂誰調(diào)指誰原則的
解決方法,內(nèi)部函數(shù)外使用一個(gè)變量保存this
var age = 10 var person = { age:12, say(){ var that = this function f(){ console.log(that.age) } f() } } person.say()// 12
方法調(diào)用模式
當(dāng)一個(gè)函數(shù)被保存為對(duì)象的一個(gè)屬性時(shí),我們稱它為一個(gè)方法。當(dāng)一個(gè)對(duì)象的方法被調(diào)用時(shí),this被綁定到調(diào)用方法的對(duì)象。
var age = 10 var person = { age:12, say(){ console.log(this.age) } } person.say()// 12
這就有點(diǎn)像所謂的誰調(diào)指誰
在數(shù)組中的特例
var arr=[ function(){ console.log(this) }, 1, 2, ] arr[0]() //輸出結(jié)果 [f, 1, 2]
很顯然,數(shù)組其實(shí)也是對(duì)象,雖然這種調(diào)用方式在寫法上可能更加貼近函數(shù)調(diào)用模式,但是從打印結(jié)果來看,這很顯然屬于方法調(diào)用模式有點(diǎn)類似于arr.0()
反過來想,方法調(diào)用模式也可以寫成
person['say']()
構(gòu)造器調(diào)用模式
如果在一個(gè)函數(shù)前面帶上 new 關(guān)鍵字來調(diào)用,那么背地里將會(huì)創(chuàng)建一個(gè)連接到該函數(shù)的prototype成員的新對(duì)象,同時(shí)this會(huì)被綁定到那個(gè)新對(duì)象上。
function foo(){ console.log('this is' + this) } new foo() // this is [object]
new 前綴也會(huì)改變r(jià)eturn 語句的行為,如果return 的值是對(duì)象,那么將會(huì)將這個(gè)對(duì)象返回,否則將返回默認(rèn)創(chuàng)建的新對(duì)象。
function foo(){ console.log('this is ' + this.a) const obj = { a:1 } return obj } const f1 = new foo() console.log(f1.a) // this is undefied // 1
新知識(shí)點(diǎn),買一送一
這里可以看到,雖然返回的實(shí)例中確實(shí)有屬性a,但是在代碼執(zhí)行上仍然存在先后順序
call、apply、bind
沒什么好講的,更改作用域
特殊情況——箭頭函數(shù)
箭頭函數(shù)作為函數(shù)式調(diào)用設(shè)計(jì)失誤的解決方案而提出(例a如下)
var age = 10 var person = { age:12, say(){ var f=()=>{ console.log(this.age) } f() } } person.say()// 12
箭頭函數(shù)的特點(diǎn)中有一點(diǎn)是函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。
所以call、apply、bind對(duì)箭頭函數(shù)是不起作用的
var age = 10 var person = { age:12, say(){ var f=()=>{ console.log(this.age) } f.call({age:9}) } } person.say()// 12
再看下面的例子
var age = 10 var person = { age:12, say:()=>{ console.log(this.age) } } person.say()// 10
這是因?yàn)閷?duì)象不構(gòu)成單獨(dú)的作用域,導(dǎo)致say箭頭函數(shù)定義時(shí)的作用域就是全局作用域。阮一峰的es6教程中也有相關(guān)內(nèi)容。
最后一個(gè)例子
var age = 10 var person = { age:12, say:()=>{ console.log(this.age) } } var person1 = { age:13, say(){ var f=person.say f() } } person1.say()// 10
這個(gè)例子看起來和例a很像,內(nèi)層函數(shù)f是一個(gè)箭頭函數(shù),雖然是person的一個(gè)屬性,那么結(jié)果是不是13呢,然而并不是,還是緊扣箭頭函數(shù)特征:函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。
該例中,person.say定義時(shí)在person內(nèi),對(duì)象不構(gòu)成單獨(dú)作用域,所以person.say定義時(shí)的作用域是全局作用域,所以f被調(diào)用時(shí)打印的結(jié)果為全局變量age=10
綜合例題
var length = 10; function fn(){ console.log(this.length) } var obj = { length: 5, method: function(fn){ fn() arguments[0]() } } obj.method(fn, 1) // 輸出結(jié)果 // 10 // 2 // 如果注釋掉第一行代碼 輸出結(jié)果 // 0 // 2
(1)首先如果不注釋第一行代碼,obj調(diào)用自己的method方法,傳了兩個(gè)參數(shù),第一個(gè)是在全局定義的函數(shù)fn,第二個(gè)參數(shù)是number類型的常量
進(jìn)入method,立即調(diào)用fn,因?yàn)閒n是一個(gè)函數(shù)式調(diào)用,所以this指向全局,也就是window,打印結(jié)果是10
再看接下來執(zhí)行的arguments[0](),arguments是一個(gè)數(shù)組,表示函數(shù)的實(shí)參列表,也就是說這個(gè)數(shù)組中包含兩個(gè)元素,第一個(gè)是傳進(jìn)去的fn,第二個(gè)是傳進(jìn)去的常量1,然后它調(diào)用了第0個(gè)元素表示的函數(shù),所以這里是方法調(diào)用模式中的特例,作為數(shù)組元素被調(diào)用,this指向數(shù)組本身,我們經(jīng)常會(huì)忽略數(shù)組中其實(shí)默認(rèn)是有l(wèi)ength屬性的,即使在寫for循環(huán)的時(shí)候經(jīng)常會(huì)用,數(shù)組長(zhǎng)度為2,所以打印結(jié)果是2
(2)如果注釋了第一行代碼,不在全局定義length屬性,正常分析,length未定義,返回undefined,那么答案就錯(cuò)了,事實(shí)上,全局window是有l(wèi)ength屬性的,默認(rèn)值為0
這就是這道題隱藏最深的點(diǎn)了
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript錯(cuò)誤處理之分析 Uncaught(in promise) error的
在開發(fā)過程中,JavaScript的錯(cuò)誤處理是一個(gè)老生常談的話題,當(dāng)應(yīng)用程序發(fā)生未捕獲的異常時(shí),Uncaught(in promise) error是其中最常見的錯(cuò)誤類型,這篇文章將從多個(gè)方面詳細(xì)闡述這種錯(cuò)誤類型的原因與解決方案,感興趣的朋友一起看看吧2023-12-12layer ui 導(dǎo)入文件之前傳入數(shù)據(jù)的實(shí)例
今天小編就為大家分享一篇layer ui 導(dǎo)入文件之前傳入數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09JavaScript輸出當(dāng)前時(shí)間Unix時(shí)間戳的方法
這篇文章主要介紹了JavaScript輸出當(dāng)前時(shí)間Unix時(shí)間戳的方法,涉及javascript中Date及getTime等函數(shù)操作時(shí)間的使用技巧,需要的朋友可以參考下2015-04-04非常好用的JsonToString 方法 簡(jiǎn)單實(shí)例
這篇文章介紹了非常好用的JsonToString簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下2013-07-07Autocomplete Textbox Example javascript實(shí)現(xiàn)自動(dòng)完成成功
Autocomplete Textbox Example javascript實(shí)現(xiàn)自動(dòng)完成成功...2007-08-08swiperjs實(shí)現(xiàn)導(dǎo)航與tab頁的聯(lián)動(dòng)
這篇文章主要為大家詳細(xì)介紹了swiperjs實(shí)現(xiàn)導(dǎo)航與tab頁的聯(lián)動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12淺談JS獲取元素的N種方法及其動(dòng)靜態(tài)討論
這篇文章主要介紹了淺談JS獲取元素的N種方法及其動(dòng)靜態(tài)討論,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-08-08