在 ES6 实现class
关键字之前,JavaScript 只能通过一些特殊的方式实现继承的概念。
常见的有以下几种实现方式:
- 临时变量
call
和apply
改变上下文- 原型链
- mixin
尽管前两种能够获得父类的属性和方法,但是childreni_instance instanceof parent_instance
的结果为false
使用原型链实现继承:
function Parent() {
this.sayAge = function () {
console.log(this.age);
};
}
function Child(firstname) {
this.fname = firstname;
this.age = 40;
this.saySomeThing = function () {
console.log(this.fname);
this.sayAge();
};
}
Child.prototype = new Parent();
let child = new Child("zhang");
child.saySomeThing(); // zhang 40
console.log(child instanceof Parent); // true
这种实现方式,中每个Parent
实例的sayAge()
方法实际上应该是共用的。
可以通过使用call
或apply
改变上下文的方式改良。改良如下:
function Parent() {}
Parent.prototype.sayAge = function () {
console.log(this.age);
};
function Child(firstname) {
Parent.call(this);
this.fname = firstname;
this.age = 40;
this.saySomeThing = function () {
console.log(this.fname);
this.sayAge();
};
}
Child.prototype = new Parent();
let child = new Child("zhang");
child.saySomeThing(); // zhang 40
还有点问题:
- 上述实现方式中,
Parent
实际上共创建了Child
实例个数 + 1 个实例。 - 原型链上存在额外的属性
进一步结合 mixin 的方式改良:
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
return Child;
}
function Parent(lastname) {
this.lastname = lastname;
}
Parent.prototype.sayAge = function () {
console.log(this.age);
};
function Child(firstname, lastname) {
Parent.call(this, lastname); // Mock super
this.firstname = firstname;
this.age = 40;
this.saySomeThing = function () {
console.log(this.lastname, this.firstname);
this.sayAge();
};
}
extend(Child, Parent);
var child = new Child("san", "zhang");
child.saySomeThing(); // zhangsan 40