🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
继承是面向对象中一个比较核心的概念,其他正统面相对象语言都会用两种方式实现继承:一个是接口;一个是继承。而ECMAScript只支持继承,不支持接口实现,而实现继承的方式需要依靠原型链来完成。 # 原型链继承 ``` function Box () { // 被继承的函数叫做超类(父类、基类) this.name = 'Lee' } function Desk () { // 继承的函数叫做子类(派生类) this.age = 100 } Desk.prototype = new Box() // Desk继承了Box,通过原型,形成链条。子类的原型指向父类的实例 var desk = new Desk() desk.age desk.name // 得到被继承的属性 ``` 使用原型链继承会存在以下两个问题: 1. 字面量重写原型会中断关系,使用引用类型的原型; 2. 子类无法给超类传递参数。 # 借用构造函数(对象冒充)继承 为了解决引用共享和超类无法传参的问题,我们采用一种叫借用构造函数的技术,或者称为对象冒充(伪造对象、经典继承)的技术来解决这两个问题。 ``` function Box (name, age) { this.name = name this.age = age } Box.prototype.family = '家庭' function Desk (name, age) { Box.call(this, name, age) // 对象冒充,给超类传参 } var desk = new Desk('Lee', 100) desk.name // Lee desk.age // 100 desk.family // undefined 对象冒充只能继承实例(构造函数)属性或方法,不能继承类(原型)属性或方法 ``` # 组合继承 借用构造函数虽然解决了刚才两种问题,但是没有原型,复用则无从谈起。所以,我们需要原型链+借用构造函数的模式,这种模式称为组合继承。 ``` function Box (age) { this.age = age } Box.prototype.run = function () { return '年龄是:' + this.age } function Desk (age) { Box.call(this, age) // 对象冒充 } Desk.prototype = new Box() var desk = new Desk(100) desk.run() ``` # 原型式继承 原型式继承借助原型并给予已有的对象创建新对象,同时不必因此创建自定义类型。 ``` function obj (o) { // 创建一个字面量函数,临时中转函数 function F () {} // 一个临时新建的对象(构造函数),用来存储传递进来的对象 F.prototype = o // 把字面量函数赋值给构造函数的原型 return new F() } // 字面量 相当于 var box = new Box() var box = { name: 'Lee', age: 100 } var box1 = obj(box) ``` # 寄生式继承(原型式+工厂模式) 目的是为了封装创建对象的过程 ``` /** 临时中转函数 */ function obj (o) { // 创建一个字面量函数,临时中转函数 function F () {} // 一个临时新建的对象(构造函数),用来存储传递进来的对象 F.prototype = o // 把字面量函数赋值给构造函数的原型 return new F() } /** 寄生函数 */ function create (o) { var f = obj(o) // 此处可以对f进行扩展,例如下面给f扩展一个run函数 f.run = function () { return this.name } return f } // 字面量 相当于 var box = new Box() var box = { name: 'Lee', age: 100 } var box1 =create(box) ``` # 寄生组合继承 组合式继承是javascript最常用的继承模式,但是组合式继承也有一点小问题,就是超类在使用过程中会被调用两次: 一次是创建子类的时候; 一次是在子类构造函数的内部 ``` function Box (age) { this.age = age } Box.prototype.run = function () { return '年龄是:' + this.age } function Desk (age) { Box.call(this, age) // 第二次调用Box } Desk.prototype = new Box() // 第一次调用Box var desk = new Desk(100) desk.run() ``` 以上带面是之前的组合式继承,那么寄生组合继承,解决了两次调用的问题 ``` function obj (o) { function F () {} F.prototype = o return new F() } function create (box, desk) { var f = obj(box.prototype) f.constructor = desk desk.prototype = f } function Box (name, age) { this.name = name this.age = age } Box.prototype.run = function () { return this.name + this.age } function Desk (name, age) { Box.call(this, name, age) } // 通过寄生组合继承来实现继承 create(Box, Desk) // 这句话用来替代Desk.prototype = new Box() var desk = new Desk() desk.run() ```