類
我們來回顧一下ES6 / TypeScript / ES5
類的寫法以作對比。首先,我們創建一個GithubUser
類,它擁有一個login
方法,和一個靜態方法getPublicServices
, 用於獲取public
的方法列表:
class GithubUser {
static getPublicServices() {
return ['login']
}
constructor(username, password) {
this.username = username
this.password = password
}
login() {
console.log(this.username + '要登錄Github,密碼是' + this.password)
}
}
複製代碼
實際上,ES6
這個類的寫法有一個弊病,實際上,密碼password
應該是Github
用戶一個私有變量,接下來,我們用TypeScript
重寫一下:
class GithubUser {
static getPublicServices() {
return ['login']
}
public username: string
private password: string
constructor(username, password) {
this.username = username
this.password = password
}
public login(): void {
console.log(this.username + '要登錄Github,密碼是' + this.password)
}
}
複製代碼
如此一來,password
就只能在類的內部訪問了。好了,問題來了,如果結合原型講解那一文的知識,來用ES5
實現這個類呢?just show you my code
:
function GithubUser(username, password) {
// private屬性
let _password = password
// public屬性
this.username = username
// public方法
GithubUser.prototype.login = function () {
console.log(this.username + '要登錄Github,密碼是' + _password)
}
}
// 靜態方法
GithubUser.getPublicServices = function () {
return ['login']
}
複製代碼
值得注意的是,我們一般都會把
共有方法
放在類的原型上,而不會採用this.login = function() {}
這種寫法。因爲只有這樣,才能讓多個實例引用同一個共有方法,從而避免重複創建方法的浪費。
是不是很直觀!留下2
個疑問:
- 如何實現
private方法
呢? - 能否實現
protected屬性/方法
呢?
繼承
用掘金的用戶都應該知道,我們可以選擇直接使用 Github
登錄,那麼,結合上一節,我們如果創建了一個 JuejinUser
來繼承 GithubUser
,那麼 JuejinUser
及其實例就可以調用 Github
的 login
方法了。首先,先寫出這個簡單 JuejinUser
類:
function JuejinUser(username, password) {
// TODO need implementation
this.articles = 3 // 文章數量
JuejinUser.prototype.readArticle = function () {
console.log('Read article')
}
}
複製代碼
由於ES6/TS
的繼承太過直觀,本節將忽略。首先概述一下本文將要講解的幾種繼承方法:
看起來很多,我們一一論述。
類式繼承
因爲我們已經得知:
若通過
new Parent()
創建了Child
,則Child.__proto__ = Parent.prototype
,而原型鏈則是順着__proto__
依次向上查找。因此,可以通過修改子類的原型爲父類的實例來實現繼承。
第一直覺的實現如下:
function GithubUser(username, password) {
let _password = password
this.username = username
GithubUser.prototype.login = function () {
console.log(this.username + '要登錄Github,密碼是' + _password)
}
}
function JuejinUser(username, password) {
this.articles = 3 // 文章數量
JuejinUser.prototype = new GithubUser(username, password)
JuejinUser.prototype.readArticle = function () {
console.log('Read article')
}
}
const juejinUser1 = new JuejinUser('ulivz', 'xxx', 3)
console.log(juejinUser1)
複製代碼
在瀏覽器中查看原型鏈:
誒,不對啊,很明顯 juejinUser1.__proto__
並不是 GithubUser
的一個實例。
實際上,這是因爲之前我們爲了能夠在類的方法中讀取私有變量,將JuejinUser.prototype
的重新賦值放在了構造函數中,而此時實例已經創建,其__proto__
還還指向老的JuejinUser.prototype
。所以,重新賦值一下實例的__proto__
就可以解決這個問題:
function GithubUser(username, password) {
let _password = password
this.username = username
GithubUser.prototype.login = function () {
console.log(this.username + '要登錄Github,密碼是' + _password)
}
}
function JuejinUser(username, password) {
this.articles = 3 // 文章數量
const prototype = new GithubUser(username, password)
// JuejinUser.prototype = prototype // 這一行已經沒有意義了
prototype.readArticle = function () {
console.log('Read article')
}
this.__proto__ = prototype
}
const juejinUser1 = new JuejinUser('ulivz', 'xxx', 3)
console.log(juejinUser1)
複製代碼
接着查看原型鏈:
Perfect!原型鏈已經出來,問題“好像”得到了完美解決!但實際上還是有明顯的問題:
- 在原型鏈上創建了屬性(一般來說,這不是一種好的實踐)
- 私自篡改
__proto__
,導致juejinUser1.__proto__ === JuejinUser.prototype
不成立!從而導致juejinUser1 instanceof JuejinUser
也不成立