先了解原型和原型链的知识,有助于你理解本文的代码。
先来看一些不常用的继承方式(因为都有各自的缺点):
1. 原型链继承
// 父类
function Animal(name){
this.name = name
this.hobbies = ['music', 'dance']
}
Animal.prototype.say = function(){
console.log('hello,my name is', this.name);
}
// 子类
function Dog(name){
this.name = name
}
// 继承
Dog.prototype = new Animal() // 重点代码
let dog1 = new Dog('小黑')
let dog2 = new Dog('小白')
console.log(dog1.name, dog2.name); // 小白 小黑
dog1.hobbies.push('eat')
console.log(dog1.hobbies, dog2.hobbies); // ['music','dance', 'eat'] ['music','dance', 'eat']
通过例子可以发现,该继承方法最大的缺点就是 --- 引用类型的属性会被所有实例共享(因为属性值指向同一个内存地址)。
2. 构造函数继承
// 父类
function Animal(name){
this.name = name
this.hobbies = ['music', 'dance']
this.say = function(){
console.log('hello,my name is ', this.name);
}
}
// 子类
function Dog(name){
Animal.call(this, name) // 重点 相当于把父类代码复制一份放到这
}
let dog1 = new Dog('小黑')
let dog2 = new Dog('小白')
console.log(dog1, dog2); // 小白 小黑
dog1.hobbies.push('eat')
console.log(dog1.hobbies, dog2.hobbies); // ['music','dance', 'eat'] ['music','dance']
console.log(dog1.say === dog2.say); // false
console.log(dog1 instanceof Animal); // false
在子类函数中复制了父类函数,并没有实现子类和父类的继承 所以dog1 instanceof Animal
的值为false, 这种方法可以解决原型链继承带来的引用类型属性共享的问题,但是也正因为解决了引用类型属性共享,导致父类构造函数中的方法也不共享了,也就是说没创建一个实例,就会往这个实例身上添加一个方法,尽管每个实例身上的方法作用都是完全一样的。
看到上面两种各带缺点的继承方法,大家可能会想到,将两者方法组合在一起,不就可以解决两者的缺点了吗? 没错, 将两者结合,就叫做组合继承,确实可以很好的实现继承,但是每次创建一个实例都需要调用两次父类构造函数,是比较消耗内存的。
还有一些其它的继承方法,也都各有优缺点,不再介绍。 下面直接讲讲最常用的一些实现继承的方法吧
圣杯模式
// 雅虎公司提出的写法,利用了闭包
var inherit = (function () {
var Temp = function () {};
return function (father, son) {
Temp.prototype = father.prototype;
son.prototype = new Temp();
son.prototype.constructor = son;
son.prototype.uber = father.prototype;
};
})();
可以使用es5提供Object.create() 来实现继承。
function inheritES5(father, son) {
son.prototype = Object.create(father);
son.prototype.constructor = son;
son.prototype.uber = father.prototype;
}
还可以使用es6新增的Object api -- Object.setPrototypeOf()
function inheritES6(father, son) {
Object.setPrototypeOf(son.prototype, father.prototype)
}
最后,就是利用es6提出extends
配合super
实现继承
class Animal {
constructor(type, name,age) {
if(new.target === Animal) { // 一般父类不允许直接创建实例
throw new TypeError('不能直接创建Animal的对象,应该通过子类创建')
}
this.type = type;
this.name = name;
this.age = age;
}
print() {
console.log(`【种类】: ${this.type}`);
console.log(`【名字】:${this.name}`);
console.log(`【年龄】:${this.age}`);
}
}
class Dog extends Animal {
constructor(name, age) {
super('犬类', name, age); // 调用父类的构造函数
this.love = '吃';
}
print(){ // 覆盖父类的同名方法
super.print(); // 先调用父类的方法
console.log(`【爱好】:${this.love}`); // 执行自己特有的操作
}
}
const d = new Dog('小黑', 3);
d.print();