angularjsangularjs-serviceangularjs-factoryangularjs-provider

AngularJS - Polluting provider, services, and factories externally


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?


Solution

  • 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.