Safe-guarding AngularJS scopes with ECMAScript 5 “Strict Mode”

Having a history as a Java developer I prefer declaring the complete JavaScript object at once through an object literal; in a similar fashion as your would declare a class in Java.
In my opinion adding new properties “on the fly” to a JavaScript object is a very bad practice:

For the same reasons I dislike how properties are declared in an AngularJS application:

Declaring scopes as object literals
In order to use an object literal to declare your scope one could use the following syntax:

To improve the construction above we could (instead of using angular.extend) add a declare function to all scope implementations.
This can be achieved easily by adding such a function to the $rootScope:

Now we can rewrite our scope declaration as follows:

Keeping this in scope
Using an object literal we can now use this to refer the scope instance from inside a function declared in the object literal:

The scope declared using the code above contains:

  • a property todoText  which is initialized to an empty string
  • a function addTodo that will reset the todoText property to its default value
Now have a look at the following dump of the internal contents of our scope:
In this screenshot (from the Chrome browser) you will notice that:
  • Our scope does indeed contain the todoText property and the addTodo function
    (as well as some other properties / functions omitted from our code sample)
  • The scope (at this specific moment) is a top-level scope, meaning it isn’t contained by any parent scope
  • The scope is a direct descendant from the $rootScope (which always has an $id of “002”)
But what if our scope would actually contain a nested TheChildCtrl scope like this:

This extra TheChildCtrl scope would actually introduce an issue in our addTodo function.
The this, used in the this.todoText = ''; statement, no longer would refer to the TodoCtrl scope but instead to the TheChildCtrl scope.

To illustrate this have look at following dump to see what the TheChildCtrl scope and its parent (= TodoCtrl) scope will look like after the “addTodo()” function was invoked:

The invocation of the addTodo function accidentally introduced a new todoText property in the TheChildCtrl scope.

To prevent this accidental introduction of properties we will modify our declare function to:

  • manually copy the properties from the object literal (instead of using angular.extend)
  • explicitly bind each function to the scope instance (using the angular.bind function) to enforce the this of each scope function:

Now that the “this” in back ‘in scope’ our “declare” function is fully functional.
To illustrate the benefit of the declare scope syntax I’ve rewritten the “Todo” sample from the AngularJS homepage in a before and after jsFiddle.

Safe-guarding your scopes
In order to ensure that no other properties can be assigned to our $scope than the properties of our object literal we can enhance our declare function to returned a “sealed” object instance through return Object.seal(this);.

The Object.seal function is part of EcmaScript 5 and will prevent future extensions to an object and protects existing properties from being deleted.
However sealing and object isn’t enough since by default no errors will be thrown or logged when the seal is violated.

To make object sealing useful we need to enable the “strict mode” from EcmaScript 5.
Enabling it will cause TypeError’s to be thrown when the seal is violated.
All modern browser except Internet Explorer (< IE10) support the usage of “strict mode”

To enable strict mode one needs to add the following ‘magic’ in front of every “.js” file:

Using object sealing combined with the ECMAScript 5 “strict mode” we can now make a much more advanced implementation of the “declare” function that:
  • enforces the actual usage of $scope.declare;
    the $scope instance supplied to the controller is of the special extension of the controller $scope of type “UndeclaredScope” which is sealed and only allows invocation of it’s declare function.
  • The declare function works in similar fashion as before but now it will also check if the browser actually supports “strict mode”;
    if this isn’t the case (like in Internet Explorer 9 or earlier) it will instead execute to the earlier described “basic” implementation of “declare”.
A complete “sealed” + “strict mode” implementation can be found in this jsFiddle.
It includes some commented-out code that all would raise a “TypeError” in case executed inside a “strict mode” browser.

3 thoughts on “Safe-guarding AngularJS scopes with ECMAScript 5 “Strict Mode”

  1. I was wondering if “declare” method would be available in isolated scopes in directives. These scopes to do not prototypally inherit from rootScope, they are constructed from scratch and simply hold a reference to it.

    • The “declare” method would indeed “not” be available through the prototype-chain, however it is still accessible through “scope.$parent.declare”.
      When writing the blog post I intended for using the “declare” method only inside controller declarations.

      • I took a look at the jsfiddle you provided. There, you define the “declare” method by decorating the $controller service. I used this technique in my application and seems to be working in all cases (both inherited and isolated scopes). I thing it is a great way to enhance the scope API with your custom, app-specific API. Thnx :)

Leave a Reply

Your email address will not be published. Required fields are marked *