Angular. JS learning dependency injection $ injector details, angular. jsinjector

Source: Internet
Author: User
Tags delete cache

Angular. JS learning dependency injection $ injector details, angular. jsinjector

Preface

Before dependency injection (IoC), it is very simple and straightforward to create an Object in the program, that is, to create a new Object in the code, we create, maintain, modify, and delete objects on our own. That is to say, we control the entire lifecycle of objects until they are not referenced and recycled. It is true that this practice is understandable when the number of objects created or maintained is small, but when a large project needs to create objects of an order of magnitude, it only relies on programmers to maintain all objects, this is hard to achieve, especially if we want to reuse some objects throughout the entire life cycle of the program, we need to write a cache module to cache all objects, which increases the complexity. Of course, the benefits of IoC are not limited to this. It also reduces the coupling between dependencies and does not need to be referenced or passed in the code to operate dependencies.

In js, we can introduce dependencies in this way.

1. Use global variables to reference

2. Pass function parameters as needed

The disadvantage of using global variables is that the global namespace is polluted, and the reference can be passed through the function parameter in two ways:

1. Closure Transfer

2. parse the dependency object in the background and useFunction.prototype.callPassing Parameters

In AngularJS, dependency injection is implemented through the latter. The following sections will introduce the implementation of the IoC module.

Obtain dependency

Var FN_ARGS =/^ function \ s * [^ \ (] * \ (\ s * ([^ \)] *) \)/m; var FN_ARG_SPLIT = /,/; // obtain the 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 the function signature can help in // debugging. 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) {v Ar $ 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 be invoked in strict mode', name );} 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) {$ inject. 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 ;}

annotateThe function performs targeted analysis on input parameters. If a function is passed, the dependent module is passed as the input parameter. In this case, you can perform regular matching using the serialization function to obtain the name of the dependent module and store it in$injectReturned in the array. In addition, an exception is thrown when the dependency is passed through the function input parameter in strict mode. In the second mode, the dependency is transmitted through the array, the last element of the array is the function that requires dependency.annotateThe function returns the parsed dependency name.

Create a Injector

AngularJS APIS also provide injector, which can be used through injector.get,has,instantiate,invokeAndannotateThrough the source code, you can have a clearer understanding.

Function createInternalInjector (cache, factory) {// For the service injector providerInjector, retrieve the service only according to the service name. The factory will throw an exception function getService (serviceName, caller) {if (cache. hasOwnProperty (serviceName) {if (cache [serviceName] === INSTANTIATING) {throw $ injectorMinErr ('cdep', 'Circular dependency found: {0 }', serviceName + '<-' + path. join ('<-');} return cache [serviceName];} else {try {path. unshift (servic EName); 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 obtain the list of injection services $ inject = anno Tate (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 ', 'encrect injection token! Expected service name as string, got {0} ', key);} // The injected service is passed into args. push (locals & locals. hasOwnProperty (key) as a parameter )? Locals [key]: getService (key, serviceName);} if (isArray (fn) {fn = fn [length];} // http://jsperf.com/angularjs-invoke-apply-vs-switch // #5388 return fn. apply (self, args);} 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 ', [' $ Windows', function (renamed $ window) {}]); // Obje Ct 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 providerCache. hasOwnProperty (name + providerSuffix) | cache. hasOwnProperty (name );}};}

createInternalInjectorMethod To create a $ injector object. The passed parameters are cache objects and factory functions. In specific implementation, AngularJS creates two injector objects-providerInjector and instanceInjector (the differences between the two objects are mainly because the cached objects passed by the createInternalInjector method are different), but through angular. injector () exports instanceInjector. ProviderInjector is mainly used to obtain the service provider, that is, serviceProvider. For instanceInjector, it is mainly used to execute the provider object obtained from providerInjector$getMethod, produce service objects (dependencies), and pass the service objects to the corresponding functions to complete IoC.

First of all, let's start with the get method. The get method mainly obtains the service with the specified name, and obtains the instanceInjector through the angular injector method. When this service object (dependency) is not in the cache, we need to executefactory(serviceName, caller)Method, let's look at the factory function:

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 part is the factory function. It shows that the provider serviceProvider of the corresponding service is obtained through providerInjector, and then the $ get method of serviceProvider is executed in the serviceProvider context by calling the invoke method of instanceInjector, return the service object and save it in the cache. In this way, the service object (dependency) is obtained and cached.

The invoke method is also very simple, and its input parameters are asked separatelyfn,self,locals,serviceNameThat is, the function to be executed, the context of the function execution, the options Option and service name provided. First, obtain all the dependency names of the function. After the annotate method is used, if the options provides the dependency on the name, use it. Otherwise, use the get method to obtain the dependency and pass in the function, return the execution result of the function. The result returned by invoke is often a service object.

The instantiate method mainly creates an example based on the provided constructor for dependency or service provision. It is worth mentioning that the object is not created through the new Keyword, but is provided through ECMA5Object.createTo inherit the implementation of the function's prototype object, which is very clever.

The has method successively checks whether serviceProvider and service exist in the cache.

So far, the $ injector object has been created.

Register a service (dependency)

Services cannot come out of thin air. We need to implement or introduce services or dependencies externally. Therefore, the registration service module is also worth further consideration. AngularJS provides a variety of Registration service APIs, But we focus on the provider method. Other factory and service methods are built based on this.

These methods (provider, factory, etc.) are boundproviderCache.provideObject, and we useangular.module(′app′,[]).provider(...)The provider function called by the method will be called during module loading (this call is abstracted into an array, that is, [provider, method, arguments]) bound to the inner invokeQueue array, and finally inproviderCache.provideObject, and we useangular.module(′app′,[]).provider(...)The provider function called by the method will be called during module loading (this call is abstracted into an array, that is, [provider, method, arguments]) bound to the inner invokeQueue array, and finally inproviderCache.provideThe provider method is called on the object. Other methods such as controller and directive are similar, but they are boundproviderCache.controllerProvider,providerCache.controllerProvider,providerCache.compileProviderObject.

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 _;} function enforceReturnValue (name, factory) {return function enforcedReturnValue () {var result = instanceInjector. invoke (factory, this); if (isUndefined (result) {throw $ injectorMinErr ('undef ', "Provider' {0} 'must return a value fr Om $ get factory method. ", name);} return result ;};} function factory (name, factoryFn, enforce) {return provider (name, {$ get: enforce! = False? EnforceReturnValue (name, factoryFn): factoryFn});} function service (name, constructor) {return factory (name, ['$ injector', function ($ injector) {return $ injector. instantiate (constructor) ;}]);} function value (name, val) {return factory (name, valueFn (val), false);} function constant (name, value) {assertNotHasOwnProperty (name, 'constant'); providerCache [name] = value; instanceCache [name] = v Alue;} // call (intercept) after the service is instantiated. Intercept the function to inject the instantiated service, which can be modified and extended to replace the 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: Service name (dependency name), factory method, or an array containing dependency and factory method. Create an instance of the factory method using providerInjector and add it to providerCache.

The factory method encapsulates the second parameter as an object containing the $ get method, namely serviceProvider, cache. Not complex.

The service method is nested and injected.$injectorA service, namely, instanceInjector, creates an instance of the constructor as a service object.

The value Method encapsulates only one provider.$getReturn value.

The constant method saves the value to providerCache and instanceCache respectively, and does not require invoke to obtain the value.

The special and highly scalable decorator method is to add an intercept function after the get method of serviceProvider and add an intercept function after passing the dependency get method, and pass the dependency delegate to get the originalinvoke $getService object returned by the method. We can use decorator to expand or delete services.

Process

Finally, based on the basic implementation, we will go through the specific injection process to make it easier for us to understand it in depth.

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 the specific code implementation. We just use the above Code as a demonstration.

First, determine the range of AngularJS context and obtain the dependency module (null here );

Continue to register the Service (dependency) and cache serviceProvider to providerCache;

Declare the Controller;

The injector example is obtained here. By executing the invoke function, the ["injector example" is obtained. By executing the invoke function, ["scope", "locationService", "location"] dependency list is obtained, use location "] dependency list to obtain the corresponding dependent object through the get method of injector. For scope, scope, and location services, AngularJS has been injected into Angular during initialization. Therefore, you can obtain the corresponding provider object and execute relevant methods to return scope, scope, and location objects, the locationService is declared in the provider. Therefore, the locationServiceProvider object is obtained by callinginstanceInjector.invoke(locationServiceProvider.$get, locationServiceProvider, undefined, “locationService”)Returns the locationService object.

Finally, all dependencies are assembled into arrays [scope, locationService, scope, locationService, location] and passed to the anonymous function for execution as parameters.

Summary

So far, dependency injection has been completed. Do you know more about dependency injection $ injector? The above is all about this article. I hope this article will help you in your study or work. If you have any questions, please leave a message.

Related Article

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.