理解JavaScript(4):JavaScript实现继承的多种方式

JavaScript实现继承的多种方式

  JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一,那么它是怎么实现继承的呢!

创建一个父类

function People (name, age) {
    // 属性
    this.name = name || '无名氏';
    this.age = age || 18;
    // 实例方法
    this.sleep = function () {
        console.log('有事就来梦里找我吧...');
    }
}
// 原型方法 
// 原型方法和实例方法的区别是原型方法是共有的,实例方法在创建实例的时候也会一起创建
People.prototype.eat = function (food) {
    console.log (this.name + '正在吃' + food);
}

1. 原型实现继承

核心:将父类的实例作为子类的原型

function Man (name, age) {
    this.sex = 'man';
    this.duty = function () {
        console.log('get money and take care of family');
    }
}
Man.prototype = new People(); // 原型继承
Man.prototype.constructor = Man; // 修复构造函数
Man.prototype.name = 'man'; // 如果要扩展属性,必须在new People()之后
var xiaoMing = new Man('xiaoMing', 24); // 原型继承无法向父类构造函数传参
console.log(xiaoMing.name); // man 
console.log(xiaoMing.sex); // Man构造函数中的私有变量
xiaoMing.eat('肉'); // man正在吃肉 继承People的方法
xiaoMing.duty(); // get money and take care of family, Man构造函数的方法
console.log(xiaoMing instanceof Man); // true
console.log(xiaoMing instanceof People); // true

从上面可以看出,该方法有一些有点,也有一些缺点

优点
1. 纯粹的继承关系,即是父类的实例,又是子类的实例
2. 父类新增原型方法/原型属性,子类都能访问到
3. 简单,易于实现

缺点
1. 要想为子类新增属性和方法,必须要在new People()这样的语句之后执行,不能放到构造器中
2. 无法实现多继承
3. 来自原型对象的引用属性是所有实例共有的
4. 创建子类实例时无法向父类传参

2. 构造函数继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Man (name, age) {
    People.apply(this, arguments);
    this.sex = 'man';
    this.duty = function () {
        console.log('get money and take care of family');
    }
}
var xiaoMing = new Man('xiaoMing', 24); // 
console.log(xiaoMing.name); // xiaoMing 
console.log(xiaoMing.sex); // Man
xiaoMing.eat('肉'); // VM99:25 Uncaught TypeError: xiaoMing.eat is not a function at <anonymous>:25:10 不能继承原型的方法和属性
xiaoMing.duty(); // get money and take care of family
console.log(xiaoMing instanceof Man); // true
console.log(xiaoMing instanceof People); // false

特点:
1. 解决了1中,子类实例共享父类引用属性的问题
2. 创建子类实例时,可以向父类传递参数
3. 可以实现多继承(call多个父类对象)

缺点:
1. 实例并不是父类的实例,只是子类的实例
2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

3. 组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Man (name, age) {
    People.apply(this, arguments);
    this.sex = 'man';
    this.duty = function () {
        console.log('get money and take care of family');
    }
}
Man.prototype = new People();
Man.prototype.constructor = Man;
var xiaoMing = new Man('xiaoMing', 24); 
console.log(xiaoMing.name); // xiaoMing 
console.log(xiaoMing.sex); // Man
xiaoMing.eat('肉'); // xiaoMing正在吃肉
xiaoMing.duty(); // get money and take care of family
console.log(xiaoMing instanceof Man); // true
console.log(xiaoMing instanceof People); // true

优点:
1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
2. 既是子类的实例,也是父类的实例
3. 不存在引用属性共享问题
4. 可传参
5. 函数可复用

缺点:
1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

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

function Man (name, age) {
    People.apply(this, arguments);
    this.sex = 'man';
    this.duty = function () {
        console.log('get money and take care of family');
    }
} 
//通过一个没有空的方法类,将方法类的显示原型指向,父类的显示原型。将实例作为子类的原型
(function(){
    var Super = function () {}; //不能用super,super是关键字
    Super.prototype = People.prototype;
    Man.prototype = new Super();
    Man.prototype.constructor = Man;
})();
var xiaoMing = new Man('xiaoMing', 24); 
console.log(xiaoMing.name); // xiaoMing 
console.log(xiaoMing.sex); // Man
xiaoMing.eat('肉'); // xiaoMing正在吃肉
xiaoMing.duty(); // get money and take care of family
console.log(xiaoMing instanceof Man); // true
console.log(xiaoMing instanceof People); // true

优点
所有优点都有

缺点
理解稍微有点困难

  js实现继承还有一些其他的方式,具体请参考,博客JS实现继承的几种方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值