JS多种继承方法

本文详细介绍了JavaScript中包括原型链继承、构造继承、组合继承、拷贝继承、原型式继承、寄生式继承和寄生组合继承以及ES6的class实现。分析了各种继承方式的优缺点,如原型链继承的共享引用类型问题,构造继承无法继承原型属性,组合继承的重复调用,拷贝继承的低效率等。最后指出,寄生组合继承被认为是较为完美的继承方式,但实现相对复杂。

1.原型链继承

将父类的实例作为子类的原型

function Father(name, age) {
    this.name = name;
    this.age = age
};

function Son() {};

Son.prototype = new Father();
Son.prototype.constructor = Son;

优点:

1.简单易于实现

2.可以继承父类中的方法

3.实例既是父类实例也是子类实例

缺点:

1.每个实例对引用类型属性的修改都会被其他的实例共享

2.创建子类实例时,无法向父类构造函数传参

3.无法实现多继承

2.构造继承

在子类的构造函数中调用父类的构造函数

function Father(name, age) {
    this.name = name;
    this.age = age
};

function Son() {
    Father.call(this)
};

优点:

1.每个实例对引用类型属性的修改不会被其他的实例共享

2.创建子类实例时,可以向父类构造函数传参

3.可以实现多继承

缺点:

1.只能继承父类的实例属性和方法,不能继承原型属性/方法

2.实例只是子类的实例

3.父类方法无法复用

3.组合继承

将原型链继承和构造继承进行组合

在子类中调用父类的构造函数,将父类实例对象作为子类的原型

function Father(name, age) {
    this.name = name;
    this.age = age
};

function Son() {
    Father.call(this)
};
Son.prototype = new Father();
Son.prototype.constructor = Son;

特点:

1.每个实例对引用类型属性的修改不会被其他的实例共享

2.创建子类实例时,可以向父类构造函数传参

3.可以实现多继承

4.可以继承父类的实例属性和方法,也可以继承原型属性/方法

5.既是子类的实例,也是父类的实例

6.可以实现父类方法的复用

缺点:

调用了两次父类,产生了两个实例对象

4.拷贝继承

在子类构造函数中创建父类实例对象,利用for in遍历出父类实例对象的键名,并添加到子类的原型上

function Father(name, age) {
    this.name = name;
    this.age = age
};

function Son() {
    let father = new Father();
    for (let i in father) {
        Son.prototype[i] = father[i]
    }
};

优点:

1.支持多继承

缺点:

1.效率极低,内存占用高(因为要拷贝父类的属性)

2.无法获取父类不可枚举的方法(for in不能访问到的)

5.原型式继承(实例继承)

创建父类的实例对象,以父类实例对象为原型对象创建子类构造函数,利用该构造函数产生子类对象

function Father(name, age) {
    this.name = name;
    this.age = age
};

function beget(obj) {
    let F = function() {};
    F.prototype = obj;
    return new F()
};

let father = new Father();
let son = beget(father)

特点:

从父类对象直接产生新的对象

缺点:

1.每个实例对引用类型属性的修改都会被其他的实例共享

2.实例只是子类的实例

3.父类方法无法复用

2.不支持多继承

6.寄生式继承

创建父类的实例对象,以父类实例对象为原型对象创建子类构造函数,利用该构造函数产生子类对象,并且以某种方法增强这个对象

function Father(name, age) {
    this.name = name;
    this.age = age
};

function beget(obj) {
    let F = function() {};
    F.prototype = obj;
    return new F()
};

function create(obj) {
    let temp = beget(obj);
    temp.hello = function() {
        console.log("hello");
    };
    return temp
}

let father = new Father();
let son = create(father)

特点:

从父类对象直接产生新的对象

缺点:

1.每个实例对引用类型属性的修改都会被其他的实例共享

2.实例只是子类的实例

3.父类方法无法复用

2.不支持多继承

7.寄生组合继承

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

function Father(name, age) {
    this.name = name;
    this.age = age
};

function beget(obj) {
    let F = function() {};
    F.prototype = obj;
    return new F()
};

function Son() {
    Father.call(this)
}

let proto = beget(Father.prototype);
proto.constructor = Son;
Son.prototype = proto;
let son = new Son()

特点:

基本上是完美的

缺点:

实现起来较为复杂

7.class

本质上还是原型链继承

class Point {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    
    toString() {
        return this.x + '' + this.y
    }
}

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y) //调用父类的constructor(x, y)
        this.color = color
    }
    
    toString() {
        return this.color + ' ' + super.toString() // 调用父类的toString()
    }
}

var colorPoint = new ColorPoint('1', '2', 'red')

console.log(colorPoint.toString())  // red 12


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天都在掉头发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值