构造函数、原型对象、实例对象三者之间的关系
每一个构造函数都有一个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。