如何正确扩展AngularJS工厂?解决方法作用域异常问题
看起来你遇到的是工厂方法作用域绑定错误的问题——当你用angular.extend把TestFactory的方法复制到NewFactory时,那些方法依然绑定在TestFactory的原始作用域上,所以调用screech()时会用TestFactory里的name和lastname(也就是null),而不是NewFactory里的。
先拆解下问题根源:angular.extend只是做浅拷贝,把TestFactory的方法引用直接复制到NewFactory的对象上,但方法内部的闭包变量还是属于TestFactory的私有作用域,完全访问不到NewFactory里的变量。下面给你几个可行的解决方案:
方案1:重构为基于this的原型继承(最规范的继承方式)
先把TestFactory改成可实例化的构造函数,用this来引用属性,这样子类可以通过原型继承方法,且方法里的this会指向子类实例:
// 基础TestFactory angular.module('myApp').factory('TestFactory', function() { function TestFactory() { this.name = null; this.lastname = null; } TestFactory.prototype.setNames = function(first, last) { this.name = first; this.lastname = last; }; TestFactory.prototype.screech = function() { alert(this.name + ' ' + this.lastname); }; return TestFactory; }); // 扩展的NewFactory angular.module('myApp').factory('NewFactory', function(TestFactory) { function NewFactory() { // 调用父类构造函数初始化基础结构 TestFactory.call(this); // 设置自己的默认值 this.name = 'Cool'; this.lastname = 'Guy'; } // 继承父类的所有原型方法 NewFactory.prototype = Object.create(TestFactory.prototype); // 修正构造函数指向,避免原型链混乱 NewFactory.prototype.constructor = NewFactory; // 可选:如果需要重写screech逻辑,直接覆盖即可 // NewFactory.prototype.screech = function() { // alert(`自定义弹窗:${this.name} ${this.lastname}`); // }; return new NewFactory(); });
现在调用NewFactory.screech()时,this会指向NewFactory的实例,自然会弹出"Cool Guy"。
方案2:组合替代继承(Angular中更灵活的实践)
如果不想改TestFactory的结构,可以用组合的方式——NewFactory内部持有TestFactory的实例,自己控制变量和方法的逻辑:
angular.module('myApp').factory('NewFactory', function(TestFactory) { // NewFactory自己的私有变量 var name = 'Cool'; var lastname = 'Guy'; return { // 复用TestFactory的setNames逻辑,但更新自己的变量 setNames: function(first, last) { name = first; lastname = last; }, // 自己实现screech,或者复用TestFactory的方法 screech: function() { // 直接用自己的变量弹窗 alert(name + ' ' + lastname); // 或者如果想复用TestFactory的screech逻辑: // TestFactory.setNames(name, lastname); // TestFactory.screech(); } }; });
这种方式完全避开了作用域绑定的坑,逻辑更清晰,也符合Angular中"服务组合优先于继承"的实践思路。
方案3:绑定方法上下文(快速修复)
如果只是想快速解决现有代码的问题,可以把TestFactory的方法绑定到NewFactory的作用域上:
angular.module('myApp').factory('NewFactory', function(TestFactory) { var name = 'Cool'; var lastname = 'Guy'; // 复制TestFactory的方法,同时绑定当前作用域的变量 var newFactory = angular.extend({}, TestFactory, { screech: function() { alert(name + ' ' + lastname); } }); return newFactory; });
本质是直接重写screech方法,让它访问NewFactory的私有变量,这种方式适合小范围的快速调整。
内容的提问来源于stack exchange,提问作者init fail

