作为面向对象语言大军中的一员,javascript的继承问题也会经常出现在前端面试中,很多小伙伴都折在这个问题上,今天为各位小伙伴们整理了js中的6种继承,并指出它们各自最核心的特点,可作为前端面试准备资料。
一、原型链继承
1、核心:
将父类的实例作为子类的原型。
2、代码:
function Father1(){
this.name = 'baba';
}
Father1.prototype.getName = function(){
return this.name;
}
function Son1(){
this.sonname = 'erzi';
}
Son1.prototype = new Father1(); //核心步骤
Son1.prototype.getSonName = function(){
return this.sonname;
}
var son1 = new Son1();
console.log(son1.getName()); //输出为baba son1继承了父类中的getName方法
console.log(son1.getSonName()); //输出为erzi son1继承了父类之后又新添加的方法
console.log(son1 instanceof Father1); //输出为true son1也是父类的实例
3、缺点:
a、所有新实例都会共享父类实例的属性;
b、新实例无法向父类构造函数传递参数;
c、继承单一。
二、借用构造函数继承
1、核心:
用.call()或者.apply()将父类的构造函数引入子类函数中。
2、代码:
function Father2(name){
this.name = name;
}
function Son2(){
Father2.call(this,'xiaoming'); //核心步骤,继承了父类,同时传递了参数。相对原型链继承一个很大的优势。
this.age = 20;
}
var son2 = new Son2();
console.log(son2.name);
console.log(son2.age);
console.log(son2 instanceof Father2); //输出为false son不是父类的实例
3、缺点:
a、只能继承父类构造函数中的属性,不能继承父类原型的属性;
b、无法实现构造函数的复用(每次用都要重新调用);
c、每个新实例都会有父类构造函数的副本,臃肿,影响性能;
d、实例并不是父类的原型,只是子类的实例。
三、组合继承
1、核心:
组合原型链继承和借用构造函数继承
2、代码:
function Father3(name){
this.name = name;
}
Father3.prototype.sayName = function(){
console.log(this.name);
}
function Son3(name,age){
Father3.call(this,name); //借用构造函数继承,可以传参。 继承父类属性 第二次调用父类构造函数
this.age = age;
}
Son3.prototype = new Father3(); //原型链继承 继承父类原型对象上的方法 第一次调用父类构造函数
Son3.prototype.sayAge = function(){
console.log(this.age);
}
var son3 = new Son3('zhangsan',18);
console.log(son3.name);
son3.sayName();
son3.sayAge();
console.log(son3 instanceof Father3) //输出为true son3也是父类的实例
3、特点:
避免了两种模式的缺陷,结合了两种模式的优点:传参和复用,最常用的继承模式。
4、缺点:(重要)
调用两次父类构造函数,一次是在创建子类原型的时候,一次是在子类构造函数内部。耗内存。
四、原型式继承
1、核心:
基于已有的对象创建新对象.
2、代码:
function Father4(name){
this.name = name;
this.age = 10;
}
function object(o){
function F(){}
F.prototype = o; //继承了传入的参数
return new F(); //返回了函数对象
}
var sup = new Father4(); //父类的一个实例
var sup1 = object(sup); //将这个实例传入到object函数中
console.log(sup1.age); //输出为10 sup1继承了父类的属性
3、特点:
a、类似于复制一个对象,用函数来包装;
b、所有新实例都会继承原型上的属性,无法复用。
[object函数:先创建了一个临时的构造函数,然后把传入的对象作为这个构造函数的原型,最后返回这个临时类型的新实例.]
五、寄生式继承
1、核心:
创建一个仅用来封装过程的函数,该函数在内部以某种形式来增强对象,最后再返回对象。
2、代码:
function Father5(name){
this.name = name;
this.age = 20;
}
function object(o){
function F(){}
F.prototype = o; //继承了传入的参数
return new F(); //返回了函数对象
}
//相当于给原型式继承套了一个壳子
function createSon(obj){
var created = object(obj);
//在内部可以增强对象,添加新的方法和属性
created.sayHi = function(){
console.log("hi");
}
return created;
}
var father5 = new Father5('bababa');
var son5 = createSon(father5);
console.log(son5.age); //20
son5.sayHi(); //hi
3、缺点:
没用到原型。
六、组合式寄生式继承
1、核心:
不必为了指定子类型而调用父类型的构造函数,我们只需要父类型圆形的一个副本。
2、代码:
function object(o){
function F(){}
F.prototype = o; //继承了传入的参数
return new F(); //返回了函数对象
}
function Fn(father,son){
var proto = object(father.prototype); //创建对象 创建父类型原型的副本
proto.constructor = son; //增强对象 为创建的副本添加constructor属性
son.prototype = proto; //指定对象 将创建的副本赋值给子类型的原型
}
function Father6(name){
this.name= name;
}
Father6.prototype.sayName = function(){
console.log(this.name);
}
function Son6(name,age){
Father6.call(this,name);
this.age = age;
}
Fn(Father6,Son6); //核心
var son6 = new Son6('daerzi',16);
son6.sayName(); //输出为daerzi
console.log(son6 instanceof Father6) //输出为true
3、特点:
解决了组合式继承的缺点。(忘了组合式继承什么缺点了?翻回去再看一遍吧~)
【作者水平有限,欢迎大家在评论区交流指正~】