js的原型链和继承方式

76 阅读2分钟

构造函数、原型对象、实例对象三者之间的关系

每一个构造函数都有一个prototype对象,prototype一定存在constructor属性,指向构造函数。通过构造函数创建的实例对象,实例对象通过__proto__可以访问原型对象。

原型链

寻找属性的过程就是在遍历原型对象

其实很简单,当一个js对象寻找属性的时候,自己本身没有,就会通过__proto__找到原型对象,询问原型对象是否存在需要找的属性,如果原型对象没有,就去原型对象上的原型对象找属性,一直找到Object的原型对象,而Object的原型对象是null,如果在Object的原型对象上还找不到,那么就会报错了。

js实现继承的方法

js实现继承的方式有很多,比如构造函数继承、原型链继承组合继承、原型继承,组合+原型继承这里介绍几个常用的。

构造函数继承

const Child = function(name){ this.name = name; };
const Parent = function(age){ this.age = age; };

//为了让child拥有parent的属性,我们在child中调用parent
const Child = function(name,age){ 
    Parent.call(this,age)    this.name = name; 
};

const child = new Child("ezreal",66);

在new Child的时候,parent函数的this其实是Child的实例对象,就会为child实例添加Parent函数的属性

缺点:不能继承Parent原型对象上的属性

原型链继承

const Child = function(name){ this.name = name; };
const Parent = function(age){ this.age = age; };
//关键在这
Child.prototype = new Parent();
//之后在任何地方实例化child
const child = new Child("ezreal");

为了继承父类上的属性,替换了Child的原型对象

缺点:Parent不能传参

组合继承

// 结合构造函数继承和原型链继承
const Parent = function(age){ this.age = age; };
const Child = function(name,age){  
    Parent.call(this,age)
    this.name = name; 
};Child.prototype = new Parent();
const child = new Child("ezreal");//现在child继承了parent的实例属性和原型属性

缺点:调用两次parent的构造函数,并且child的原型对象存在很多已经在child实例对象存在的相同的parent属性,属性重复

原型继承

const Child = function(name){ this.name = name; };
const Parent = function(age){ this.age = age; };
Child.prototype = Object.create(new Parent());//之后在任何地方实例化child
const child = new Child("ezreal");

缺点:Parent不能传参

组合+原型继承

const Child = function(name){ this.name = name; };
const Parent = function(age){ 
 Parent.call(this,age);
 this.age = age; 
};
Child.prototype = Object.create(Parent.prototype);

//现在Parent构造函数执行一次,并且child拥有Parent实例的属性,
//并且child的原型对象指向了一个新的空对象(Object.create返回一个没有属性的对象),
//该空对象的原型对象为Parent的原型对象,构成了原型链

总结

继承方式最完美的当属组合+原型继承,原型对象上最好只放方法,不要放引用类型,多个实例共享同一个原型对象,原型对象上存在Array等,可能会出现bug。