原型链继承
我们先通过一个栗子,了解一下原型链继承。留意代码注释内容
一个简单的栗子,郭靖和黄蓉都从洪七公那里继承到了skill 打狗棒,貌似没什么问题,我们继续看demo
对郭靖的skill重新赋值,也没有影响黄蓉的skill,我们打印一下gj
gj的skill屏蔽掉了原型链上的skill,所以gj的skill是降龙十八掌,而hr的skill依然是打狗棒
问题即将暴露
总结一下,gj和hr都是Hqg的实例,继承Hqg的属性和方法,当Hqg的属性或者方被改变了,后面的实例也会受影响,有时候这并不是我们希望的结果
借用构造函数
借用?就是使用call或者apply改变一下this指向,
就是子类的构造函数内部通过call或者apply调用父类的构造函数,如果对call方法有不了解的地方,可以翻看昨天的文章
举一个栗子
输出
这样就避免了原型链继承中,构造函数中的属性或者方法被其他实例所改变的问题
⚠️:这里要注意call方法的执行顺序:
如果call在之后执行就会导致一个问题
值会被覆盖,这个要注意!
借用构造函数进行传参
这个算是一个升级的玩法吧
输出
组合继承
将原型链和借用构造函数技术组合到一起。
使用原型链实现对原型属性和方法的继承,用借用构造函数模式实现对实例属性的继承。
这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有自己的属性
一个栗子
先看下输出
我们把这个组合继承和之前的两个原型链继承和借用构造函数继承进行比较
不难发现组合继承融合了他们的优点,成为javascript中最常用的继承模式
原型式继承
这种继承方式没有使用严格意义上的构造函数,借助原型还可以基于已有的对象创建新对象,同时还不必因此创建自定义类型
在object函数内部,先创建一个临时性的构造函数F,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
本质上来说,object对传入其中的对象执行了一次浅复制。
这种模式要去你必须有一个对象作为另一个对象的基础。
在这个例子中,hqg作为另一个对象的基础,把hqg传入object中,该函数就会返回一个新的对象。
这个新对象将hqg作为原型,所以它的原型中就包含一个基本类型和一个引用类型。
所以意味着如果还有另外一个对象关联了hqg,gj和hr修改数组skill的时候,也会体现在这个对象中。
实际上就相当于创建了hqg对象的两个副本
ES5新增Object.create规范了原型式继承
Object.create
Object.create这是什么?
先简单了解一下
object.create() 是使用指定的原型proto对象及其属性propertiesObject去创建一个新的对象。
proto 是必填参数,就是新创建出来的对象的原型 (新对象的 __proto__属性指向的对象),值得注意的是当proto为null的时候创建的新对象完全是一个空对象,没有原型,也就是没有继承Object.prototype上的方法。(如hasOwnProperty() toString() 等)
所以我们也可以这么说,以下三种创建等效
propertiesObject是可选参数,作用就是给新对象添加新属性以及描述器,需要注意的是新添加的属性是新对象自身具有的属性也就是通过hasOwnProperty() 方法可以获取到的属性,而不是添加在原型对象里。
第二个是可选参数具体可以看这里
不展开讨论
看一个栗子,重点看gj.favourite
favourite下属性还有很多,不再一一列举,可参看这里查看
区别
- Object.cerate()继承指定对象
- new Object() 继承内置对象Object
- 可以通过Object.create(null) 创建一个干净的对象,也就是没有原型,而 new Object() 创建的对象是 Object的实例,原型永远指向Object.prototype。
寄生式继承
寄生式继承是与原型式继承紧密相关的一种思路,它创造一个仅用于封装继承过程的函数,在函数内部以某种方式增强对象,最后再返回对象。
寄生式继承和原型式继承,方法接近
寄生组合式继承
组合继承有个弊端就是会调用两次被继承者的构造函数,解决方法就是使用寄生组合式继承。这又是什么呢?这个相对之前的比较复杂,但是高效的一点是只调用一次被继承者构造函数,原理就是通过寄生方式创建一个被继承者的副本,副本和被继承者共用一个prototype,这样就解决了之前的问题
转载链接:https://www.qdtalk.com/2018/12/16/javascript面向对象之es5和es6的继承/