原型链继承
function Parent2() {
this.name = "parent2";
this.play = [1, 2, 3];
}
function Child2() {
this.type = "child2";
}
Child2.prototype = new Parent2();
let child1 = new Child2();
let child2 = new Child2();
child1.play.push(4);
child1.name='child1'; // 该行为是往 child1 上添加name属性,并不会查找
console.log(child2.name) // "parent2",
console.log(child1.play, child2.play);
缺点
- 需要手动修改 constructor 指向
- 无法调用父类的构造函数传参
- 多个子实例的原型都是指向同一个对象,容易造成污染
构造继承
- Parent1.call(this);会调用Parent方法,将属性绑定到child1上
function Parent1() {
this.name = "parent1";
this.f = function () {
console.log("parent1");
};
}
function Child1() {
Parent1.call(this);
this.type = "child1";
}
let child = new Child1();
child.f();
优点
- 解决了原型链继承中多个子实例的原型都是指向同一个对象,容易造成污染的问题
- 不需要手动修改 constructor 指向
- 可以通过 instanceof 判断来源
缺点
- 因为没有 new 操作无法继承 Parent 原型上的内容
- child 并不算 Parent 真正的子类,通过 instanceof 无法判断
- 根据调用位置可能覆盖子实例的属性,不以子实例属性优先
function Child () {
this.sex = 'boy'
this.name = 'bad boy'
Parent.call(this, 'good boy') // 如果 Parent 内也有 this.name,最终 child.name="good boy"
}
组合继承
- 解决上一步实例属性污染的问题
function Parent (name) {
console.log(name) // 这里有个console.log()
this.name = name
}
Parent.prototype.colors=['red'];
function Child (name) {
Parent.call(this, name)
}
Child.prototype = new Parent()
Child.prototype.constructor = Child;
var child1 = new Child('child1')
优点
- 继承父类原型上的属性和方法
- 父类非原型上的属性和方法隔离
缺点
- 多个子实例的原型都是指向同一个对象,如果子实例修改了父类原型上的属性,会造成污染,但如果修改的是父类非原型上的属性和方法则不会,因为已经复制了一遍到子实例上
- Parent.call(this, name)、new Parent() 会造成父类构造函数调用两次
- 需要手动修改 constructor 指向
寄生组合继承(推荐,和 extends 继承效果一致)
function Parent5() {
this.name = "parent5";
this.play = [1, 2, 3];
}
function Child5() {
Parent5.call(this);
this.type = "child5";
}
// 和组合继承的区别
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;
console.log(new Child5());
优点
- 父类构造函数不会执行两次
- 父类非原型属性和方法隔离
缺点
- 需要手动修改 constructor 指向
- 多个子实例的原型都是指向同一个对象,如果子实例修改了父类原型上的属性,会造成污染,但如果修改的是父类非原型上的属性和方法则不会,因为已经复制了一遍到子实例上
混入寄生组合式继承
- 继承多个父类
function Child () {
Parent.call(this)
OtherParent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
Object.assign(Child.prototype, OtherParent.prototype)
Child.prototype.constructor = Child
function Parent (sex) {
this.sex = sex
}
Parent.prototype.getSex = function () {
console.log(this.sex)
}
function OtherParent (colors) {
this.colors = colors
}
OtherParent.prototype.getColors = function () {
console.log(this.colors)
}
function Child (sex, colors) {
Parent.call(this, sex)
OtherParent.call(this, colors) // 新增的父类
this.name = 'child'
}
Child.prototype = Object.create(Parent.prototype)
Object.assign(Child.prototype, OtherParent.prototype) // 新增的父类原型对象,只是将 OtherParent.prototype 赋值到了 Parent.prototype 上
Child.prototype.constructor = Child
var child1 = new Child('boy', ['white'])
// child1.getSex()
// child1.getColors()
// console.log(child1)
console.log(Child.prototype.__proto__ === Parent.prototype) // true,因为 Object.assign 是浅拷贝
console.log(Child.prototype.__proto__ === OtherParent.prototype)// false
console.log(child1 instanceof Parent) // true
console.log(child1 instanceof OtherParent) // false
- 优缺点和寄生组合继承一致
原型式继承(对象继承)
function create (obj) {
var newObj = {}
newObj.__proto__ = obj
return newObj;
}
优点
- 在不用构造函数和 new 的方式下实现继承
缺点
- 继承对象污染问题
寄生式继承(对象继承)
var cat = {
heart: '❤️',
colors: ['white', 'black']
}
function createAnother (original) {
var clone = Object.create(original);
clone.actingCute = function () {
console.log('我是一只会卖萌的猫咪')
}
return clone;
}
var guaiguai = createAnother(cat)
- 优缺点和原型式继承一致