做为 JavaScript
中最重要的内容之一,继承问题一直是咱们关注的重点。那么你是否清晰地知道它的原理以及各类实现方式呢html
阅读这篇文章,你将知道:前端
这里默认你已经清楚的知道构造函数、实例和原型对象之间的关系,若是并非那么清晰,那么推荐你先阅读这篇文章 -- JavaScript 中的原型与原型链git
若是文章中有出现纰漏、错误之处,还请看到的小伙伴多多指教,先行谢过github
如下↓web
继承(inheritance)是面向对象软件技术当中的一个概念。若是一个类别B
继承自
另外一个类别A
,就把这个B
称为A的子类
,而把A
称为B的父类别
也能够称A是B的超类
。继承可使得子类具备父类别的各类属性和方法,而不须要再次编写相同的代码 ... 更多)
经过这些概念和图示咱们不难知道继承能够在咱们的开发中带来的便捷,那么在 JavaScript
中如何去实现继承呢?segmentfault
利用原型让一个引用类型继承另外一个引用类型的属性和方法
function SuperType() { this.name = 'tt'; } SuperType.prototype.sayName = function() { return this.name } function SubType() { this.name = 'oo'; } SubType.prototype = new SuperType() var instance = new SubType() instance.sayName() // oo instance instanceof SubType // true instance instanceof SuperType // ture
以上的试验中,咱们建立了两个构造函数 SuperType
和 SubType
,而且让 SubType
的原型指向 SuperType
,SubType
也就继承了 SuperType
原型对象中的方法。因此在建立 instance
实例的时候,实例自己也就具备了 SuperType
中的方法,而且都处在它们的原型链中app
SubType.prototype.constructor == SubType // false SubType.prototype.constructor == SuperType // true
须要注意的是:这个时候 SubType.prototype.constructor
是指向 SuperType
的,至关于重写了 SubType
的原型对象。函数
用一张图表示:学习
SubType.prototype
至关于 SuperType
的实例存在的,因此 SubType.prototype.constructor
就指向 SuperType
优势:this
缺点:
SubType
添加原型方法,就必须在 new SuperType
以后添加(会覆盖)在子类构造函数的内部调用超类型构造函数,经过apply
和call
实现
function SuperType(name) { this.name = name; this.colors = ['red', 'orange', 'black']; } function SubType() { SuperType.call(this, 'tt'); } var instance = new SubType() var instance1 = new SubType() instance.colors // ['red', 'orange', 'black'] instance.name // tt instance.colors.push('green'); instance.colors // ['red', 'orange', 'black', 'green'] instance1.colors // ['red', 'orange', 'black']
优势:
call
能够指定不一样的超类)缺点:
伪经典继承(最经常使用的继承模式):将原型链和借用构造函数的技术组合到一块儿。使用原型链实现对原型属性和方法的继承,经过构造函数来实现对实例属性的继承
function SuperType(name) { this.name = name; this.colors = ['red', 'orange', 'black']; } SuperType.prototype.sayName = function() { return this.name } function SubType() { SuperType.call(this, 'tt'); this.name = 'oo'; } // 这里的 SubType.prototype.constructor 仍是指向 SuperType SubType.prototype = new SuperType(); var instance = new SubType(); var instance1 = new SubType(); instance.name // oo instance.sayName() // oo instance.colors.push('green'); instance.colors // ['red', 'orange', 'black', 'green'] instance1.colors // ['red', 'orange', 'black']
优势:
缺点:
借助原型链能够基于已有的对象建立新对象,同时还没必要所以建立自定义类型
function obj(o) { function F(){} F.prototype = o; return new F(); } var person = { name: 'tt', age: 18, colors: ['red', 'green'] } var instance = obj(person); var instance1 = obj(person); instance.colors.push('black'); instance.name // tt instance.colors // ['red', 'green', 'black'] instance1.colors // ['red', 'green', 'black']
建立一个临时的构造函数,而后将传入的对象当作这个构造函数的原型对象,最后返回这个临时构造函数的新实例。实际上,就是对传入的对象进行了一次浅复制
ES5
经过新增 Object.create()
规范化了原型式继承
更多 Object.create()语法请点击 这里
优势:
缺点: 和原型链继承基本一致,效率较低,内存占用高(由于要拷贝父类的属性)
建立一个仅用于封装继承过程的函数,在函数内部对这个对象进行改变,最后返回这个对象
function createAnother(obj) { var clone = Object(obj); clone.sayHi = function() { alert('Hi'); } return clone } var person = { name: 'tt', age: 18, friends: ['oo', 'aa', 'cc'], sayName() { return this.name } } var instance = createAnother(person) var instance1 = createAnother(person) instance.friends.push('yy') instance.name // 'tt' instance.sayHi() // Hi instance.friends // ["oo", "aa", "cc", "yy"] instance1.friends // ["oo", "aa", "cc", "yy"]
优势:
缺点:
借用构造函数来继承属性,经过原型链的混成形式来继承方法。经过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function inherit(subType, superType) { var obj = Object(superType.prototype); // 建立对象 obj.constructor = subType; // 指定constructor subType.prototype = obj; // 指定对象 } function SuperType(name) { this.name = name; this.colors = ['red', 'orange', 'black']; } SuperType.prototype.sayName = function() { return this.name } function SubType() { SuperType.call(this, 'tt'); this.name = 'oo'; } inherit(SubType, SuperType) var instance = new SubType() instance.name // oo instance.sayName // oo instance instanceof SubType // true instance instanceof SuperType // true SubType.prototype.constructor == SubType // true
堪称完美,只是实现稍微复杂一点
做为 JavaScript
最重要的概念之一,对于继承实现的方式方法以及它们之间的差别咱们仍是颇有必要了解的。
在实现继承的时候,拷贝
也是一种颇有效的方式,因为 JavaScript
简单数据类型与引用类型的存在,衍生出了 浅拷贝
与 深拷贝
的概念,那么它们又是什么,怎么去实现呢
且听下回分解,哈哈
周末愉快
最后,推荐一波前端学习历程,不按期分享一些前端问题和有意思的东西欢迎 star 关注 传送门
JavaScript 高级程序设计