这是我参与更文挑战的第3天,活动详情查看:更文挑战
题目
将下面这个 ES6 的继承用 ES5 代码进行实现
class Parent {
static author = 'LvLin';
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
static getAuthor() {
return Parent.author;
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
sayHello() {
console.log('Hello, I am ' + super.getName());
}
static printAuthor() {
console.log(super.getAuthor());
}
}
实现
原型链实现继承(extends)
所谓「继承」,是面向对象软件技术当中的一个概念。如果一个类 B 继承自一个类 A,那么称 B 为 A 的 子类,称 A 为 B 的父类。继承使子类具有父类的各种属性和方法,从而不需要再编写相同的代码,但是子类可以追加新的属性和方法,也可以覆盖父类的属性和方法。
各个语言都有属于自己的继承机制,JavaScript 的继承是通过原型链实现的。将需要被继承的属性、方法都定义在构造函数的原型 prototype 里。当实例对象在自己身上找不到相关的属性或方法时,会去原型链上寻找父类的属性或方法。深入了解可以参看这篇文章。
let child = new Child()
child.__proto__ === Child.prototype // true
child.__proto__.__proto__ === Parent.prototype // true
child.getName === Parent.prototype.getName // true
暂时不考虑 ES6 类继承中 static、super 部分的实现, ES5 代码如下:
function Parent (name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
}
Parent.prototype.constructor = Parent;
function Child (name, age) {
//... 存在 super 关键字,后续实现
}
Child.prototype = new Parent(); // 这一步可以优化
Child.prototype.constructor = Child;
其中 Child.prototype = new Parent()
这一步,我们只希望让 Child.prototype.__proto__
指向 Parent.prototype
,不希望调用 Parent 构造方法,所以可以适当优化一下:
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
static 实现
静态(static)属性、静态方法,都是定义在 Class 本身的属性,只能通过类调用,供所有实例共享,不被继承,所以不能定义在原型上,定义在构造函数本身,如下:
Parent.getAuthor = 'LvLin';
Parent.getAuthor = function() {
return Parent.author;
}
super 实现
在 ES6 中,super 既可以是函数,也可以是对象,存在两种情况:
- 作为函数调用时,代表父类的构造函数。
- 作为对象时,在普通方法中,指向父类的原型对象(需要注意此时 this 的指向);在静态方法中,指向父类。
所以 ES6 类继承中的 super 可以改写成以下 ES5 代码:
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype.sayHello = function() {
console.log('Hello, I am ' + Parent.prototype.getName().call(this));
}
Child.printAuthor = function() {
console.log(Parent.getAuthor());
}
最终结果
结合以上分析,最终改写的 ES5 代码如下所示:
function Parent (name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
}
Parent.prototype.constructor = Parent;
Parent.author = 'LvLin';
Parent.getAuthor = function() {
return Parent.author;
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.prototype.sayHello = function() {
console.log('Hello, I am ' + Parent.prototype.getName().call(this));
}
Child.printAuthor = function() {
console.log(Parent.getAuthor());
}
测试验证一下:
let child = new Child('LvLin', 18)
child.sayHello() // Hello, I am LvLin
child.getName() // "LvLin"
Child.printAuthor() // LvLin
最后需要注意的是,ES6 的类继承,其实存在两条原型链,类之间也是存在继承关系,尝试一下:
Child.__proto__ === Parent // true
参考
Class 的基本语法,Class 的继承,by 阮一峰