一次面试被面试官问到:你简历上写你对js深入的理解,请你说一下js继承有几种方式?我心里暗喜:我当然知道啦,我5年的前端开发经验又不是虚的,然后就列了以下3种模式:
1.通过call/apply将父构造函数this指向子构造函数
2.通过给子构造函数的prototype属性赋值父构造函数实例
3.直接给子构造函数的prototype属性赋值父构造函数prototype属性
然后面试官:嗯,那你知道它们之间的差异和优缺点吗?
我:没有深入了解.....
毫无悬念面试挂了,吸取笔者的教训,以后简历就不要轻易写“深入理解”’,除非你真的对其有很深入的研究,不然就是给自己挖坑!还有千万不要写“精通”某某语言,相信那些技术大牛也不敢说自己“精通”哪门语言。扯远了,回归正题js原生继承有几种方式它们的优缺点分别是什么?
一、构造函数绑定
就是上面说的通过call/apply将父构造函数this指向子构造函数看下面的例子
// 人类的构造函数
function Person(species){
this.species = species;
}
Person.prototype.sex='man'
//中国人的构造函数
function Chinese(species,name,skin){
Person.apply(this, arguments);
this.name=name;
this.skin = skin ;
}
var chinese = new Chinese("会说话","小明","黄色");
console.log(chinese.species); // 会说话
console.log(chinese.sex); //undefined
优点:
1.简单快捷
2.创建子类实例时,可以向父类传递参数
3.可以实现多继承(apply多个父类对象)
缺点:
1.实例只是子类的实例不属于父类实例
2.只能继续父类属性/方法不能继承其原型属性/方法
二、原型链继承
function Person(){
this.species = "会说话";
}
//中国人的构造函数
function Chinese(name,skin){
this.name=name;
this.skin = skin ;
}
Chinese.prototype=new Person();
Chinese.prototype.constructor = Chinese
var chinese = new Chinese("小明","黄色");
console.log(chinese.species); // 会说话
有同学可能会对这句Chinese.prototype.constructor = Chinese感到疑惑,为什么要重新赋值Chinese.prototype的constructor呢,因为Chinese.prototype=new Person()会把Chinese.prototype的constructor指向了Person,如果不手动改过来就会导致会导致继承链的紊乱。
优点:
1.非常纯粹的继承关系,实例是子类的实例,也是父类的实例
2.父类新增原型方法/原型属性,子类都能访问到
3.简单,易于实现
缺点:
1.性能低,建立Person的实例,浪费内存
2.来自原型对象的所有属性被所有实例共享,改变实例的属性会同时改变原型属性
三、prototype继承
function Person(){
}
Person.prototype.species="会说话"
function Chinese(name,skin){
this.name=name;
this.skin = skin ;
}
Chinese.prototype=Person.prototype;
Chinese.prototype.constructor = Chinese
var chinese = new Chinese("小明","黄色");
console.log(chinese.species); // 会说话
Chinese.prototype.species="好客"
console.log(Person.prototype.species)// 好客
优点:效率比较高(不用执行和建立Person的实例了),比较省内存
缺点: Person.prototype和Chinese.prototype现在指向了同一个对象,Chinese.prototype的修改,会修改到Person.prototype。
function Person(){
}
Person.prototype.species="会说话"
function Chinese(name){
var person= Person.prototype;
for(var p in person){
Chinese.prototype[p] = person[p];
}
Chinese.prototype.name = name || 'Tom';
}
var chinese= new Chinese();
console.log(chinese.species); //会说话
console.log(chinese instanceof Person); // false
console.log(chinese instanceof Chinese); // true
特点:支持多继承
缺点:
1.效率较低,内存占用高(因为要拷贝父类的属性)
2.无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
五、Object.create
function Person(){
}
Person.prototype.species="会说话"
function Chinese(name,skin){
this.name=name;
this.skin = skin ;
}
Chinese.prototype=Object.create(Person.prototype);
Chinese.prototype.constructor = Chinese
var chinese = new Chinese("小明","黄色");
console.log(chinese.species); // 会说话
Chinese.prototype.species="好客"
console.log(Person.prototype.species)// 会说话
这种方式是目前常用的一种继承方式,效率高,又解决了Chinese.prototype和Person.prototype共享指针内存问题
原文参考
http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
https://www.cnblogs.com/humin/p/4556820.html