2

考虑以下示例:

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函数设置为未定义,比如说有人在某处重写了这个函数(这是我想要阻止的)。

在第二个run()块上,一切都是未定义的,将抛出错误。我对此感到困惑......至少其中一些constant首先想到的)不应该是不可变的对象吗?

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

4

2 回答 2

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);
于 2015-02-01T12:21:00.447 回答
1

函数constant,factoryservice允许您创建一次创建的 Javascript 对象,然后由 Angular 缓存,并在需要时注入组件。因为这些是 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').

于 2015-02-01T15:08:28.830 回答