JS 裡為什麼會有 this

飢人谷前端發表於2017-12-14

這篇文章是從語言創造者(JS 之父的角度)來思考 this,我之前那篇講 this 的文章是從使用者的角度寫的。

本文寫得並不好,因為這個概念太抽象,用視訊講會更好懂。

需求

假設我們有一個物件

var person = {
    name: 'Frank',
    age: 18,
    phone: '13812345678',
    sayHi: function(){
      // 待補充
    },
    sayBye: function(){
      // 待補充
    }
}
複製程式碼

這個 person 物件有 name 和 age 屬性,還有一個 sayHi 方法,現在的需求是:

呼叫 person.sayHi(...),列印出「你好,我是 Frank,今年 18 歲」。

呼叫 person.sayBuy(...),列印出「再見,記得我叫 Frank

哦,想約我的話打電話給我,我的電話是 13812345678」

需求就是這麼簡單,通過達成這個需求,我們就能理解 this 的本質。

最傻的方案

var person = {
    ...
    sayHi: function(name, age){
      console.log('你好,我是 ${name},今年 ${age} 歲')
    },
    sayBye: function(name, phone){
      console.log('再見,記得我叫 ${name}哦,想約我的話打電話給我,我的電話是 ${phone}')
}
}
複製程式碼

呼叫方式是

person.sayHi(person.name, person.age)
person.sayBye(person.name, person.phone)
複製程式碼

別急,我知道這程式碼很傻,接下來改進。

第一次改進

上面方法中,每次都要在呼叫的時候自行選擇 person.name 作為引數,真的很傻,不如直接傳入一個 person,程式碼如下:

var person = {
    ...
    sayHi: function(self){
     console.log('你好,我是 ${self.name},今年 ${self.age} 歲')
    },
    sayBye: function(self){
      console.log('再見,記得我叫 ${self.name} 哦,想約我的話打電話給我,我的電話是 ${self.phone}')
    }
}
複製程式碼

呼叫方式是

person.sayHi(person)
person.sayBye(person)
複製程式碼

稍微好一點了,但是這程式碼依然很傻。 為什麼不能把引數裡的 person 省掉,變成 person.sayHi() 就好了。

加糖

開發者最想要的呼叫方式是 person.sayHi(),那麼問題來了,如果 person.sayHi() 沒有實參,person.sayHi 函式是如何接收到 person 的呢?

方法1:依然把第一個引數 self 當做 person,這樣形參就會永遠比實參多出一個 self

方法2:隱藏 self,然後用關鍵字 this 來訪問 self。 JS 之父選擇了方法2,用 this 訪問 self。Python 之父選擇了方法1,留下 self 作為第一個引數。

過程如下:

// 用 this 之前:
sayHi: function(self){
    console.log('你好,我是 ${self.name},今年 ${self.age} 歲')
}
// 用 this 之後:
sayHi: function(){
   // this 就是 self
console.log('你好,我是 ${this.name},今年 ${this.age} 歲')
}
複製程式碼

費解

用了 this 之後,完整程式碼如下:

var person = {
    name: 'Frank',
    age: 18,
    phone: '13812345678',
    sayHi: function(){
     console.log('你好,我是 ${this.name},今年 ${this.age} 歲')
    },
    sayBye: function(){
      console.log('再見,記得我叫 ${this.name} 哦,想約我的話打電話給我,我的電話是 ${this.phone}')
   }
}
複製程式碼

現在輪到新手疑惑了,這個 this 到底是個啥玩意兒?從哪來的呀?

實際上 this 是隱藏的第一個形參。在你呼叫 person.sayHi() 時,這個 person 會「變成」 this。

高手的不滿

很多 JS 高手不屑於用 this,覺得 this 不夠單純。所以 JS 之父給高手們準備了無糖的 .call 方法。

person.sayHi.call(person) 就等價於 person.sayHi()

.call 的第一個引數就是顯式的 person,沒有任何語法糖。

所以高手一般用 obj.fn.call(null,1,2,3) 來手動禁用 this。

這樣一來 person.sayHi.call 的引數其實可以是任何物件。

也就是說 person.sayHi 雖然是 person 的方法,但是是可以呼叫在任何物件上的。

物件與函式

JS 沒有類沒有方法,只有物件和函式。

JS 加了 class 關鍵字之後,勉強弄出來一個假的類。

this 是連線物件和函式的橋樑。

相比於 JS,我更喜歡 Python 的方式,不使用 this 關鍵字,而是使用 self 形參,更易懂。

ps:本週小編邀請了幾位入職bilibili,美團,阿里,百度,京東,51信用卡的同學來谷裡分享面試經驗,加微信astak10,聽聽他們面試的那些故事

相關文章