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