继承是面向对象软件技术当中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
下面我们来分析一下前端开发中常用的几种继承。
原型链继承
原型链继承,就是让对象实例通过原型链的方式串联起来,当访问目标对象的某一属性时,能顺着原型链进行查找,从而达到类似继承的效果。
/**
* 父
* @param {*} obj
*/
function Parent(obj={a:1,b:2}){
this.obj=obj;
this.toString=function(){
console.log(JSON.toString(obj))
}
}
/**
* 子
* @param {*} data
*/
function Child(data){
this.data=data;
}
//将子类的原型指向父类的实例
Child.prototype=new Parent(); //缺陷:创建子类实例时候不能给Parent 传参
//创建子类实例1
let c1=new Child(1);
//创建子类实例2
let c2=new Child(2);
//获得父类的继承的值
c1.obj; //{a: 1, b: 2}
c2.obj; //{a: 1, b: 2}
//修改
c1.obj.c=3;
c1.obj; //{a: 1, b: 2, c: 2}
c2.obj; //{a: 1, b: 2, c: 2} 实例1修改或导致实例获取值也会改变(因为是修改是原型对象导致原型链污染)
通过原型链可以去到父类的属性达到继承的目的,但是这种方法存在两种缺陷
缺陷
1:创建子类实例无法想父类传参。因为是子类的原型指向父类的实例。
2:创建了多个子类实例,子类实例修改原型对象属性,会造成同步修改。(代码中有显示)
2:组合继承
鉴于原型链继承存在以上两种缺陷,衍生出组合继承,可以解决上述缺陷。
组合继承:使用 call 在子类构造函数中调用父类构造函数。
//父类和原型链继承相同
//子类
function Child(obj){
//相当于重新执行一次 父类
Parent.call(this,obj);
}
//可以向父类传参
var c3=new Child({a:1,b:2});
var c4=new Child({a:3,b:4});
c3.obj; //{a: 1, b: 2}
c4.obj; //{a: 3, b: 4}
//修改
c3.obj.c=3;
c3.obj; //{a: 1, b: 2, c: 3}
c4.obj; //{a: 3, b: 4} 发现并没有修改 c4
缺陷
Parent 被执行两次消耗内存,影响性能。
寄生组合继承
针对组合继承存在的缺陷,又进化出了“寄生组合继承”:使用 Object.create(Parent.prototype) 创建一个新的原型对象赋予子类从而解决组合继承的缺陷:
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
可点击链接查看Object.create 的api
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
//取代 Child.prototype=new Parent()
//组合继承
function Child(obj){
//相当于重新执行一次 父类
Parent.call(this,obj);
}
//将子类的原型指向父类的实例
Child.prototype = Object.create(Parent.prototype)
var c5=new Child({a:1,b:2});
var c6=new Child({a:3,b:4});
c5.obj; //{a: 1, b: 2}
c6.obj; //{a: 3, b: 4}
//修改
c5.obj.c=3;
c5.obj; //{a: 1, b: 2, c: 3}
c6.obj; //{a: 3, b: 4} 发现并没有修改 c6
class 继承
class 继承是ES6 新增属性。关于class 继承点击下面链接可查看。
https://www.jianshu.com/p/a30c931a6ef9
网友评论