原型链继承
实现:子类 prototype 赋值为 父类 instance (实例)。
function Parent() {
this.names = ['kevin', 'daisy'];
}
function Child() {}
// 子类 prototype 赋值为 父类 instance (实例)。
Child.prototype = new Parent();
// 手动修改 prototype.constructor = 子类
Child.prototype.constructor = Child
缺点:
- 所有子类实例,共享可修改的原型属性。
- 子类
new时不能向 父类 构造函数 传参。 - 要手动修改
prototype.constructor为子类。
let a = new Child();
a.names.push('yayu');
console.log(a.names); // ["kevin", "daisy", "yayu"]
let b = new Child();
console.log(b.names); // ["kevin", "daisy", "yayu"]
借用构造函数(经典继承)
实现:子类 调用 父类构造函数。
function Parent() {
this.names = ['1', '2'];
this.getNames = () => this.names;
}
Parent.prototype.protoGetNames = function () {}
function Child() {
// 子类 调用 父类构造函数。
Parent.call(this);
}
优点:
- 属性定义在
this,避免共享。 - 可以向 父类 构造函数 传参。
缺点:
- 父类方法,不能通过
prototype访问。 - 方法必须在父类构造函数中定义,且每次创建实例都会创建一遍方法。
// a
let a = new Child();
a.names.push('3');
console.log(a.names); // ["1", "2", "3"]
// b
let b = new Child();
console.log(b.names); // ["1", "2"]
// 方法都在构造函数中定义
console.log(a.getNames()) // [ '1', '2', '3' ]
// 每次创建实例都会创建一遍方法。
console.log(a.getNames === b.getNames) // false
// 访问不到父类原型方法
console.log(a.protoGetNames)// undefined
组合继承
实现:原型链继承 + 构造函数继承
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
return this.name
}
function Child(name, age) {
// 构造函数继承
Parent.call(this, name);
this.age = age;
}
// 原型继承
// 父类构造函数重复调用,原型有了父类属性和方法
Child.prototype = new Parent();
Child.prototype.constructor = Child;
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。 缺点:
- 调用2次构造函数。
- 原型上有父类属性和方法。
寄生式继承 (对象增强)
实现:一个特定封装继承的对象,增强对象,类似于继承。
// 主要关注对象,而不在乎类型和构造函数的场景。-- 红宝书
function createDog(obj){
// object()函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用。 -- 红宝书
let clone = object(obj);
clone.getColor = function(){
console.log(clone.color)
}
return clone
}
// 寄生继承就是不用实例化父类了,直接实例化一个临时副本实现了相同的原型链继承。
function object(proto) {
function F() {}
F.prototype = proto;
return new F();
}
优点:不调用构造函数。 缺点:
- 特定封装,无法复用。
let dog = {
color: 'yellow'
}
let dog1 = createDog(dog);
dog1.getColor(); // yellow
寄生 组合 继承
实现:寄生 + 组合
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
return this.name
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
// 寄生组合继承
function extend(Child, Parent) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
// 修正constructor
Child.prototype.constructor = Child
}
extend(Child, Parent)
优点:几乎完美实现继承,修正了组合继承的原型bug。
- 属性定义在
this,避免共享。 - 可以向父类传参。
- 只调用一次构造函数,原型无多余属性。
construcot正确。