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 也不成立

猜你喜欢

转载自blog.csdn.net/qq_26632807/article/details/81215771