重学JS 系列:聊聊继承(推荐)
原型
继承得靠原型来实现,原型不是这篇文章的重点,我们来复习一下即可。
其实原型的概念很简单
- 所有对象都有一个属性 __proto__ 指向一个对象,也就是原型
- 每个对象的原型都可以通过 constructor 找到构造函数,构造函数也可以通过 prototype 找到原型
- 所有函数都可以通过 __proto__ 找到 Function 对象
- 所有对象都可以通过 __proto__ 找到 Object 对象
- 对象之间通过 __proto__ 连接起来,这样称之为原型链。当前对象上不存在的属性可以通过原型链一层层往上查找,直到顶层 Object 对象
其实原型中最重要的内容就是这些了,完全没有必要去看那些长篇大论什么是原型的文章,初学者会越看越迷糊。
如果你想了解更多原型的深入内容,可以阅读我
ES5 实现继承
ES5 实现继承就两种办法,之前写过这方面的内容,就直接复制来用了。
这部分的内容我觉得在当下更多的是为了应付面试吧。
组合继承
组合继承是最常用的继承方式,
function Parent(value) { this.val = value } Parent.prototype.getValue = function() { console.log(this.val) } function Child(value) { Parent.call(this, value) } Child.prototype = new Parent() const child = new Child(1) child.getValue() // 1 child instanceof Parent // true
以上继承的方式核心是在子类的构造函数中通过 Parent.call(this) 继承父类的属性,然后改变子类的原型为 new Parent() 来继承父类的函数。
这种继承方式优点在于构造函数可以传参,不会与父类引用属性共享,可以复用父类的函数,也存在一个缺点就是在继承父类函数的时候调用了父类构造函数,导致子类的原型上多了不需要的父类属性,存在内存上的浪费。
寄生组合继承
这种继承方式对组合继承进行了优化,组合继承缺点在于继承父类函数时调用了构造函数,我们只需要优化掉这点就行了。
function Parent(value) { this.val = value } Parent.prototype.getValue = function() { console.log(this.val) } function Child(value) { Parent.call(this, value) } Child.prototype = Object.create(Parent.prototype, { constructor: { value: Child, enumerable: false, writable: true, configurable: true } }) const child = new Child(1) child.getValue() // 1 child instanceof Parent // true
以上继承实现的核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数。
Babel 如何编译 ES6 Class 的
为什么在前文说 ES5 实现继承更多的是应付面试呢,因为我们现在可以直接使用 class 来实现继承。
class 毕竟是 ES6 的东西,为了能更好地兼容浏览器,我们通常都会通过 Babel 去编译 ES6 的代码。接下来我们就来了解下通过 Babel 编译后的代码是怎么样的。
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),其实这句代码的作用是为了继承到父类的静态方法,之前我们实现的两种继承方法都是没有这个功能的。
然后 Child 构造函数这块的代码也基本和之前的实现方式类似。所以 Babel 实现继承的方式还是寄生组合继承,无非多实现了一步继承父类的静态方法。
继承存在的问题
讲了这么些如何实现继承,现在我们来考虑下继承是否是一个好的选择?
,我个人不怎么喜欢继承,原因呢就一个个来说。
我们先看代码。假如说我们现在要描述几辆不同品牌的车,车必然是一个父类,然后各个品牌的车都分别是一个子类。
class Car { constructor (brand) { this.brand = brand } wheel () { return '4 个轮子' } drvie () { return '车可以开驾驶' } addOil () { return '车可以加油' } } Class OtherCar extends Car {}
这部分代码在当下看着没啥毛病,实现了车的几个基本功能,我们也可以通过子类去扩展出各种车。
现在出现了新能源车,新能源车是不需要加油的。除了加油这个功能不需要,其他几个车的基本功能还是需要的。
如果新能源车直接继承车这个父类的话,就出现了第一个问题 ,大猩猩与香蕉问题。这个问题的意思是我们现在只需要一根香蕉,却得到了握着香蕉的大猩猩,大猩猩其实我们是不需要的,父类还是强塞给了子类。继承虽然可以重写父类的方法,并不能选择需要继承什么东西。
单个父类很难描述清楚所有场景,这就导致我们可能又需要新增几个不同的父类去描述更多的场景。随着不断的扩展,代码势必会存在重复,这也是继承存在的问题之一。
除了以上两个问题,继承还存在强耦合的情况,不管怎么样子类都会和它的父类耦合在一起。
既然出现了强耦合,那么这个架构必定是脆弱的。一旦我们的父类设计的有问题,就会对维护造成很大的影响。因为所有的子类都和父类耦合在一起了,假如更改父类中的任何东西,都可能会导致需要更改所有的子类。
如何解决继承的问题
继承更多的是去描述一个东西是什么,描述的不好就会出现各种各样的问题,那么我们是否有办法去解决这些问题呢?答案是组合。
什么是组合呢?你可以把这个概念想成是,你拥有各种各样的零件,可以通过这些零件去造出各种各样的产品,组合更多的是去描述一个东西能干什么。
现在我们把之前那个车的案例通过组合的方式来实现。
function wheel() { return "4 个轮子"; } function drvie() { return "车可以开驾驶"; } function addOil() { return "车可以加油"; } // 油车 const car = pose(wheel, drvie, addOil) // 新能源车 const energyCar = pose(wheel, drive)
从上述伪代码中想必你也发现了组合比继承好的地方。无论你想描述任何东西,都可以通过几个函数组合起来的方式去实现。代码很干净,也很利于复用。
其实这篇文章的主旨还是后面两小节的内容,如果你还有什么疑问欢迎在评论区与我互动。
我所有的系列文章都会在我的中最先更新,有兴趣的可以关注下。今年主要会着重写以下三个专栏
- 重学 JS
- React 进阶
- 重写组件
以上所述是长沙网络推广给大家介绍的JS继承详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,长沙网络推广会及时回复大家的。在此也非常感谢大家对狼蚁SEO网站的支持!
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程