我们来回顾一下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
也不成立