整合JS中的继承

379 阅读5分钟

15082fe43c4e0497e91dd86f283ccdf9.jpeg 几年前将js继承整合过,但是稿纸不知道我放在哪里了,所以打算在整合一遍,放在这里就不会搞丢了,方便我复习巩固。

继承有哪些:

  1. 原型链继承
  2. 借用构造函数继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合继承

原型链继承

基本思想:将父类的实例作为子类的原型

// 父类
function SuperFun(){
    this.name = 'Super';
    this.colors=['red','blue']
}
SuperFun.prototype.sayName = function (params) {
    console.log(this.name)
}

// 子类
function SubFun() {
    this.subName = 'Sub';
    this.age = 18;
}
SubFun.prototype = new SuperFun();//此时要注意的是SubFun.construction指向的是SuperFun
SubFun.prototype.sayAge = function (params) {
    console.log(params ? params :this.age)
}

// 实例
let instance = new SubFun();
console.log('继承了父类的属性', instance.name);// 继承了父类的属性 Super
instance.sayAge();// 18
instance.sayName();// 继承了父类的原型上的方法 Super
instance.colors.push('yellow');
console.log('instance.colors', instance.colors);
// instance.colors [ 'red', 'blue', 'yellow' ]

let instance1 = new SubFun();
instance1.colors.push('black');
console.log('instance1.colors', instance1.colors);
//instance1.colors [ 'red', 'blue', 'yellow', 'black' ] 无法实现多继承

instance instanceof SubFun;// true
instance instanceof SuperFun;// true
SubFun instanceof SuperFun; // false
SubFun.prototype instanceof SuperFun;// true
SubFun.prototype instanceof SubFun;// true

特点:利用原型,让一个引用类型继承另一个引用类型的属性及方法

优点:继承了父类的模板也继承了父类的原型对象

缺点:

  1. 来自原型对象的所有属性被所有实例共享
  2. 创建子类实例时,无法向父类构造函数中传递参数
  3. 无法实现多继承
  4. 可以在子类构造函数中,为子类实例增加实例属性,如果要新增原型属性和方法,必须在SubFun.prototype = new SuperFun()之后加。

借用构造函数继承

基本思想: 在子类构造函数内部调用父类构造函数

// 父类
function SuperFun(name){
    this.name = name;
    this.colors=['red','blue']
}
SuperFun.prototype.sayName = function () {
    console.log(this.name)
}

// 子类
function SubFun() {
    SuperFun.call(this, 'Super');//继承了父类,同时还可以向父类传递参数
    this.age = 18;
}

// 实例
let instance = new SubFun();
instance.colors.push('yellow');
console.log('instance.colors', instance.colors);
// instance.colors [ 'red', 'blue', 'yellow' ]

let instance1 = new SubFun();
instance1.colors.push('black');
console.log('instance1.colors', instance1.colors);
// instance.colors [ 'red', 'blue', 'yellow' ] 实现了多继承

优点:

  1. 可以想父类传递参数
  2. 实现多继承(子类实例共享父类的定义的所有对象的初始化代码) 缺点:
  3. 只能继承父类的实例属性和方法,不能继承原型属性和方法
  4. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

组合继承

基本思想: 将原型链继承与构造函数继承组合在一起,从而发挥两者职场的一种继承模式

使用原型链实现对原型属性和方法的继承,实现函数复用;而通过借用构造函数来实现实例属性的继承,保证了每个实力都有自己的属性。

// 父类
function SuperFun(name){
    // 在这里定义的是实例属性和实例方法
    this.name = name;
    this.colors=['red','blue'];
}

SuperFun.prototype.saySupName = function () {
    console.log(this.name)
}

// 子类
function SubFun(name, age) {
    SuperFun.call(this, name);//继承了父类,同时还可以向父类传递参数(第1次调用)
    this.age = age;
}
SubFun.prototype = new SuperFun();// 将父类实例作为子类原型实现函数复用(第2次调用)
SubFun.prototype.constructor = SubFun;// 组合继承需要修复构造函数指向
SubFun.prototype.sayAge = function () {
    console.log(this.age);
}

let instance = new SubFun('xm', 18);
instance.saySupName();// xm
instance.sayAge();//18
instance.colors.push('yellow');
console.log('instance.colors', instance.colors);
// instance.colors [ 'red', 'blue', 'yellow' ]


let instance1 = new SubFun('sl', 20);
instance1.saySupName();// sl
instance1.sayAge();// 20
instance1.colors.push('black');
console.log('instance1.colors', instance1.colors);
// instance.colors [ 'red', 'blue', 'yellow' ] 实现了多继承

优点:

  1. 可以继承实例属性和方法
  2. 也可以继承原型的属性和方法
  3. 不存在应用属性共享问题
  4. 可传参可复用

缺点:

  1. 调用了两次父类狗在函数,生成了两份实例

原型式继承

这个方法并没有使用严格意义上的构造函数。

基本思想:借助原型可以基于已有的对象创建新对象,同事还不必因此创建自定义类型。

原型式继承本质其实就是个浅拷贝,以一个对象为模板复制出新的对象,属性共享

function object( o ){
    var G = function(){};
    G.prototype = o;
    return new G();
}
var obj = {
    name : 'ghostwu',
    age : 22,
    color: ['red'],
    show : function(){
        return this.name + ',' + this.age;
    }
};
var obj1 = object( obj );
obj1.color.push('blue');
console.log('obj1.color',obj1.color);//['red', 'blue']
var obj2 = object( obj );
obj1.color.push('yellow');
console.log('obj1.color',obj1.color);//['red', 'blue', 'yellow']
console.log('obj2.color',obj2.color);//['red', 'blue', 'yellow']

object函数中,以对象o为模板,在object函数体里面,定义一个构造函数,让构造函数的原型对象(prototype)指向o,返回构造函数的一个实例,这样就可以访问到对象o的所有属性和方法。

在ES5中主要是通过Object.creat()方法规范化了原型式继承。这个方法接受两个参数:一个作为新对象原型的对象,另一个(可选)为新对象定义额外属性的对象。

在传入一个参数的时候,Object.creat()Object()方法行为相同。

var obj = {
    name: 'gxm',
    color : ['red']
};
var obj1 = Object.create( obj );
obj1.color.push( 'yellow' );

var obj2 = Object.create( obj,{
    name:{
        value: 'xmz'
    }
} );
console.log(obj2.name,'---', obj2.color ); 
//xmz --- ['red','yellow']

var obj3 = Object.create( obj,{
    color:{value:['red','blue']}
} );
console.log(obj1.color,obj2.color,obj3.color );
// ['red','yellow'] ['red','yellow'] ['red','blue']

寄生式继承

寄生式继承就是把原型式继承再次封装,然后在对象上扩展新的方法,再把新对象返回

var obj = {
    name: 'gxm',
    color : ['red']
};
function object(o) {
    var G = function () { };
    G.prototype = o;
    return new G();
}
function createAnother(original) {
    let clone = object(original);
    clone.sayHi = function(){
        console.log('hi');
    }
    return clone;
}
let anotherObj = createAnother(obj);
anotherObj.sayHi();// hi

寄生组合式继承

在组合继承中,调用了两次父类构造函数,这里通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造函数的时候,就不会初始化两次实例方法和实例属性,避免了组合继承的缺点。

基本思想: 借用构造函数继承实例属性,通过原型链的混成形式来继承原型方法

// 父类
function SuperFun(name){
    this.name = name;
    this.colors=['red','blue'];
}

SuperFun.prototype.saySupName = function () {
    console.log(this.name)
}

// 子类
function SubFun(name, age) {
    SuperFun.call(this, name);//继承了父类,同时还可以向父类传递参数
    this.age = age;
}
SubFun.prototype = Object.create(SuperFun.prototype);// 将父类实例作为子类原型实现函数复用
SubFun.prototype.constructor = SubFun;// 组合继承需要修复构造函数指向
SubFun.prototype.sayAge = function () {
    console.log(this.age);
}

let instance = new SubFun('xm', 18);

优点:

  1. 调用两次SuperFun构造函数,只创建一份父类属性
  2. 原型链保持不变
  3. 能正常使用instancofisPrototypeOf

寄生组合式继承是集寄生式继承和组合继承的优点于一身,是实现基于类型继承的最有效的方式。

如有错误,请留言,Thanks♪(・ω・)ノ