继承 inherit
class 是对原型继承的一种语法糖的包装。那相对于原型继承,它有什么优点呢?
我们来先看一个典型的基于原型链继承的例子。部分内容来自“Javascript 高级程序设计”
function SuperType() {this.property = true;}
SuperType.prototype.getSuperValue = function() {return this.property;}
function SubType() {this.subProperty = false;}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {return this.subProperty;}
var instance = new SubType();
console.log(instance.getSuperValue()); // true
console.log(instance instanceof Object); // true
console.log(instance instanceof SuperType); // true
console.log(instance instanceof SubType); // true
问题,当包含引用类型的值。
function SuperType() {this.colors = ["red", "blue", "green"];
}
function SubType() {}
SubType.prototype = new SuperType();
var instance = new SubType();
instance.colors.push("black");
var instance1 = new SubType();
instance1.colors.push("white");
console.log(instance.colors); // ['red', 'blue', 'green', 'black', 'white']
console.log(instance1.colors); // ['red', 'blue', 'green', 'black', 'white']
解决方案:
- 借用构造函数
function SuperType() {this.colors = ["red", "blue", "green"];
}
function SubType() {SuperType.call(this);
}
SubType.prototype = new SuperType();
var instance = new SubType();
instance.colors.push("black");
var instance1 = new SubType();
instance1.colors.push("white");
console.log(instance.colors);
console.log(instance1.colors);
- 组合继承
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {console.log(this.name);
}
function SubType(name, age) {SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {console.log(this.age);
}
- 寄生组合式继承
function object(o) {function F() {}
F.prototype = o;
return new F();}
function inheritPrototype(subType, superType) {let prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {console.log(this.name);
}
function SubType(name, age) {SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {console.log(this.age);
}
var instance = new SubType("Tom", 70);
instance.colors.push("black");
var instance1 = new SubType("Jerry", 69);
instance1.colors.push("white");
console.log(instance.colors);
console.log(instance.sayName());
console.log(instance.sayAge());
console.log(instance1.colors);
console.log(instance1.sayName());
console.log(instance1.sayAge());
MDN 原型链继承
(欠图一张)
extends
从 es5 来说,实现对象的继承,还是相当麻烦的。而 extends 关键字的出现,使继承变得简单,原型会自动进行调整,super()/super 关键字可以访问父类的构造方法和属性。
class Animal {constructor(name) {this.name = name;}
speak() {console.log(this.name + 'makes a noise.');
}
}
class Dog extends Animal {speak() {console.log(this.name + 'barks.');
}
}
var d = new Dog('Mitzie');
d.speak();// 'Mitzie barks.'
分析:Dog 类没有构造函数,这样合理吗?
// 等价于上个类定义
class Dog extends Animal {constructor(name) {super(name)
}
speak() {console.log(this.name + 'barks.');
}
}
super() 方法调用注意:
- 只可在以 extends 实现的派生类中的 constructor 方法中调用,在非派生类或方法中直接调用,会报错。
- 在 constructor 中访问 this 之前,一定要先调用 super(), 因为它负责初始化 this, 如果在 super() 调用之前尝试访问 this,会报错。
- 如果不想调用 super(),则唯一的方法是让类的 constructor() 返回一个对象。
类方法遮蔽
说明:派生类中的方法总会覆盖基类中的同名方法。
class Animal {constructor(name) {this.name = name;}
speak() {console.log(this.name + 'makes a noise.');
}
}
class Dog extends Animal {speak() {console.log(this.name + 'barks.');
}
}
// 基类中的 speak() 方法被覆盖
静态类成员继承
说明:如果基类有静态成员,那么这些静态成员在派生类中也可用。
class Animal {constructor(name) {this.name = name;}
speak() {console.log(this.name + 'makes a noise.');
}
static create(name) {return new Animal(name);
}
}
class Dog extends Animal {speak() {console.log(this.name + 'barks.');
}
}
let a1 = Animal.create("Monkey");
let a2 = Dog.create("BeijinDog");
console.log(a1 instanceof Animal); // true
console.log(a2 instanceof Animal); // true
console.log(a2 instanceof Dog); // false 这个是不是很意外?