ES5 实现 ES6 类继承:深度解析与完整实现 ES6 类继承回顾 ES6 类继承示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Animal { constructor (name ) { this .name = name; } speak ( ) { console .log (`${this .name} makes a noise.` ); } static info ( ) { console .log ('Animal class' ); } } class Dog extends Animal { constructor (name, breed ) { super (name); this .breed = breed; } speak ( ) { super .speak (); console .log (`${this .name} barks!` ); } static info ( ) { super .info (); console .log ('Dog class' ); } } const rex = new Dog ('Rex' , 'Labrador' );rex.speak (); Dog .info ();
ES5 实现 ES6 类继承 完整实现方案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 function Animal (name ) { this .name = name; } Animal .prototype .speak = function ( ) { console .log (this .name + ' makes a noise.' ); }; Animal .info = function ( ) { console .log ('Animal class' ); }; function Dog (name, breed ) { Animal .call (this , name); this .breed = breed; } Dog .prototype = Object .create (Animal .prototype );Dog .prototype .constructor = Dog ;Dog .prototype .speak = function ( ) { Animal .prototype .speak .call (this ); console .log (this .name + ' barks!' ); }; Object .setPrototypeOf (Dog , Animal );Dog .info = function ( ) { Animal .info .call (this ); console .log ('Dog class' ); }; var rex = new Dog ('Rex' , 'Labrador' );rex.speak (); Dog .info ();
关键继承技术详解 1. 原型链继承 1 2 3 4 5 6 Child .prototype = new Parent ();Child .prototype = Object .create (Parent .prototype );
2. 构造函数继承 1 2 3 4 function Child ( ) { Parent .call (this , arguments ); }
3. 组合继承(经典模式) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Parent (name ) { this .name = name; } Parent .prototype .say = function ( ) { console .log ('Hello, ' + this .name ); }; function Child (name, age ) { Parent .call (this , name); this .age = age; } Child .prototype = new Parent (); Child .prototype .constructor = Child ;
4. 寄生组合继承(最优方案) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function inherit (Child, Parent ) { var prototype = Object .create (Parent .prototype ); prototype.constructor = Child ; Child .prototype = prototype; Object .setPrototypeOf (Child , Parent ); } function Child ( ) { Parent .apply (this , arguments ); } inherit (Child , Parent );
各种继承方式对比
继承方式
优点
缺点
原型链
实例属性
原型链继承
简单
1. 引用类型共享问题 2. 无法传参
✅
❌
构造函数继承
1. 解决引用类型问题 2. 可传参
无法继承原型方法
❌
✅
组合继承
1. 实例属性独立 2. 方法复用
父类构造函数调用两次
✅
✅
原型式继承
简单对象继承
引用类型共享问题
✅
❌
寄生式继承
增强对象
方法不能复用
❌
❌
寄生组合继承
最优方案(ES5实现ES6类继承)
实现稍复杂
✅
✅
ES6类继承
语法简洁
需要现代JS环境
✅
✅
特殊继承场景处理 1. 多继承实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function mixin (target, ...sources ) { Object .assign (target.prototype , ...sources.map (src => src.prototype )); return target; } const CanSwim = { swim ( ) { console .log (`${this .name} is swimming` ); } }; const CanFly = { fly ( ) { console .log (`${this .name} is flying` ); } }; function Animal (name ) { this .name = name; } function Duck (name ) { Animal .call (this , name); } Duck .prototype = Object .create (Animal .prototype );Duck .prototype .constructor = Duck ;mixin (Duck , CanSwim , CanFly );const donald = new Duck ('Donald' );donald.swim (); donald.fly ();
2. 静态属性和方法继承 1 2 3 4 5 6 7 8 9 10 11 function Parent ( ) {}Parent .staticProp = 'Parent Static' ;Parent .staticMethod = function ( ) { console .log ('Parent static method' ); }; function Child ( ) {}Object .setPrototypeOf (Child , Parent );console .log (Child .staticProp ); Child .staticMethod ();
3. 私有字段模拟 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const privateData = new WeakMap ();class Animal { constructor (name ) { privateData.set (this , { name }); } getName ( ) { return privateData.get (this ).name ; } } function Animal (name ) { const privateFields = { name }; this .getName = function ( ) { return privateFields.name ; }; }
原型链关系图解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 graph TD A[rex: Dog实例] --> B[[__proto__]] B --> C[Dog.prototype] C --> D[[constructor]] D --> E[Dog构造函数] C --> F[[__proto__]] F --> G[Animal.prototype] G --> H[[constructor]] H --> I[Animal构造函数] G --> J[[__proto__]] J --> K[Object.prototype] K --> L[[__proto__]] L --> M[null] E --> N[[__proto__]] N --> O[Animal构造函数] O --> P[[__proto__]] P --> Q[Function.prototype] style A fill:#f9f,stroke:#333 style E fill:#bbf,stroke:#333 style I fill:#bbf,stroke:#333
继承的现代演进 1. ES6 类继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Animal { constructor (name ) { this .name = name; } speak ( ) { console .log (`${this .name} makes a noise.` ); } } class Dog extends Animal { constructor (name, breed ) { super (name); this .breed = breed; } speak ( ) { super .speak (); console .log (`${this .name} barks!` ); } }
2. 使用 Reflect 优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function Parent (name ) { this .name = name; } function Child (name, age ) { const instance = Reflect .construct (Parent , [name], new .target ); instance.age = age; return instance; } Object .setPrototypeOf (Child , Parent );Object .setPrototypeOf (Child .prototype , Parent .prototype );const child = new Child ('Alice' , 10 );console .log (child instanceof Parent );
3. 使用 class 语法糖转译 Babel 将 ES6 类转译为 ES5 的代码结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Child extends Parent {}function _inherits (subClass, superClass ) { subClass.prototype = Object .create (superClass && superClass.prototype , { constructor : { value : subClass, writable : true , configurable : true } }); if (superClass) _setPrototypeOf (subClass, superClass); } function _setPrototypeOf (o, p ) { _setPrototypeOf = Object .setPrototypeOf || function _setPrototypeOf (o, p ) { o.__proto__ = p; return o; }; return _setPrototypeOf (o, p); } var Child = (function (_Parent ) { _inherits (Child , _Parent); function Child ( ) { _classCallCheck (this , Child ); return _Parent.apply (this , arguments ) || this ; } return Child ; })(Parent );
实际应用场景 1. UI 组件继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function UIComponent ( ) { this .element = document .createElement ('div' ); } UIComponent .prototype .render = function ( ) { return this .element ; }; function Button (text ) { UIComponent .call (this ); this .text = text; this .element .textContent = text; this .element .className = 'btn' ; } Button .prototype = Object .create (UIComponent .prototype );Button .prototype .constructor = Button ;Button .prototype .onClick = function (handler ) { this .element .addEventListener ('click' , handler); }; const myButton = new Button ('Click Me' );document .body .appendChild (myButton.render ());myButton.onClick (() => alert ('Clicked!' ));
2. 数据模型继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function Model ( ) {}Model .prototype .save = function ( ) { console .log (`Saving ${this .constructor.name} to database` ); }; function User (name, email ) { this .name = name; this .email = email; } User .prototype = Object .create (Model .prototype );User .prototype .constructor = User ;User .prototype .sendWelcomeEmail = function ( ) { console .log (`Sending welcome email to ${this .email} ` ); }; const user = new User ('Alice' , 'alice@example.com' );user.save (); user.sendWelcomeEmail ();
面试重点问题解答 1. new 关键字执行过程 1 2 3 4 5 6 7 8 9 10 11 12 13 function myNew (Constructor, ...args ) { const obj = Object .create (Constructor .prototype ); const result = Constructor .apply (obj, args); return result instanceof Object ? result : obj; } const rex = myNew (Dog , 'Rex' , 'Labrador' );
2. instanceof 原理实现 1 2 3 4 5 6 7 8 9 10 11 12 13 function myInstanceof (obj, constructor ) { let proto = Object .getPrototypeOf (obj); while (proto) { if (proto === constructor.prototype ) return true ; proto = Object .getPrototypeOf (proto); } return false ; }
3. 为什么寄生组合继承是最佳方案?
效率高 :只调用一次父类构造函数
原型链纯净 :子类原型不包含父类实例属性
完整性 :保留完整的原型链关系
无冗余 :实例属性不会重复创建
支持静态继承 :通过 Object.setPrototypeOf 实现
总结与最佳实践 继承选择指南
现代项目 :优先使用 ES6 类继承
旧浏览器支持 :使用寄生组合继承
功能混入 :使用 Mixin 模式
私有字段 :使用 WeakMap 或闭包
最佳实践原则
遵循 LSP 原则 :子类应能替换父类
避免深度继承 :继承层次不超过 3 层
多用组合少用继承 :优先考虑对象组合
封装变化 :将变化点封装在子类中
保持一致性 :子类不应改变父类行为
1 2 3 4 5 6 7 graph LR A[继承需求] --> B{选择方案} B -->|ES6环境| C[ES6类继承] B -->|ES5环境| D[寄生组合继承] B -->|多继承需求| E[Mixin模式] B -->|共享方法| F[原型链继承] B -->|实例隔离| G[构造函数继承]