AngularJS多类继承类式服务创建遇阻,求自定义实现方案
Hey there! Let's break down what's going wrong with your approach and fix that multi-inheritance issue in your Angular 1.x project.
Why Your Original Method Failed
The core problem with using Object.assign on class prototypes is this:
- ES6 class prototype methods are non-enumerable by default.
Object.assign()only copies enumerable properties from source objects to the target. When you triedObject.assign({}, Object.getPrototypeOf(myTableInstance1), ...), you ended up with an empty object because none of the class methods were picked up.
You can test this yourself: run Object.keys(TableClass1.prototype) and it'll return an empty array, but Object.getOwnPropertyNames(TableClass1.prototype) will list all your method names (including the underscore-prefixed "private" ones).
Additionally, replacing an instance's prototype post-construction with Object.setPrototypeOf can lead to unexpected behavior in Angular's dependency injection system, and it doesn't properly handle the state from your table class instances.
Feasible Solutions for Multi-Inheritance/Mixins
Here are three practical approaches tailored to your use case:
1. Manual Non-Enumerable Method Copy
This approach copies all methods (including non-enumerable ones) from your table class prototypes to the main service instance, ensuring you have access to all public and "private" methods.
import TableClass1 from './tables/1'; import TableClass2 from './tables/2'; export default class MyMainClass { constructor(some, varArg, other, param) { // Keep references to your table instances for state access this.table1 = new TableClass1(some, varArg); this.table2 = new TableClass2(other, param); // Copy methods from both table prototypes to this instance this.copyPrototypeMethods(TableClass1.prototype, this); this.copyPrototypeMethods(TableClass2.prototype, this); } copyPrototypeMethods(sourceProto, targetInstance) { // Get ALL property names (including non-enumerable methods) Object.getOwnPropertyNames(sourceProto).forEach(prop => { if (prop !== 'constructor' && typeof sourceProto[prop] === 'function') { // Bind the method to the target instance to preserve `this` context targetInstance[prop] = sourceProto[prop].bind(targetInstance); } }); } // Add your main service-specific methods here mainServiceLogic() { console.log('Running complex main service logic'); } }
Note: If your table class methods depend on their own instance state, you may need to adjust them to reference this.table1/this.table2 instead of their own this.
2. Mixin Function for Class Prototype Injection
This method injects table class methods directly into the main class's prototype, so all instances of MyMainClass inherit them automatically.
// Reusable mixin helper function applyMixins(targetClass, sourceClasses) { sourceClasses.forEach(sourceClass => { Object.getOwnPropertyNames(sourceClass.prototype).forEach(prop => { if (prop !== 'constructor' && typeof sourceClass.prototype[prop] === 'function') { targetClass.prototype[prop] = sourceClass.prototype[prop]; } }); }); } import TableClass1 from './tables/1'; import TableClass2 from './tables/2'; class MyMainClass { constructor(some, varArg, other, param) { this.table1 = new TableClass1(some, varArg); this.table2 = new TableClass2(other, param); } mainServiceMethod() { console.log('Main service exclusive method'); } } // Inject table class methods into MyMainClass prototype applyMixins(MyMainClass, [TableClass1, TableClass2]); export default MyMainClass;
If table methods rely on their own instance state, override them in MyMainClass to proxy calls to the stored table instances:
_myPrivateMethod() { return this.table1._myPrivateMethod(); }
3. Proxy Pattern (Safest for State-Dependent Methods)
If you want to preserve the original table instance state and avoid this context confusion, use a proxy to forward method calls from the main service to the table instances.
import TableClass1 from './tables/1'; import TableClass2 from './tables/2'; export default class MyMainClass { constructor(some, varArg, other, param) { this.table1 = new TableClass1(some, varArg); this.table2 = new TableClass2(other, param); // Proxy all methods from both table instances this.proxyInstanceMethods(this.table1); this.proxyInstanceMethods(this.table2); } proxyInstanceMethods(sourceInstance) { const proto = Object.getPrototypeOf(sourceInstance); Object.getOwnPropertyNames(proto).forEach(prop => { if (prop !== 'constructor' && typeof proto[prop] === 'function') { // Forward calls to the original table instance, preserving its `this` this[prop] = (...args) => sourceInstance[prop](...args); } }); } }
This ensures that when you call myMainService._myPrivateMethod(), it runs the method on the original TableClass1 instance, keeping all its internal state intact.
Quick Angular 1.x Note
When registering your main service with Angular, make sure to handle dependency injection correctly:
angular.module('yourApp') .service('MyMainService', ['dependency1', 'dependency2', MyMainClass]);
内容的提问来源于stack exchange,提问作者Mattia Costamagna

