A detailed analysis of the dependence injection $injector of angular.js learning _angularjs

Source: Internet
Author: User
Tags anonymous extend delete cache hasownproperty

Objective

Before the dependency injection (IoC), we need to create an object in the program is very simple and straightforward, that is, in the code new object, have our own responsibility to create, maintain, modify and delete, that is, we control the entire lifecycle of the object, until the object is not referenced, is recycled. True, this is understandable when there are fewer objects to create or maintain, but it is difficult to rely on programmers to maintain all objects when it comes to creating large quantities of objects in a large project, especially if you want to reuse objects throughout the lifecycle of a program, We need to write a cache module to cache all objects, which increases the complexity. Of course, the benefits of the IOC are not limited to this, they also reduce the coupling of dependencies, do not have to be referenced in code or can be used to manipulate dependencies.

In JS, we can introduce dependencies like this

1. Use global variable references

2. Pass the function parameter where needed

The disadvantage of using global variables without saying, polluting the global namespace, and passing the reference through the function can also be done in two ways:

1, Closure package delivery

2, the background to resolve the dependent objects, and through Function.prototype.call the reference

In Angularjs, dependency injection is implemented through the latter, and the following sections will introduce the implementation of the IOC module.

Get dependencies

var Fn_args =/^function\s*[^\ (]*\ (\s* ([^\)]*) \)/m;
 var fn_arg_split =/,/; Get Service name var Fn_arg =/^\s* (_?) (\s+?)
\1\s*$/; var strip_comments =/((\/\/.*$) | (
\/\*[\s\s]*?\*\/))/mg;

var $injectorMinErr = minerr (' $injector '); function Anonfn (FN) {//For anonymous functions, showing at the very least of the function signature can help in//Debugg
 Ing.
 var fntext = fn.tostring (). Replace (Strip_comments, ""), args = Fntext.match (Fn_args); if (args) {return ' function (' + args[1] | |
 '). Replace (/[\s\r\n]+/, ') + ') ';
Return ' FN ';

 Function Annotate (FN, STRICTDI, name) {var $inject, Fntext, Argdecl, last; if (typeof fn = = ' function ') {if (!) (
  $inject = fn. $inject)) {$inject = [];
   if (fn.length) {if (STRICTDI) {if (!isstring (name) | |!name) {name = Fn.name | | ANONFN (FN); } Throw $injectorMinErr (' strictdi ', ' {0} ' is not using explicit annotation and cannot being invoked in strict mode ', Nam
  e); } Fntext = Fn.tostring (). Replace (strip_comments, ');
  Argdecl = Fntext.match (Fn_args); ForEach (Argdecl[1].split (Fn_arg_split), function (ARG) {arg.replace (Fn_arg, function (all, underscore, name) {$INJEC
   T.push (name);
  });
  });
 fn. $inject = $inject;
 } else if (IsArray (FN)) {last = fn.length-1;
 ASSERTARGFN (Fn[last], ' FN ');
 $inject = Fn.slice (0, last);
 else {ASSERTARGFN (FN, ' fn ', true);
return $inject; }

annotatefunction through the analysis of the incoming argument, if a function is passed, then the dependent module is passed as an argument, at which point a regular match can be made through the serialization function, get the name of the dependent module and $inject return it to the array, and in strict mode, it throws an exception through the way of the function passing in the dependency. The second type of dependency passing is the array, and the last element of the array is the function that needs to be relied upon. annotateThe function eventually returns the resolved dependency name.

The creation of the injector

The Angularjs API also provides the injector part, through the injector section, through the injector can be used, and, get has instantiate invoke As mentioned in the previous section and annotate other methods, through the source can be clearer understanding.

function Createinternalinjector (cache, Factory) {//For service injector Providerinjector, only fetch service based on service name, factory throws exception function Getser Vice (serviceName, caller) {if (Cache.hasownproperty (serviceName)) {if (cache[servicename] = = instantiating) {th
  Row $injectorMinErr (' cdep ', ' circular dependency found: {0} ', ServiceName + ' <-' + path.join (' <-'));
  return Cache[servicename];
   else {try {path.unshift (serviceName);
   Cache[servicename] = instantiating;
  return Cache[servicename] = Factory (serviceName, caller);
   catch (Err) {if (cache[servicename] = = instantiating) {delete Cache[servicename];
  } Throw err;
  finally {path.shift ();
  }} function Invoke (FN, self, locals, serviceName) {if (typeof locals = = ' string ') {serviceName = locals;
  locals = null;

  var args = [],//parse and get injection Service list $inject = Annotate (FN, STRICTDI, serviceName), length, I, key;
 for (i = 0, length = $inject. length; i < length; i++) { key = $inject [i]; if (typeof key!== ' string ') {throw $injectorMinErr (' itkn ', ' incorrect injection ' token!
  Expected service name As String, got {0} ', key '; //injected service as parameter incoming Args.push (locals && locals.hasownproperty (key) Locals[key]: GetService (Key, serv
  Icename));
  } if (IsArray (FN)) {fn = fn[length];
 }//Http://jsperf.com/angularjs-invoke-apply-vs-switch//#5388 return fn.apply (self, args); The function instantiate (type, locals, serviceName) {//Check if Type is annotated and use just the given function at n
  -1 as parameter//e.g. somemodule.factory (' greeter ', [' $window ', function (Renamed$window) {}]); Object CREATION:HTTP://JSPERF.COM/CREATE-CONSTRUCTOR/2 var instance = object.create (IsArray (Type)?
  TYPE[TYPE.LENGTH-1]: Type). prototype);

  var returnedvalue = Invoke (Type, instance, locals, serviceName); Return IsObject (returnedvalue) | | Isfunction (returnedvalue)?
 Returnedvalue:instance; } return {INvoke:invoke, Instantiate:instantiate, Get:getservice, Annotate:annotate, has:function (name) {return provi Dercache.hasownproperty (name + providersuffix) | |
  Cache.hasownproperty (name);
 }
 }; }

createInternalInjectormethod to create a $injector object that passes the parameters of a cached object and a factory function, respectively. In a specific implementation, ANGULARJS created two injector objects--providerinjector and Instanceinjector ( The differences between the two objects are mainly the different cache objects passed by the Createinternalinjector method, and the Instanceinjector is derived by Angular.injector (). For Providerinjector, it is primarily used to obtain service providers, i.e. serviceprovider. For Instanceinjector, the main purpose is to perform the methods of provider objects obtained from Providerinjector $get , produce service objects (dependencies), and pass the service objects to the corresponding functions to complete the IOC.

First of all, from the Get method, the Getting method mainly obtains the service of the specified name, obtains the instanceinjector through the angular injector method, and when the service object (dependency) is not in the cache, we need to execute the factory(serviceName, caller) method. Let's look at the factory function for:

Instanceinjector = (instancecache. $injector =
   createinternalinjector (instancecache, function
(ServiceName, Caller) {var Provider = Providerinjector.get (ServiceName + providersuffix, caller); return
 Instanceinjector.invoke (provider. $get, provider, undefined, serviceName))
   ;

The red section is the factory function, which shows the provider serviceprovider that obtains the corresponding service through the Providerinjector, The Instanceinjector invoke method is then invoked to execute the ServiceProvider $get method in the serviceprovider context, returning the service object and saving it in the cache. This completes the acquisition and caching of the service objects (dependencies).

The Invoke method is also very simple, and its entry is asked,,,, that is, the fn self locals serviceName function to execute, the context in which the function is executed, the options option and the service name provided. First gets all the dependencies of the function, after the annotate method is completed, if the option provides a dependency on the name, it is used, otherwise the Get method gets the dependency, the last pass in the function, and returns the result of the function's execution. The result of the invoke return is often a service object.

The instantiate method creates an example, based primarily on the provided constructor, to be used as a dependency or provision service. It is worth mentioning that the object was not created through the new keyword, but rather the implementation of the prototype object provided by ECMA5 Object.create to inherit the function, very ingenious.

The has method is to determine sequentially whether the serviceprovider and service exist in the cache.

At this point, the $injector object is created.

Registration Service (dependent)

Services cannot come out of thin air, we need to implement them ourselves or externally to introduce services or dependencies. Therefore, the Registration service module is also worth examining. ANGULARJS provides a variety of registration services APIs, but we focus on the provider approach, and other factory,service methods are built on this.

These methods (Provider,factory, etc.) are bound to the providercache.provide object, and we pass Angular.module (′app′,[). Provider (...) The provider function, which is invoked during module loading (which is abstracted as an array, that is, [provider,method,arguments]), is bound to the internal invokequeue array, and eventually the Providercache.provide object, and we pass Angular.module (′app′,[]). Provider (...) The provider function, which is invoked during module loading (which is abstracted as an array, that is, [provider,method,arguments]), is bound to the internal invokequeue array, and eventually the The provider method is called on the Providercache.provide object, and other methods such as controller,directive are similar, but are bound to the Providercache.controllerprovider,providercache.controllerprovider,providercache.compileprovider the object.

function provider (name, Provider_) {assertnothasownproperty (name, ' service ');
 if (isfunction (provider_) | | | IsArray (PROVIDER_)) {provider_ = Providerinjector.instantiate (Provider_);
 } if (!provider_. $get) {throw $injectorMinErr (' Pget ', ' provider ' {0} ' must define $get factory method. ", name);
 return providercache[name + providersuffix] = Provider_; The function Enforcereturnvalue (name, factory) {return function enforcedreturnvalue () {var result = Instanceinjector.
  Invoke (factory, this); if (isundefined) {throw $injectorMinErr (' undef ', ' Provider ' {0} ' must return a value from $get factory method. "
  , name);
 return result;
 }; function factory (name, FACTORYFN, enforce) {return provider (name, {$get: Enforce!== false? Enforcereturnvalue (n)
 Ame, FACTORYFN): Factoryfn}); function service (name, constructor) {return factory (name, [' $injector ', function ($injector) {return $injector. ins
 Tantiate (constructor);
 }]); } function Value (NAMe, Val) {return factory (name, Valuefn (Val), false);
 function constant (name, value) {assertnothasownproperty (name, ' constant ');
 Providercache[name] = value;
 Instancecache[name] = value;
 ///Call (intercept) after service instantiation, an instantiated service in the Intercept function can be modified to extend the replacement service. function Decorator (ServiceName, DECORFN) {var origprovider = Providerinjector.get (ServiceName + providersuffix), orig$

 get = Origprovider. $get;
  Origprovider. $get = function () {var originstance = Instanceinjector.invoke (Orig$get, Origprovider);
 Return Instanceinjector.invoke (DECORFN, NULL, {$delegate: originstance});
 }; }

The provider method requires two parameters, one is the service name (the dependency name), the other is the factory method, or an array that contains the dependency and factory methods. First, an instance of the factory method is created by Providerinjector and added to the Providercache, which is returned.

The factory method simply encapsulates the second parameter into an object that contains the $get method, that is, the serviceprovider, the cache. is not complicated.

The service method, however, is nested into the $injector services, that is, Instanceinjector, which creates an instance of the constructor as the service object.

The value method encapsulates only one provider, whose $get method returns the value.

The constant method then stores value values in both Providercache and Instancecache, and does not require invoke to obtain their value.

A special and highly scalable decorator method is to add a blocking function after the serviceprovider get method, and to add an intercept function by passing the dependency on the Fetch method, and by passing the dependent delegate to obtain the original invoke $get The service object that the method returns. We can extend the service by decorator, delete and so on.

Process

Finally, on the basis of the basic implementation has been completed, we go through the specific injection process, more easily our in-depth understanding.

Angular.module ("App", [])
  . Provider ("Locationservice", function () {
  ...
 })
 . Controller ("Weathercontroller", Function ($scope, Locationservice, $location) {
  locationservice.getweather ()
   . Then (function (data) {
    $scope. Weather = Data
   },function (e) {
    console.log ("error message:" + e.message)
   })
 

We don't care about specific code implementations, just use the code above as a demo.

First, the scope of the ANGULARJS context is determined, and the dependency module is obtained (null here);

Continue to register the service (dependency) and cache the serviceprovider into the Providercache;

Declaration Controller;

Get the Injector sample here, by executing the Invoke function, get the [injector example, get the "scope", "Locationservice", "Location" dependency list by executing the Invoke function, Through the location "] dependency list, the corresponding dependent object is obtained through the injector get method. For scope and scope and location services, the ANGULARJS is injected into the angular when it is initialized, so the corresponding provider object can be obtained, and the associated method is executed to return the scope and scope and location objects. Locationservice is declared in provider, so gets to the Locationserviceprovider object and instanceInjector.invoke(locationServiceProvider.$get, locationServiceProvider, undefined, “locationService”) returns the Locationservice object by calling.

Finally, all the dependent assembly array [scope,locationservice,scope,locationservice,location] are passed as parameters to the anonymous function execution.

Summarize

Thus, dependency injection is complete. Do you have any further understanding of the dependency injection $injector? The above is the entire content of this article, I hope the content of this article for everyone's study or work can bring certain help, if you have questions you can message exchange.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.