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:

var jsLibrary = { name: 'AngularJS' };
// adds a new property "homepage" to the existing object...
jsLibrary.homepage = 'http://www.angularjs.org/';

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

$scope._<property-name>_ = _..._;

Declaring scopes as object literals

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

$scope = angular.extend($scope, {
    _<property-name>_: _..._,
    _<property-name>_: _..._ 
});

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:

angular.module('blog.jdriven', \[\])
  .run(function($rootScope) {
    $rootScope.declare = function(obj) {
      return angular.extend(this, obj);
    };
  });

Now we can rewrite our scope declaration as follows:

$scope = $scope.declare({
    _<property-name>_: _..._,
    _<property-name>_: _..._
});

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:

$scope = $scope.declare({
    // _..._
    todoText: '',

    addTodo: function() {
        // _..._
        this.todoText = '';
    }
    // _..._
});

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:

<div ng-controller="TodoCtrl">
  ...
  <div ng-controller="TheChildCtrl">
    <form ng-submit="addTodo()">

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:
  // ...
  $rootScope.declare = function(obj) {
    var self = this;
    angular.forEach(obj, function(value, key) {
      self\[key\] = angular.isFunction(value)
          ? angular.bind(self, value) : value;
    });
    return this;
  };
  // ...

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:

'use strict';

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.

shadow-left