Angularjs source code parsing: injector and angularjsinjector

Source: Internet
Author: User
Tags delete cache hasownproperty

Angularjs source code parsing: injector and angularjsinjector

Introduction

Injector is used for Automatic Parameter injection, for example

function fn ($http, $scope, aService) {}

Ng automatically transmits $ http, $ scope, and aService as parameters for execution at runtime.

In fact, it is easy to understand that injector has done two things.

  1. Cache those services and inject them as parameters later
  2. Analyze the parameter list and find the required parameter injection.

The following source code analyzes how to implement the above two things.

Structure

createInjector -> createInternalInjector  return: instanceInjector

Therefore, createInjector () returns instanceInjector with the following structure:

{ invoke: invoke, instantiate: instantiate, get: getService, annotate: annotate, has: function(name) {  return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); }}

Source code analysis

1. createInjector

Function createInjector (modulesToLoad, strictDi) {strictDi = (strictDi = true); var INSTANTIATING ={}, providerSuffix = 'provider', path = [], loadedModules = new HashMap ([], true), // pre-configured $ provide for loadModules to call providerCache such as registration service ={$ provide: {provider: supportObject (provider ), factory: supportObject (factory), service: supportObject (service), value: supportObject (value), constant: supportObject (constant), decorator: decorator}, // providerInjector, instanceInjector: Two injection collectors // instanceInjector provides external services and other injections, and providerInjector provides providers internally to obtain providerInjector = (providerCache. $ injector = createInternalInjector (providerCache, function () {throw $ injectorMinErr ('unpr', "Unknown provider: {0}", path. join ('<-') ;}, strictDi), instanceCache ={}, instanceInjector = (instanceCache. $ injector = createInternalInjector (instanceCache, function (servicename) {var provider = providerInjector. get (servicename + providerSuffix); return instanceInjector. invoke (provider. $ get, provider, undefined, servicename) ;}, strictDi); // load module forEach (loadModules (modulesToLoad), function (fn) {instanceInjector. invoke (fn | noop) ;}); return instanceInjector ;}

2. $ provide

$provide: {  provider: supportObject(provider),  factory: supportObject(factory),  service: supportObject(service),  value: supportObject(value),  constant: supportObject(constant),  decorator: decorator}

2.1 supportObject

The method is used for packaging. The method before packaging accepts two parameters (key, value). The encapsulated method supports the input of object parameters, that is, multiple key-> value.

function supportObject(delegate) { return function(key, value) {  if (isObject(key)) {   forEach(key, reverseParams(delegate));  } else {   return delegate(key, value);  } };}

2.2 provider

Review the usage of provider, service, and factory

App. factory ('servicename', function () {return {getName: function () {}, setName: function () {}}); app. service ('servicename', function () {this. getName = function () {} this. setName = function () {}}); app. provider ('servicename', function ($ httpProvider) {// inject $ httpProvider this. $ get = function () {return {getName: function () {}, setName: function () {}}}); app. provider ('servicename', {$ get: function () {}}); Function provider (name, provider _) {assertNotHasOwnProperty (name, 'service '); // when provider _ Is fn or array, other providers can be injected into the parameter. // because providerInjector. other dependent providers can be passed in when instantiate (provider _) // This is also the provider and service, where the factory method is different 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 factory (name, factoryFn) {return provider (name, {$ get: factoryFn });} function service (name, constructor) {return factory (name, ['$ injector', function ($ injector) {return $ injector. instantiate (constructor) ;}]);} function value (name, val) {return factory (name, valueFn (val ));}

Finally, it summarizes the implementation of the provider and caches the provider to providerCache for calling.

Unlike other constant implementations, the constant implementations are saved to providerCache and instanceCache respectively, so that both providers and services can be injected.

function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value;}

2.3 review loadModules

Function runInvokeQueue (queue) {var I, ii; for (I = 0, ii = queue. length; I <ii; I ++) {var invokeArgs = queue [I], provider = providerInjector. get (invokeArgs [0]); // The format of the stored queue, such as [$ provide, factory, arguments] // is replaced, $ provide. factory. apply ($ provide, arguments); // call $ provid's factory, service, and other provider [invokeArgs [1]. apply (provider, invokeArgs [2]) ;}}

2.4 decorator

Example:

module.config(function($provide) { $provide.decorator('Mail', function($delegate) {  $delegate.addCC = function(cc) {   this.cc.push(cc);  };  return $delegate; });})

The example shows that the input parameter $ delegate is the original service instance, and you can add methods to the instance, that is, the so-called decorator.

Source code:

Function decorator (serviceName, decorFn) {var origProvider = providerInjector. get (serviceName + providerSuffix), orig $ get = origProvider. $ get; origProvider. $ get = function () {// use the provider obtained above to generate the required service instance and inject it to the parameter list var origInstance = instanceInjector with $ delegate. invoke (orig $ get, origProvider); return instanceInjector. invoke (decorFn, null, {$ delegate: origInstance });};}

3. createInternalInjector

3.1 Overall Structure

// Obtain from the cache. If not, call the factory for creation. For details, refer to getService resolution.

Function createInternalInjector (cache, factory) {function getService (serviceName) {} function invoke (fn, self, locals, serviceName) {} function instantiate (Type, locals, serviceName) {} return {// execute fn, with the parameter injection function invoke: invoke, // instantiate fn, instantiate: instantiate can be injected with parameters, // get provider or service get: getService, // obtain the parameter list of the method for injection. annotate: annotate, // check whether provider or service has: function (name) {return providerCache exists. hasOwnProperty (name + providerSuffix) | cache. hasOwnProperty (name );}};}

3.2 annotate

Obtain the fn parameter list

// type1function fn (a, b, c) -> ['a', 'b', 'c']// type2['a', 'b', fn] -> ['a', 'b']// type3function fn () {}fn.$inject = ['a', 'c']-> ['a', 'c']

Source code:

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 be invoked in strict mode', name );} // remove the annotation fnText = fn. toString (). replace (STRIP_COMMENTS, ''); // select all parameters fn (a, B, c, d)-> 'a, B, c, d' argDecl = fnText. match (FN_ARGS); // split it into array 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 ;}

3.3 getService

// When the service is not in the cache, enter else. First cache [serviceName] = INSTANTIATING for a flag // because the following call factory (serviceName ), actually it is a recursive call // function (servicename) {// var provider = providerInjector. get (servicename + providerSuffix); // return instanceInjector. invoke (provider. $ get, provider, undefined, servicename); //} // instanceInjector. invoke (provider. $ get will get the parameters to be injected and then inject // After marking, you can determine whether there is a circular dependency on function getService (serviceName) {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 (serviceName); cache [serviceName] = INSTANTIATING; return cache [serviceName] = factory (serviceName);} catch (err) {if (cache [serviceName] === INSTANTIATING) {delete cache [serviceName];} throw err;} finally {path. shift ();}}}

3.4 invoke

Function invoke (fn, self, locals, serviceName) {if (typeof locals = 'string') {serviceName = locals; locals = null;} var args = [], // obtain the parameter 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 ', 'encrect injection token! Expected service name as string, got {0} ', key);} // locals priority args. push (locals & locals. hasOwnProperty (key )? Locals [key]: getService (key);} if (isArray (fn) {fn = fn [length];} return fn. apply (self, args );}

3.5 instantiate

Function instantiate (Type, locals, serviceName) {var Constructor = function () {}, instance, returnedValue; // when type is array, obtain the final parameter, for example: ['$ Window', function ($ win) {}] Constructor. prototype = (isArray (Type )? Type [Type. length-1]: Type ). prototype; instance = new Constructor (); // call invoke to execute the Type method returnedValue = invoke (Type, instance, locals, serviceName); return isObject (returnedValue) | isFunction (returnedValue )? ReturnedValue: instance ;}

Instantiate is used to instantiate Type. During the instantiation process, parameters can be automatically input to the constructor.

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.