前言作为 JavaScript 中最重要的内容之一,继承问题一直是我们关注的重点。那么你是否清晰地知道它的原理以及各种实现方式呢阅读这篇文章,你将知道:什么是继承实现继承有哪几种方式它们各有什么特点这里默认你已经清楚的知道构造函数、实例和原型对象之间的关系,如果并不是那么清晰,那么推荐你先阅读这篇文章 – JavaScript 中的原型与原型链如果文章中有出现纰漏、错误之处,还请看到的小伙伴多多指教,先行谢过以下↓概念继承(inheritance)是面向对象软件技术当中的一个概念。如果一个类别 B 继承自 另一个类别 A ,就把这个 B 称为 A的子类 ,而把 A 称为 B的父类别 也可以称 A是B的超类 。继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码 …更多)通过这些概念和图示我们不难知道继承可以在我们的开发中带来的便捷,那么在 JavaScript 中如何去实现继承呢?继承实现方式原型链继承利用原型让一个引用类型继承另一个引用类型的属性和方法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() // ooinstance instanceof SubType // trueinstance instanceof SuperType // ture以上的试验中,我们创建了两个构造函数 SuperType 和 SubType ,并且让 SubType 的原型指向 SuperType ,SubType 也就继承了 SuperType 原型对象中的方法。所以在创建 instance 实例的时候,实例本身也就具有了 SuperType 中的方法,并且都处在它们的原型链中SubType.prototype.constructor == SubType // falseSubType.prototype.constructor == SuperType // true需要注意的是:这个时候 SubType.prototype.constructor 是指向 SuperType 的,相当于重写了 SubType 的原型对象。用一张图表示:SubType.prototype 相当于 SuperType 的实例存在的,所以 SubType.prototype.constructor 就指向 SuperType原型继承的特点优点:简单、易于实现父类新增原型方法/原型属性,子类都能访问到非常纯粹的继承关系,实例是子类的实例,也是父类的实例缺点:无法实现多继承想要为子类 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 // ttinstance.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 还是指向 SuperTypeSubType.prototype = new SuperType();var instance = new SubType();var instance1 = new SubType();instance.name // ooinstance.sayName() // ooinstance.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 // ttinstance.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() // Hiinstance.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 // ooinstance.sayName // ooinstance instanceof SubType // trueinstance instanceof SuperType // trueSubType.prototype.constructor == SubType // true寄生组合继承的特点堪称完美,只是实现稍微复杂一点后记作为 JavaScript 最重要的概念之一,对于继承实现的方式方法以及它们之间的差异我们还是很有必要了解的。在实现继承的时候,拷贝 也是一种很有效的方式,由于 JavaScript 简单数据类型与引用类型的存在,衍生出了 浅拷贝 与 深拷贝 的概念,那么它们又是什么,怎么去实现呢且听下回分解,哈哈周末愉快最后,推荐一波前端学习历程,不定期分享一些前端问题和有意思的东西欢迎 star 关注 传送门参考文档JavaScript 高级程序设计JavaScript实现继承的几种方式