前言
忘了整理的理一理。
ES5继承
先不涉及ES6的继承,后面会涉及。
这里主要是ES5的继承:
- 原型链继承
- 构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
/**
* 原型链继承
*/
console.log("----------------------------------原型链继承")
const Man=function(){
this.name="dad";
this.sex='man';
Man.prototype.say=function(){
console.log("I am ",this.name);
}
}
const pInstance=new Man();
console.log(pInstance.constructor===Man); //true
pInstance.say() //I am data
const Child=function(){
this.name="child";
}
Child.prototype=new Man();
const cInstance=new Child();
console.log(cInstance instanceof Man ) //true
console.log(cInstance.constructor===pInstance.constructor) //true
console.log(cInstance.constructor===Child) //false
console.log("child:",cInstance.name) //child
原型链继承的一些问题:
1.因为继承是通过实例替换原型来到达的,那么实例原型的上的属性会被所以子类的实例所共享。
2.子类实例创建时,不能向父构造函数传递参数,因为会存在构造函数需要初始化参数的情况
/**
* 构造函数继承
*/
console.log("----------------------------------构造函数继承")
const Man1=function(age){
this.name="dad";
this.sex='man';
this.age=age;
}
const Child1=function(){
Man1.call(this,11);
}
const cInstance1=new Child1();
console.log(cInstance1 instanceof Man1) //false
console.log(cInstance1)
构造函数继承的缺点
1.只能继承父类构造函数的属性,即父类原型的方法,子类也是无法调用的
2.无法实现构造函数的复用。因为1的原因,即方法需要在构造函数内生成,那么实例化每个函数都是独立的,即达不到复用的效果
/**
* 组合继承 = 原型链继承+构造函数继承
*/
console.log("----------------------------------组合继承")
const Man2=function(){
this.name="dad";
this.sex='man';
}
const Child2=function(){
Man2.call(this);
}
Child2.prototype=new Man2();
const cInstance2=new Child2();
console.log(cInstance2 instanceof Man2) //true
console.log(cInstance2.constructor===Child2) //false
Child2.prototype.constructor=Child2;
console.log(cInstance2.constructor===Child2) //true
组合继承的缺点:
1.调用了两次构造函数,原型属性上会有重复的可能,即声明了多余的属性
/**
* 原型继承
*/
console.log("----------------------------------原型继承")
function createObj(obj){
function O(){
}
O.prototype=obj;
return new O();
}
const Man3=function(){
this.name="dad";
this.sex='man';
}
const mInstance3=new Man3();
const cInstance3=createObj(mInstance3);
console.log(cInstance3 instanceof Man3) //true
//所有的实例都会继承原型上的属性,
//所有子类属性都是实例生成后添加的,无法复用
/**
* 寄生继承
*/
console.log("----------------------------------寄生继承")
const Man4=function(){
this.name="dad";
this.sex='man';
}
const mIntance4=new Man4();
const createChild=function(obj){
const childInstance=createObj(obj);
// childInstance.name='child'
childInstance.say=function(){
console.log("I am",childInstance.name);
}
return childInstance;
}
const cIntance4=createChild(mIntance4);
cIntance4.say()
//无法复用,同构造函数继承
/**
* 寄生组合继承
*/
console.log("----------------------------------寄生组合继承")
const Man5=function(){
this.name="dad";
this.sex='man';
}
const Child5=function(){
Man5.call(this);
this.sex="woman";
this.age=11;
}
const ex=function(child,parent){
const proto=Object.create(parent.prototype);
// const proto=createObj(parent.prototype)
proto.constructor=child;
child.prototype=proto;
}
ex(Child5,Man5);
const cInstance5=new Child5();
console.log(cInstance5)
console.log(cInstance5 instanceof Man5); //true
//通过寄生的方式来解决组合继承中两次构造函数调用的问题
ES6继承
ES6的继承原理跟ES5的寄生组合继承有相似的地方。
class Parent {
constructor(a){
this.filed1 = a;
}
filed2 = 2;
func1 = function(){}
}
class Child extends Parent {
constructor(a,b) {
super(a);
this.filed3 = b;
}
filed4 = 1;
func2 = function(){}
}
//babel 转换后
function _classCallCheck(instance,parent){ //es6class类的调用需要使用new的
//instanceOf判断一个实例是否属于某种类型,也可以用来判断原型链
if(!(instance instanceOf parent)){
new TypeError("Cannot call a class as a function");
}
}
var parent=function parent(a){ //可见class类的底层还是构造函数
//_classCallCheck方法判断当前函数调用前是否有new关键字
_classCallCheck(this,Parent); //this指向new出来的空对象
this.filed1=a;
this.filed2 = 2;
this.func1 = function(){}
}
var Child = function (_Parent) {
_inherits(Child, _Parent); //子类继承父类的proptype
function Child(a, b) { //闭包保存父类的引用,在闭包中做子类的引用
_classCallCheck(this, Child);//判断当前函数是否是使用new关键字调用
//Child.__proto__ || Object.getPrototypeOf(Child)实际上是父构造函数(_inherits最后的操作),
//然后通过call将其调用方改为当前this,并传递参数
var _this = _possibleConstructorReturn(this,
(Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));
_this.filed4 = 1;
_this.func2 = function () {};
_this.filed3 = b;
return _this;
}
return Child;
}(Parent);
function _inherits(sub,parent){
//当parent不为函数时必须为空,或者是当parent不为空时必须为函数,否则类型错误
if(typeOf parent!=="function" && parent!==null){
throw new TypeError(
"Super expression must either be null or a function,not " + typeof superClass
);
}
sub.prototype=Object.Create(parent&&parent.prototype,{//寄生组合继承
constructor:{ value: sub, enumerable: false, writable: true, configurable: true }
})
if(parent){
Object.setPrototypeOf ? Object.setPrototypeOf(sub,parent):sub.__proto__ =parent
}
}
function _possibleConstructorReturn(self, call) {
if (!self) {//校验this是否被初始化
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
参考:
《JavaScript高级程序设计》
javascript中的六种继承方式
深入JavaScript继承原理
es6 class类继承实现原理
网友评论