0

I unintentionally polluted a service with properties I thought I was assigning only to a scope. Example:

var ServiceFunction = function ServiceFunction(){
    this.greeting = 'Hello';
};

var TestController = function TestController(testService){
    var testCtrl = this;
    testCtrl.testService = testService;

    //here I make changes within the controller. This amends the service itself
    testCtrl.testService.newProperty = 'new value';
};

angular.module('test',[])
.service('testService',ServiceFunction)
.controller(['testService',TestController])

Here is a Plunker to demonstrate this behaviour:

https://plnkr.co/edit/nDkNfKmQRtcBuJ0NUcHo?p=preview

I know that JavaScript is pass by reference when it comes to passing in objects to functions. This is unless those objects are deep cloned.

I had assumed that something outside a service would have access to objects etc exposed by that service but only by value, and any changes to that service would have to be done by an api supplied by that service. It seems the only way to do this is by using angular.copy e.g. in the above example testCtrl.testService = angular.copy(testService);

I'm guessing this must be intended behaviour. If so, with the exception of performance benefits, I see this as a drawback.

Why does AngularJS allow this behaviour and not "blackbox" providers?

dewd
  • 4,380
  • 3
  • 29
  • 43
  • this is how JavaScript prototype inheritance works; functions are objects. Incidentally, providers are singletons, and using `angular.copy` to "clone" a provider could have unintended side effects. – Claies Jun 07 '16 at 10:21
  • It isn't the responsibility of the framework to keep the user from misunderstanding how JS works. Btw, what kind answer do you expect? – Estus Flask Jun 07 '16 at 10:24
  • @estus I just think if you're going to call something a service it should behave like a service. If I offer you a service of delivering a crate of milk to your house and you take that crate and remove a couple of bottles, I wouldn't expect 2 bottles to be removed from every crate in my van, or that you could change eveything in my van to cartons of yogurt. The answer I expect is one which might highlight a benefit I cannot myself see. – dewd Jun 07 '16 at 10:41
  • @Claies Yes, you're right providers are singletons and `angular.copy` would cause unintended side effects.I had thought a manual watcher would have to be used to track model changes. – dewd Jun 07 '16 at 10:44
  • your comments are appropriate, but misplaced. Angular can't change JavaScript into a typesafe language, and trying to make providers be typesafe without the language support for it would take more effort than the benefit it would provide, and make the result more cumbersome and less usable. – Claies Jun 07 '16 at 10:53
  • @dewd To be honest, their `constant`s are not really constant, too! As said above, Angular services are singletons, this is [the main concept](https://docs.angularjs.org/guide/services#services) behind Angular DI. If this feature doesn't suit your case, make a service a function and not an object and instantiate it when needed. – Estus Flask Jun 07 '16 at 11:05
  • @Claies I wasn't aware I was referring to type safety. In JS, an object is an object is an object. As you know, an age old question in JavaScript is being able to pass objects by value only, ie cloning them. There are hundreds of articles and snippets on the web showing this, and as we've stated, Angular provides that service itself. I'm not sure it would have taken much effort to distribute service objects in this way, and allow such objects to be sensitive to change, all without breaking the singleton paradigm. Encapsulation would allow the service to remain blackbox, exposing only the api. – dewd Jun 07 '16 at 11:22

1 Answers1

0

Concluding from the discussion in the comments under the question, it appears there is no specific reason for AngularJS to allow this behaviour other than that services have not been encapsulated with only an api to access them.

As such, normal rules of JavaScript apply, i.e. a service passed to a function, e.g. directive, controller is passed by reference to the service object and any changes to that reference update the object directly at source. Since services are singletons, the change is reflected throughout the app where the service is used.

There are performance benefits to this approach, since it is natural JavaScript behaviour.

dewd
  • 4,380
  • 3
  • 29
  • 43