一、原型链继承
思路:利用原型让一个引用类型继承另外一个引用类型的属性和方法,因为每个构造函数都有一个原型对象(也就是所有的函数默认的原型都是Object的实例),原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
重点:原型链上面所有的属性和方法都是可以相互共享的。
// 一个引用继承另外一个引用 也就实现了继承
function Parent() {
this.arr = [1, 2];
}
function Child () {
this.name = 'child';
}
Child.prototype = new Parent();
let child = new Child();
// 缺点:
// 1、实例子类的时候不能给父类传参;
// 2、实例会共享引用类型值,例如:
let child1 = new Child();
child1.arr.push(3);
console.log(child1.arr); // 1 2 3
let child2 = new Child();
console.log(child2.arr); // 1 2 3
二、借用构造函数继承
思路:改变作用域上下文方式达到继承的效果。构造函数的实例都有一个constructor的指针指向构造函数的本身
function Parent (name) {
this.name = name;
this.arr = [1, 2];
}
function Child(name, age) {
// 改变上下文作用域达到继承的效果
Parent.call(this, name);
this.age = age;
}
let child1 = new Child('gaga1', 18);
child1.arr.push(3);
console.log(child1.arr)// [1, 2, 3]
let child2 = new Child('gaga2', 19);
console.log(child2.arr)// [1, 2]
// 优点:
// 1、解决传参问题;
// 2、解决了实例共享问题;
// 缺点:
// 1、每个实例都要拷贝一份,占用内存;
// 2、方法在构造函数中定义,函数无法复用;
三、组合继承
组合继承也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式
思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function Parent (name) {
this.arr = [1, 2];
this.name = name;
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.protoType = new Parent();
Child.prototype.constructor = Child;
let child1 = new Child('gaga1', 18);
child1.arr.push(3);
console.log(child1.arr);
let child2 = new Child('gaga2', 19);
console.log(child2.arr);
// 优点:
// 解决了原型链和借用构造函数带来的问题
// 缺点:
// 1、调用了两次父类构造函数,其实是使用子类实例上的方法和属性覆盖原型原型上相同的属性和方法。
// 2、实例之后会保存在栈内存中,栈内存会开辟一个位置来存放
四、原型式继承
原理:创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时构造函数的实例。
function object (o) {
function F () {};
F.prototype = o;
return new F();
}
let o = {
name: 'gaga',
arr: [1, 2]
}
let child = object(o);
child.arr.push(3);
console.log(child.arr); // [1, 2, 3]
// 缺点:
// 1、与原型链继承一样会有引用值共享的问题;
// 2、单独比较浪费,所以单独使用率不高。
五、寄生继承
思路:在原型式的基础上,创建一个仅用于封装继承过程的函数,在该函数内部以某种方式增强对象,最后返回这个对象。
function object (o) {
function F () {};
F.prototype = o;
return new F();
}
function createAnOther (o) {
let clone = object(o);
// 单独添加会降低复用效率
clone.run = function () {
console.log('百米赛跑!');
};
return clone;
}
let o = {
name: 'gaga',
arr: [1, 2]
};
let person = createAnOther(o);
consol.log(person.run()); // '百米赛跑!'
// 缺点:
// 1、使用寄生继承来为对象添加函数,会由于不能做到函数复用而降低效率。
六、寄生组合继承
思路:由原型链继承+借用构造函数继承+原型式继承+寄生继承 4个继承的组合继承而成。
// 父类
function Parent (name) {
this.name = name;
this.arr = [1, 2];
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
// 子类
function Child (name, age) {
// 使用借用构造函数来继承父类的属性和方法(改变作用域上下文)
Parent.call(this, name);
this.age = age;
}
// 原型式继承(创建一个空的构造函数,然后把参数赋给这个空构造函数的原型属性,最后返回这个创建的构造函数的实例对象)
function object(o) {
function F () {}
F.prototype = o;
return new F();
}
// 寄生组合模式
function inheritAnOther (child, parent) {
// 复制父类的原型对象
let pro = object(parent.prototype);
// 把复制返回的对象实例的constructor指针指向子类
pro.constructor = child;
// 最后把整个复制的实例对象赋给子类的原型实现,这样就实现了寄生组合继承
child.prototype = pro;
}
inheritAnOther(Child, Parent);
Child.prototype.sayAge = function () {
console.log(this.age);
};
let child = new Child('gaga', 18);
console.log(child.sayName()); // gaga
console.log(child.sayAge()); // 18
// 缺点:写法比较复杂