前言
JavaScript中this指向一直都是剛接觸JavaScript或者已經有一兩年JavaScript程式設計經驗人員容易弄混或者記不清的地方。this的複雜性主要是由this只有在呼叫時才能確定其值而不是在宣告時和主流認為JavaScript語言設計缺陷導致。this在瀏覽器和node環境的細微差別,更加增加了理解難度。在JavaScript語言精粹中作者從方法呼叫,函式呼叫,構造器呼叫和類apply呼叫分別講解了this的繫結。本文也會從這幾方面介紹this,同時會介紹node,瀏覽器環境的輕微不同來幫助讀者理解,也方便自己以後複習。
方法呼叫
當一個函式儲存為物件的一個屬性時,我們稱它為一個方法。當一個方法被呼叫時,this就繫結到該物件上。
var obj = {
name:'joker',
sayName:function(){
console.log(this.name) // joker
}
}
obj.sayName()
複製程式碼
方法可以使用this訪問自己所屬的物件。this到物件的繫結發生在方法被呼叫時,而不是在我們定義obj物件時候。
函式呼叫
函式呼叫的模式可能是this變得複雜的最終原因。本章節我們將從瀏覽器和node環境來講解this的繫結
函式呼叫之瀏覽器環境
注意以下例項都需要在瀏覽器環境中運營,不然結果可能不一致。首先我們看看簡單函式一般的呼叫方法
var name = 'joker'
function sayName(){
console.log(this.name)
}
sayName(joker) // joker
複製程式碼
很顯然this物件被繫結到了全域性window物件上,相信大家沒有什麼疑惑的。但是程式設計實際中,一般不會存在這麼簡單的函式,JavaScript中很多函式的執行都會返回一個新的函式,然後執行新的函式(是不是感覺很熟悉,其實我們可以把新的函式理解成閉包,閉包就不展開了,有興趣可以自行google)
var number = 1
var obj = {
number:999,
f1:function(){
return function(){
console.log(this.number)
}
}
}
obj.f1()() // 1
複製程式碼
此時this還是被繫結了全域性物件,大多數人認為這是語言上的設計錯誤。倘若正確設計內部函式被呼叫時,this應該繫結到外部函式this。一般為了解決這種this繫結問題,可以在外部函式中執行 var that = this.將this賦予給變數that(變數名不一定要取that)然後在內部函式中引用that。之所以that可以訪問到我們期望的值,是因為我們在外部函式定義了變數that,當我們在內部函式訪問that時JavaScript引擎會通過作用域鏈來尋找變數that,最終在父作用域中找到。另外我們也可通過箭頭函式來保證訪問到正確的值,就不列出程式碼了。
var number = 1
var obj = {
number:999,
f1:function(){
var that = this
return function(){
console.log(that.number)
}
}
}
obj.f1()() //999
複製程式碼
函式呼叫之node環境
在node環境中,this的指向有輕微的一些差別。首先在node環境中,我們需要認識到以下幾點
- 全域性物件不是window而是global
- Node在內部採用的是commonjs規範,所有程式碼都會執行在模組作用域中,而不是全域性作用域(使用者程式碼都會被node包裹在一個函式中,從而不會汙染全域性作用域)
由於node中對程式碼的特殊處理,使得全域性物件,模組中this指向都會和瀏覽器存在一些不同
- 模組中頂層this===module.exports
- 全域性變數的宣告必須通過global顯示宣告
console.log(this===module.exports) // true
var number = 1
global.number = 2
var obj = {
number:999,
f1:function(){
return function(){
// node中此處的this依然會指向全域性物件global,
// 而不是當前模組作用域的this(module.exports)
console.log(this.number,this===global) // 2 true
}
}
}
obj.f1()()
複製程式碼
可以看到除了全域性物件,node模組中頂層this===module.exports之外,其餘和瀏覽器環境中沒有什麼不同。
建構函式呼叫
一個函式如果建立的目的就是希望結合new
使用,那麼就被稱為建構函式,按照約定一般函式名首字母要大寫從而和普通函式進行區別。如果new呼叫建構函式,this會繫結到那個新物件上
function People(name){
this.name = name
}
var people = new People('joker')
console.log(people.name) // joker
複製程式碼
類apply呼叫方式
因為JavaScript是一門函式式的物件導向程式語言,函式可以擁有方法,apply,call,bind方法都可以讓我們手動指定this的值。
var obj = {
name:'joker'
}
function sayName(){
console.log(this.name) // joker
}
sayName.apply(obj)
複製程式碼
如果採用一般函式呼叫方式,this會指向全域性物件。但是我們可以通過apply方式指定this值來呼叫函式。