如何在AngularJS中创建任何类型的不可变提供程序

时间:2015-02-01 11:33:57

标签: angularjs immutability angularjs-provider

考虑以下示例:

angular.module('demo')
    .service('MyService', function () {
        this.fn = function () {
            console.log('MyService:fn');
        };
    })
    .factory('MyFactory', function () {
        function fn() {
            console.log('MyFactory:fn');
        }
        return { fn: fn };
    })
    .value('MyValue', {
        fn: function () {
            console.log('MyValue:fn');
        }
    })
    .constant('MyConstant', {
        fn: function () {
            console.log('MyConstant:fn');
        }
    })
    .run(function (MyService, MyFactory, MyValue, MyConstant) {
        MyService.fn();
        MyFactory.fn();
        MyValue.fn();
        MyConstant.fn();

        MyService.fn = undefined;
        MyFactory.fn = undefined;
        MyValue.fn = undefined;
        MyConstant.fn = undefined;
    })
    .run(function (MyService, MyFactory, MyValue, MyConstant) {
        MyService.fn();
        MyFactory.fn();
        MyValue.fn();
        MyConstant.fn();
    });

执行第一个run()时,将执行所有4个控制台日志并在控制台上打印一些内容。然后我将每个提供者fn函数设置为undefined以简化目的,比如有人在某处重写了这个函数(这是我想要防止的)。

在第二个run()块上,所有内容都是未定义的,并且会抛出错误。我对此感到困惑......至少某些constant是第一个想到的)是不可变对象吗?

这是预期的行为还是我做错了什么?

2 个答案:

答案 0 :(得分:1)

为什么这是一个惊喜。 Angular是一个运行在Javascript之上的框架,Javascript是一种动态语言。你的问题实际上是关于语言结构的。

首先,要认识到所有调用在一天结束时都会注册一个提交程序,该提供程序将返回要注入的对象。 .service.factory.value只是.provider的短手(.constant略有不同)。

一旦注入了对象,确定它们之间没有区别,那么你需要关注的是如何使该对象不可变。

Javascript提供Object.freeze功能,例如,你可以这样做:

var immutable = {
        fn: function () {
            console.log('MyConstant:fn');
        }
    };
Object.freeze(immutable);

app.constant("MyConstant", immutable);

答案 1 :(得分:1)

函数constantfactoryservice等允许您创建一次创建的创建的Javascript对象,然后按角度缓存,并注入组件/需要时。因为这些是Javascript对象,然后(忽略使用freeze的可能性)任何代码访问这些对象都可以修改它们的属性,正如您所演示的那样。

尽管可以修改对象本身的属性,但是对象本身不能更改为另一个对象,所以如果你真的想要一个不可变的提供者,那么可以避免所有的篡改,我能想到的一种方法是使用不返回对象的工厂,而是使用getter函数:

app.factory('MyFactory', function() {
  var functions = {
    myFunctionName: function() {
    }
  };

  return function(functionName) {
    return functions[functionName];
  };
});

可以在控制器中使用,如

app.controller('MyController', function(MyFactory) {
  MyFactory('myFunctionName')();
});

使用这种方法的一个缺点是,比较传统的方式暴露所有方法并允许修改对象的可能性是我怀疑单元测试可能更复杂:你不能轻易地创建间谍您的工厂方法使用createSpy(MyFactory, 'myFunctionName')