原型链实现继承
通过重写子类的原型,并将它指向父类的手段实现。这种方式实现的继承,创建出来的实例既是子类的实例,又是父类的实例。它有如下几种缺陷:
不能向父类构造函数传参;
父类上的引用类型属性会被所有实例共享,其中一个实例改变时,会影响其他实例。
function Animal() {
this.colors = ['red','blue'];
}
function Dog(name) {
this.name = name;
}
Dog.prototype = new Animal();
var dog1 = new Dog('旺财');
var dog2 = new Dog('钢镚');
dog2.colors.push('yellow');
console.log(dog1.colors); // ["red", "blue", "yellow"]
console.log(dog2.colors); // ["red", "blue", "yellow"]
console.log(dog1 instanceof Dog); // true
console.log(dog1 instanceof Animal);// true
构造函数实现继承
借用构造函数实现继承,通过在子类中使用call()方法,实现借用父类构造函数并向父类构造函数传参的目的。但这种方法,无法继承父类原型对象上的属性和方法。
function Animal(name) {
this.name = name;
this.colors = ['red','blue'];
}
Animal.prototype.eat = function() {
console.log(this.name + ' is eating!');
}
function Dog(name) {
Animal.call(this,name);
}
var dog1 = new Dog('旺财');
var dog2 = new Dog('钢镚');
dog2.colors.push('yellow');
console.log(dog1.colors); // ["red", "blue"]
console.log(dog2.colors); // ["red", "blue", "yellow"]
console.log(dog1 instanceof Dog); // true
console.log(dog2 instanceof Animal);// false
console.log(dog1.eat()); // 报错
组合继承
组合继承是组合了原型链继承和借用构造函数继承这两种方法,它保留了两种继承方式的优点,但它并不是百分百完美的:父类构造函数被调用多次。
function Animal(name) {
this.name = name;
this.colors = ['red','blue'];
}
Animal.prototype.eat = function() {
console.log(this.name + ' is eatting');
}
function Dog(name) {
Animal.call(this,name);
}
Dog.prototype = new Animal(); // 第一次调用
var dog1 = new Dog('dog1'); // 第二次调用
var dog2 = new Dog('dog2'); // 第三次调用
dog1.colors.push('yellow');
console.log(dog1.name); // 输出dog1
console.log(dog2.colors);// 输出['red','blue']
console.log(dog2.eat()); // 输出dog2 is eatting
寄生组合继承
寄生组合继承是在组合继承的基础上,采用Object.create()方法来改造实现。
function Animal(name) {
this.name = name;
this.colors = ['red','blue'];
}
Animal.prototype.eat = function() {
console.log(this.name + ' is eatting');
}
function Dog(name) {
Animal.call(this,name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
var dog1 = new Dog('dog1');
var dog2 = new Dog('dog2');
dog1.colors.push('yellow');
console.log(dog1.name); // 输出dog1
console.log(dog2.colors);// 输出['red','blue']
console.log(dog2.eat()); // 输出dog2 is eatting
Class实现继承
1.ES6 class 内部所有定义的方法都是不可枚举的;
2.ES6 class 必须使用 new 调用;
3.ES6 class 不存在变量提升;
4.ES6 class 默认即是严格模式;
5.ES6 class 子类必须在父类的构造函数中调用super(),这样才有this对象;ES5中类继承的关系是相反的,先有子类的this,然后用父类的方法应用在this上。
class Animal {
constructor(name) {
this.name = name;
this.colors = ['red','blue'];
}
eat() {
console.log(this.name + ' is eatting');
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
}
var dog1 = new Dog('dog1');
var dog2 = new Dog('dog2');
dog1.colors.push('yellow');
console.log(dog1.name); // 输出dog1
console.log(dog2.colors);// 输出['red','blue']
console.log(dog2.eat()); // 输出dog2 is eatting
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。