js繼承,這次把你喫透

我們來回顧一下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個疑問:

  1. 如何實現private方法呢?
  2. 能否實現protected屬性/方法呢?

繼承

用掘金的用戶都應該知道,我們可以選擇直接使用 Github 登錄,那麼,結合上一節,我們如果創建了一個 JuejinUser 來繼承 GithubUser,那麼 JuejinUser 及其實例就可以調用 Githublogin 方法了。首先,先寫出這個簡單 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!原型鏈已經出來,問題“好像”得到了完美解決!但實際上還是有明顯的問題:

  1. 在原型鏈上創建了屬性(一般來說,這不是一種好的實踐)
  2. 私自篡改__proto__,導致 juejinUser1.__proto__ === JuejinUser.prototype 不成立!從而導致 juejinUser1 instanceof JuejinUser 也不成立
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章