原型
继承取决于原型。当然,原型并不是本文的重点。让我们回顾一下。其实,原型的概念很简单:
所有对象都有一个属性__proto__指向一个对象,即原型。每个对象的原型都可以通过构造函数找到构造函数,构造函数也可以通过原型找到原型。所有函数都可以通过__proto__找到函数对象,所有对象都可以通过__proto__找到对象对象,称为原型链。当前对象中不存在的属性可以通过原型链逐层向上搜索,直到顶层对象实际上是原型中最重要的内容。完全没有必要看那些关于什么是原型的长篇文章。初学者会感到困惑。
当然,如果你想了解更多的原型,可以看看我之前的文章。
ES5实现继承
一般来说,实现ES5的继承有两种方式。之前写了关于这方面的,直接复制使用。
总的来说,我觉得这部分内容更多是为了应付目前的采访。
组合遗传
组合遗传是最常见的遗传方式,
函数Parent(Value){ this . val=Value } Parent . prototype . getvalue=function(){ console . log(this . val)}函数Child(value) { Parent.call(this,Value)} child。prototype=new Parent()const child=new child(1)child。getvalue()//parent的1个子实例//true上述继承方法的核心是通过Parent.call(this)在子类构造函数中继承父类的属性,然后将子类原型改为newparent()继承父类的函数。
这种继承方法的优点是构造函数可以传递参数,不与父类的引用属性共享,可以重用父类的函数。但是也有一个缺点,继承父类的函数时调用父类的构造函数,导致子类原型上不必要的父类属性,造成内存浪费。
寄生组合遗传
这种遗传方法优化了组合遗传。组合继承的缺点是继承父类函数时使用构造函数。我们只需要优化这一点。
函数Parent(value){ this . val=value } Parent . prototype . getvalue=function(){ console . log(this . val)}函数Child(value) { Parent.call(this,value)} Child . prototype=object . create(Parent . prototype,{ constructor : { value : Child,enumerable: false,writable 3: true } })const Child=new Child(1 Child)。getvalue()//parent//true的1个子实例以上继承实现的核心是将父类的原型赋给子类,并将构造函数设置为子类,既解决了父类属性无用的问题,又能正确找到子类的构造函数。
巴贝尔如何编译ES6类
为什么前一篇文章说ES5继承更多的是应对面试,因为我们现在可以直接用类实现继承了。
不过,班级毕竟是ES6。为了与浏览器兼容,我们通常通过Babel编译ES6代码。接下来,让我们看看巴贝尔编译的代码是什么样子的。
function _可能性contractorreturn(self,call) { //.返回调用(调用类型==='object' ||调用类型==='function ')?拨打:自助;}function _inherits(子类,超类){ //.subClass . prototype=object . create(super class super class . prototype,{ constructor : { value : subClass,enumerable: false,writable: true,configurable : true } });if(超类)Object.setPrototypeOf?Object.setPrototypeOf(子类,超类): subClass。_ _ proto _ _=superClass}var Parent=function Parent () {//验证this _ classcallcheck (this,Parent)是否由Parent构造;};var Child=(函数(_Parent) { _inherits(Child,_ Parent);函数Child () { _classCallCheck(this,Child);return _可能性contractorreturn(this,(Child。_ _ proto _ _ | | object . getprototypeof(Child))。应用(这个,参数));}返回Child}(父级));上面的代码是编译代码的一部分,隐藏了一些非核心代码。让我们首先读取_inherits函数。
设置子类原型部分的代码和寄生组合继承完全一样,也说明这个实现是最好的。然而,还有一个额外的宾语句子。这部分代码中的setprototypeof(子类,超类)。实际上,这段代码的功能是继承父类的静态方法,我们之前实现的两个继承方法都没有这个功能。
那么子构造函数的代码基本和前面的实现类似。所以总体来说,巴别塔的继承方式是寄生组合继承,只实现了一步继承父类的静态方法。
继承中存在的问题
谈过如何实现继承,现在让我们考虑继承是否是一个好的选择。
一般来说,我个人是不喜欢继承的,原因是一个接一个的。
让我们先看看代码。如果我们现在要描述几款不同品牌的车,车必须是一个父类,然后每个品牌的车分别是一个子类。
Classcar {构造器(品牌){这个。brand=brand } wheel(){ return ' 4 wheels ' } drvie(){ return '车可以开' } addOil () {return '车可以加油' } } Class OtherCar extends Car { }这部分代码目前没什么毛病,已经实现了汽车的几个基本功能。我们还可以通过子类扩展各种汽车。
但是现在有新能源汽车,不需要加油。当然,除了加油的功能,其他几款车的基本功能还是需要的。
如果新能源汽车直接继承汽车的母级,那么第一个问题,大猩猩和香蕉,就会出现。这个问题的意思是,我们现在只需要一根香蕉,但是我们得到了一只带香蕉的大猩猩。我们不需要大猩猩,但是父母已经强迫它进入子类。继承可以覆盖父类的方法,但不能选择需要继承的内容。
此外,单个父类很难清晰地描述所有场景,这导致需要添加几个不同的父类来描述更多的场景。随着不断的扩展,代码必然会被复制,这也是继承的问题之一。
除了以上两个问题,继承仍然有很强的耦合性,一个子类无论如何都会和它的父类耦合。
既然有强耦合,这个架构肯定是脆弱的。一旦我们的父类设计出了问题,对维护会有很大的影响。因为所有子类都与父类相关联,所以如果您更改父类中的任何内容,您可能需要更改所有子类。
如何解决继承问题
继承更多的是描述事物是什么。如果描述不好,就会出现各种问题。我们有办法解决这些问题吗?答案是组合。
什么是组合?你可以把这个概念想象成你有各种各样的零件,通过这些零件你可以做出各种各样的产品,组合更多的是描述一个东西能做什么。
现在,让我们结合前面的汽车案例。
功能轮(){返回“4轮”;}函数drvie() {return '车可以开';}函数addOil() {return '汽车可以加油';}//油车const car=compose(车轮、drvie、加油)//新能源车const energy car=compose(车轮、驱动)从上面的伪代码中,你一定也发现组合比继承好。无论你想描述什么,你都可以通过组合几个函数来实现。代码干净且易于重用。
最后的
其实,本文的主题是以下两节的内容。如果你有任何问题,请随时在评论区与我互动。
我的所有系列文章都会先在我的Github里更新,有兴趣的可以关注一下。今年,我们将重点关注以下三个栏目
重新学习JS React高级重写组件以上是边肖介绍的JS继承和集成,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!