pomerantsev / angular-training

An empty repo for creating tasks as issues
0 stars 0 forks source link

Understanding how Injectable Constructors like Controllers are instantiated... #6

Open al-the-x opened 10 years ago

al-the-x commented 10 years ago

This assignment is tangential to our discussion of compile.js and the inner-working of Directives, but since Directives employ several different types of Injectables -- Controllers, notably but also the Compile and PreLink / PostLink functions -- I think it's important to understand how $injector.instantiate() works. This will also take you farther along in your understanding of memory leaks in JavaScript and specifically Angular JS.

In your Directive assignment (#5), you identified the return value for the controller property as replacing the instantiated object:

    // A return value here would replace the object created by the constructor,
    // since a new object is being created with $injector.instantiate, using this function as a constructor.

Which is generally true in JavaScript when using the new operator:

However, Angular isn't just using the new statement to instantiate an Injectable, as we saw. There's some "magic" at work in invoke() that's worth investigating. Trace through the lifecycle of the Constructor and Type variables to determine what is actually returned by $injector.invoke.

pomerantsev commented 10 years ago

instantiate method appeared in c925f8a6578e05. The instantiation of a new object happens in two phases:

  1. In instantiate, a new object is created with an empty constructor, but the constructor’s prototype is set to be equal to Type’s prototype, so the prototype chain is correctly maintained.
  2. In invoke, the Type function is simply called via apply, but bound to the correct object (the one created in instantiate, and with correct arguments (services that the injector supplies and locals (like $scope). If we called new Type(...) inside instantiate, we would have to duplicate the dependency-injection code there, which is why we’re piggybacking on invoke.

Now, instantiate ends with this line: return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance. Which says: if the Type function returned something that is either an object or a function, return it. Otherwise, return the newly created instance. According to the commit message to c22adbf160, this mimics the behavior of the native new operator.