继承 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 这个是不是很意外?
发表回复