前言

上文讲到js中的原型链,这篇文章对每种继承方式具体分析一下

原型链继承

原型链继承就是将父类的实例赋给子类的原型对象,话不多说,看下代码

function parent(name){
    this.name = '111';
}
parent.prototype.getName = function(){
    console.log(this.name)
}
function child(){}
child.prototype = new parent();
const child1 = new child()
child1.getName();//111
console.log(child.name);//111

优点: 可以直接继承父类的属性和方法,这里的属性继承是通过__proto__找到的,不是实例本身拥有的
缺点: 1.不能向父类传参 2.引用属性会被所有实例共享

function parent(){
    this.name = ['111','2222']
}
parent.prototype.getName = function(){
    console.log(this.name)
}
function child(){}
child.prototype = new parent();
const child1 = new child();
const child2 = new child();
child1.name.push("aaa")
console.log(child1.name,child2.name)//['111','2222','aaa']

3.child的constructor指向parent,应该予以修复

child.prototype.constructor = child;

构造函数继承

构造函数继承就是在子类调用父类,看代码

function parent(name){
    this.name = name;
}
function child(name){
   parent.call(this,name);
}
const child1 = new child('child');
console.log(child1.name)//child

优点:可以继承父类的属性(这里的属性继承是实例本身自己的),可以向父类传参,引用类型不会被所有实例共享,constructor还是指向自己child
缺点:不能继承父类原型上的方法,方法都在构造函数中定义,每次创建实例都会创建一遍方法。

function parent(name){
    this.name = name;
    this.arr = ['111']
}
function child(name){
   parent.call(this,name);
}
const child1 = new child('child1');
const child2 = new child('child2')
child1.arr.push('222');
console.log(child1.arr,child2.arr)//(2) ["111", "222"] ["111"]

组合继承

如果结合原型继承和构造函数继承,这样就可以结合其优点,摒弃其缺点了,这就是组合继承

function parent(name){
   this.name = name;
   this.arr = ['111']
}
parent.prototype.getName = function(){
   console.log(this.name)
}
function child(name){
    parent.call(this,name)
}
child.prototype = new parent();
const child1 = new child('child1');
const child2 = new child('child2');
child1.arr.push('222');
child1.getName();//child1
console.log(child1.arr,child2.arr)//2) ["111", "222"] ["111"]

优点: 可以向父类传参,可以继承父类的属性(子类会有从父类继承的属性,__proto__上基本属性是undefined,引用属性是有的,看下图)和原型上的方法,引用属性不会被共享
clipboard.png

缺点: 一个实例会实例化父类两次,parent.call(this,name)调用一次,child.prototype = new parent()调用一次,constructor指向了parent

原型继承

function create(o){
   function F(){};
   F.prototype = o;
   return new F();
}

原型继承其实就是es5的object.create()的实现
看下这段代码

const person = {
   name: 'name',
   arr: ['111','222']
}
const p1 = create(person);
const p2 = create(person);
p1.name = 'p1';
p1.arr.push('aaa');
console.log(p1.name,p2.name);//p1,name
console.log(p1.arr,p2.arr);//(3) ["111", "222", "aaa"] (3) ["111", "222", "aaa"]

缺点:会共享引用属性
注意:修改p1.name的值,p2.name的值并未发生改变,并不是因为p1和p2有独立的 name 值,而是因为p1.name = 'p1',给person1添加了 name 值,并非修改了原型上的 name 值。p1,p2的constructor属性是指向object的,继承的属性在__proto__

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

function createObj (o) {
  var clone = object.create(o);
  clone.sayName = function () {
    console.log('hi');
  }
  return clone;
}

缺点:跟构造函数模式一样,每次创建对象都会创建一遍方法。

寄生组合式继承

终极继承!!寄生组合式继承,结合了以上的优点,来看下代码
组合继承的缺点就是会调用两次父类,如何避免调用两次呢?能不能直接让子类的prototype访问到父类的prototype?当然可以!!看下面的代码

function parent(name){
   this.name = name;
   this.arr = ['111']
}
parent.prototype.getName = function(){
   console.log(this.name)
}
function child(name){
    parent.call(this,name)
}
//注意!!关键!!
function F(){}
F.prototype = parent.prototype;
child.prototype = new F();

const c1 = new child('c1');
const c2 = new child('c2');
c1.arr.push('ccc');
c1.name = 'name';
c1.getName();//name
c2.getName();//c2
console.log(c1,c2);//child {name: "name", arr: Array(2)} child {name: "c2", arr: Array(1)}

优点:可以继承父类的属性和原型上的方法,实例间不会共享引用属性,基本属性也可以继承变成自己的属性,不会执行两遍父类

clipboard.png

缺点: 子类的constructor指向指向了父类,应该修复之,可以改写如下
可以改写写上面的关键步骤

function create(o){
   function F(){}
   F.prototype = o;
   return new F();
}
function prototype(child,parent){
   const p = create(parent.prototype);
   p.constructor = child;
   child.prototype = p;
}

juan26
521 声望19 粉丝