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