1. 借助call

function Parent(){this.name='parent';
}
function Child(){Parent.call(this);this.type='child'
};
console.log(new Child);
// {name: "parent",type: "child"}

这样写的时候子类虽然能够拿到父类的属性值,但是问题是父类原型对象中一旦存在方法那么子类无法继承。

2. 借助原型链

function Parent() {this.name='parent';this.play= [1, 2, 3]
}
function Child() {this.type='child';
}
Child.prototype=new Parent();console.log(new Child());
// {type: "child",__proto__:Parent}

看似没有问题,父类的方法和属性都能够访问,但实际上有一个潜在的不足。

var s1=new Child();
var s2=new Child();
s1.play.push(4);// console.log(s1.play);
// [1, 2, 3, 4]
// console.log(s2.play);
// [1, 2, 3, 4]

明明我只改变了s1的play属性,为什么s2也跟着变了呢?很简单,因为两个实例使用的是同一个原型对象。那么还有更好的方式么?

3. 将前两种组合

function Parent() { this.name = 'parent'; this.play = [1, 2, 3];
};
function Child() { Parent.call(this); this.type = 'child';
};Child.prototype = new Parent(); var s1 = new Child();
var s2 = new Child();
s1.play.push(4); // console.log(s1.play);
// [1, 2, 3, 4]
// console.log(s2.play);
// [1, 2, 3]

之前的问题都得以解决,但是这里又徒增了一个新问题,那就是Parent的构造函数会多执行了一次,这是我们不愿看到的,那么如何解决这个问题?

4. 组合继承方式一

function Parent () {this.name='parent';this.play= [1, 2, 3];
};
function Child() {Parent.call(this);this.type='child';
};Child.prototype=Parent.prototype;

这里让将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问,但是我们来测试一下:

var s1=new Child();
var s2=new Child();console.log(s1);

打印的结果如下图所示:

子类实例的构造函数是Parent,显然这是不对的,应该是Child。

5. 组合继承方式二

function Parent () {this.name='parent';this.play= [1, 2, 3];
};function Child() {Parent.call(this);this.type='child';
};console.log(new Child());Child.prototype=Object.create(Parent.prototype);console.log(new Child());Child.prototype.constructor=Child;console.log(new Child());

这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承。

ES6的代码最后都是要在浏览器上能够跑起来的,这中间就利用了babel这个编译工具,将ES6的代码编译成ES5让一些不支持新语法的浏览器也能运行。

那最后编译成了什么样子呢?

function _possibleConstructorReturn(self, call) {// ...return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
}function _inherits(subClass, superClass) {// ...// 看到没有subClass.prototype = Object.create(superClass && superClass.prototype, {constructor: {value: subClass,enumerable: false,writable: true,configurable: true}});if (superClass) {Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;};
};var Parent = function Parent() {// 验证是否是 Parent 构造出来的 this_classCallCheck(this, Parent);
};
var Child = (function (_Parent) {_inherits(Child,_Parent);function Child() {_classCallCheck(this, Child);return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));}return Child;
}(Parent));

核心是_inherits函数,可以看到它采用的依然也是第五种方式,即寄生组合继承方式,同时证明了这种方式的成功。不过这里加了一个Object.setPrototypeOf(subClass, superClass),这是用来干啥的呢?

答案是用来继承父类的静态方法,这也是原来的继承方式疏忽掉的地方。

追问:面向对象的设计一定是好的设计吗?

假如现在有不同品牌的车,每辆车都有drive、music、addOil这三个方法。

class Car{constructor(id) {this.id=id;  }drive(){console.log("开车");  }music(){console.log("听音乐")  }addOil(){console.log("加油")  }
}
class otherCar extends Car{}

现在可以实现车的功能,并且以此去扩展不同的车。

但是问题来了,新能源汽车也是车,但是它并不需要addOil()。

如果让新能源汽车的类继承Car的话,也是有问题的,俗称"大猩猩和香蕉"的问题。大猩猩手里有香蕉,但是我现在明明只需要香蕉,却拿到了一只大猩猩。也就是说加油这个方法,我现在是不需要的,但是由于继承的原因,也给到子类了。

继承的最大问题在于:无法决定继承哪些属性,所有属性都得继承。

当然你可能会说,可以再创建一个父类啊,把加油的方法给去掉,但是这也是有问题的,一方面父类是无法描述所有子类的细节情况的,为了不同的子类特性去增加不同的父类,代码势必会大量重复,另一方面一旦子类有所变动,父类也要进行相应的更新,代码的耦合性太高,维护性不好。

那如何来解决继承的诸多问题呢?

用组合,这也是当今编程语法发展的趋势,比如golang完全采用的是面向组合的设计方式。

顾名思义,面向组合就是先设计一系列零件,然后将这些零件进行拼装,来形成不同的实例或者类。

function drive(){console.log("开车");
}
function music(){console.log("听音乐")
}
function addOil(){console.log("加油")
}
let car =compose(drive, music, addOil);
let newEnergyCar= compose(drive, music);

代码干净,复用性也很好,这就是面向组合的设计方式。

JavaScript如何实现继承?相关推荐

  1. JavaScript中的继承入门

    正统的面相对象的语言都会提供extend之类的方法用于出来类的继承,但Javascript并不提供extend方法,在Javascript中使用继承需要用点技巧. Javascript中的实例的属性和 ...

  2. JavaScript之各种继承方式和优缺点

    2019独角兽企业重金招聘Python工程师标准>>> JavaScript之各种继承方式和优缺点 原型链继承 function Parson(){this.name = 'hy' ...

  3. JavaScript学习13 JavaScript中的继承

    JavaScript学习13 JavaScript中的继承 继承第一种方式:对象冒充 <script type="text/javascript">//继承第一种方式: ...

  4. Javascript 原型和继承(Prototypes and Inheritance)

    Javascript 原型和继承(Prototypes and Inheritance) 收藏  前面我们看到了如何使用 constructor 来初始化对象.如果这样做,那么每一个创建的新对象都会对 ...

  5. javascript中的继承方式

    javascript中的继承方式有好几种. 下面分别举例供大家参考学习: 1.function parent() { this.x=1; } function child() { var instan ...

  6. JavaScript大杂烩4 - 理解JavaScript对象的继承机制

    JavaScript是单根的完全面向对象的语言 JavaScript是单根的面向对象语言,它只有单一的根Object,所有的其他对象都是直接或者间接的从Object对象继承.而在JavaScript的 ...

  7. Javascript的对象继承方法

    许多OO 语言都支持两种继承方式: 接口继承:只继承方法签名 实现继承:继承实际的方法. 由于函数没有签名,在ECMAScript 中无法实现接口继承.ECMAScript 只支持实现继承 原型链继承 ...

  8. JavaScript中es5继承(call、apply)和es6继承(super)

    欢迎加入qq群(IT-程序猿-技术交流群):757345416 今天我们来研究下JavaScript中的继承: es5: //构造器函数 function Person(name,age,sex){t ...

  9. JavaScript原型与继承的秘密

    原文发布在dreamapplehappy/blog,本文如若有更新,都会在我的博客进行更新. 我们最想夸耀的事物,就是我们所未拥有的事物 <罗生门>- 芥川龙之介 JavaScript的原 ...

  10. 02.Javascript中的继承----Inherits

    02.Javascript中的继承----Inherits 本文不再过多的阐述OOP中继承的概念,只是用原生的Javascript代码来模拟类继承(不是对象扩展) 类继承:inherits 假设有已定 ...

最新文章

  1. C#读取数据库返回泛型集合 把DataSet类型转换为ListT泛型集合
  2. C++ Testing Framework
  3. FreeRTOS系列第2篇---FreeRTOS入门指南
  4. MinGW和MSYS的自动安装 【转】
  5. 请问在allegro中如何在铜箔上单独放置过孔?
  6. A good debug parameter - sap-ds-debug=true
  7. java 导入 注释末班_Eclipse添加注释模板
  8. mask rcnn属于dnn么_基于OpenCV DNN的 MaskRCNN 目标检测与实例分割
  9. 从零开始用python处理excel视频_从零开始学数据分析,什么程度可以找工作,如何计划学习方案?...
  10. vue 定时器:setInterval和setTimeout使用实例及区别
  11. form resetFields并没有清空表单
  12. 爱奇艺开源的高性能网络安全监控引擎
  13. 金工量化优质书单推荐及下载
  14. HDU - 6070
  15. 3.3Packet Tracer - 实施基本连接
  16. php入门,windows安装与环境配置,基础语法学习
  17. 春天不健脾养胃 也要等什么时候
  18. 程序员,职场上请远离这种人!
  19. linux驱动程序的测试,Linux驱动学习笔记(4)字符设备驱动测试
  20. disc性格测试结果分析(disc性格测试结果分析23个D16个C)

热门文章

  1. 一起来飞车服务器维护到什么时候,《一起来飞车2》更新维护公告
  2. win7蓝屏0x000000f4修复_关于近期财务电脑蓝屏的处理和预防方法,必看!
  3. Matlab学习|绘图|多图基本绘制|legend函数使用
  4. iOS开发_iphone 实现剪贴板操作_iphone 复制粘贴功能
  5. CentOS6.5 安装Oracle11g R2双节点群集
  6. 【SLAM入门】概率机器人中的一些重要概念(1)
  7. 三大亮点抢先看,顶象CTO解析业务安全平台架构设计
  8. gif循环播放_防止动画GIF循环播放
  9. 十月二十五日西施沟溯溪
  10. 农业度“伏”黑科技——农业气象站