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; }
annotate
function 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. annotate
The 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);
}
}; }
createInternalInjector
method 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.