Introducción
El inyector se utiliza para realizar la inyección automática de parámetros, por ejemplo
función fn ($ http, $ alcance, aservice) {}Cuando se ejecuta Ng, $ http, $ alcance, el aservice se pasará automáticamente como parámetros para la ejecución.
En realidad es fácil de resolver, el inyector hizo dos cosas
El siguiente código fuente analiza cómo implementar las dos cosas anteriores.
estructura
createInjector -> createInternalInjector return: instanceInjector
Entonces, createInjector () Devuelve InstanceInjector, la estructura es la siguiente:
{invocar: invocar, instanciar: instanciar, get: getService, anotate: annotate, ha: function (name) {return ProviderCache.hasownproperty (name + proveedoruffix) || Cache.HasownProperty (nombre); }}Análisis del código fuente
1. CreateInjector
función createInjector (moduleStoload, strictdi) {strictDi = (strictDi === true); var instantiating = {}, Providersuffix = 'Provider', Path = [], LoadedModules = new HashMap ([], true), // Pre-Confonfigure $ Pregunta para el servicio de registro de llamadas en LoadModules, etc. ProviderCache = {$ Proporcion: {Provider: SoportObject (Provider), Factory: SopkOn SupportObject (valor), constante: SoportObject (constante), decorador: decorador}}, // ProviderInjector, InstanceInjector Two inyectors // InstanceInjector proporciona servicio y otras inyecciones externamente, y ProviderInjector proporciona Provider Internamente para obtener ProviderInjector = (ProviderCache. $ inyectorminerr ('UNS', "Proveedor desconocido: {0}", Path.Join ('<-')); instanceInjector.Invoke (proveedor. $ get, proveedor, indefinido, nombre de servicio); // Módulo de carga foreach (LoadModules (ModuleStoload), function (fn) {instanceInjector.Invoke (fn || noop);}); return instanceInjector;}2. $ Proporcionar
$ Proporcionar: {Provider: SupportObject (Provider), Factory: SupportObject (Factory), Service: SupportObject (Service), Value: SupportOn2.1 SupportObject
Para los métodos de empaque, el método antes del empaque acepta dos parámetros (clave, valor). El método empaquetado puede admitir los parámetros del objeto que pasan, es decir, múltiples teclas -> valores.
function SupportObject (Delegate) {return function (key, value) {if (isObject (key)) {foreach (key, reverseParams (delegate)); } else {return delegate (clave, valor); }};}2.2 Proveedor
Revise cómo se utilizan el proveedor, el servicio y la fábrica
app.factory ('ServiceName', function () {return {getName: function () {}, setName: function () {}}}}); app.service ('serviceName', function () {this.getName = function () {} this.setName = function () {}); app.provider ('' serviceName, function ($ httprovider) {// inyect $ httpprovider this. $ get = function () {return {getName: function () {}, setname: function () {}}; afirmarnothasownproperty (nombre, 'servicio'); // Cuando el Provider_ es FN o Array, otros proveedores pueden inyectarse en parámetros // porque cuando ProviderInjector.instantiate (Provider_) se puede pasar a otros proveedores que dependen de // esta es también la diferencia entre el proveedor y el servicio y los métodos de fábrica if (isFunction (Provider_) || isArray (Provider_)) {Provider_ = ProviderInjectorRinjor. } if (! Provider _. $ get) {lanzar $ inyectorminerr ('pget', "proveedor '{0}' debe definir $ Get Method de fábrica.", Nombre); } return ProviderCache [Name + Providersuffix] = Provider _;} function factory (name, factoryFn) {return Provider (name, {$ get: factoryfn}); } servicio de funciones (nombre, constructor) {return factory (nombre, ['$ inyector', function ($ injector) {return $ injector.instantiate (constructor);}]);} valor valor (name, val) {return factory (name, valuefn (val)); }Finalmente resumido a la implementación del proveedor, almacene en caché el proveedor al ProviderCache para una llamada
Lo que es diferente de los demás es la implementación de Constant, que se guarda en ProviderCache e Instancecache, respectivamente, para que se pueda inyectar tanto el proveedor como el servicio.
función constante (nombre, valor) {afirmarnothasownproperty (nombre, 'constante'); ProviderCache [nombre] = valor; Instancecache [nombre] = valor;}2.3 Revisión de Modules de carga
función runinVokequeue (cola) {var i, ii; for (i = 0, ii = queue.length; i <ii; i ++) {var invokeargs = queue [i], proveedor = ProviderInjector.get (Invokeargs [0]); // El formato de la cola se almacena en [$ proporcionar, fábrica, argumentos] // después de reemplazo, $ proporcion.factory.apply ($ proporcionar, argumentos); // es llamar a $ Provid Factory, Service y otro proveedor [InvokearGS [1]]. Aplicar (proveedor, invokeargs [2]); }}2.4 decorador
Ejemplo:
MODULE.Config (function ($ proporcion) {$ show.Decorator ('mail', function ($ delegate) {$ delegate.addcc = function (cc) {this.cc.push (cc);}; return $ delegate;});})Usando el ejemplo, se puede ver que el parámetro $ delegado es la instancia de servicio original, y debe agregar un método a esta instancia, que es el llamado decorador
Código fuente:
Function Decorator (ServiceName, Decorfn) {var OrigProvider = ProviderInjector.get (ServiceName + Providersuffix), Orig $ get = OrigProvider. $ get; OrigProvider. $ get = function () {// Genere la instancia de servicio requerida a través del proveedor obtenido anteriormente, e inyectándolo en la lista de parámetros con $ delegate var originStance = instanceInjector.invoke (orig $ get, origprovider); return instanceInjector.invoke (decorfn, null, {$ delegate: originStance}); };}3. CreateInternalinjector
3.1 Estructura general
// Obtén de Cache, si no, llame a Factory para crearlo. Ver el análisis de getservice para obtener más detalles
función createInternalInjector (cache, fábrica) {function getService (serviceName) {} function Invoke (fn, self, locals, serviceName) {} function instantiate (type, locals, serviceName) {} return {// ejecutar fn, invocar: invoke, // Instantizar fn, instantiate: instantiate, // getSeviate o Service // Obtenga la lista de parámetros del método para la inyección de anotado: anotate, // confirma si contiene proveedor o servicio tiene: function (name) {return ProviderCache.hasownproperty (nombre + proveedor) || Cache.HasownProperty (nombre); }};}3.2 anotar
Obtenga la lista de parámetros de FN
// type1Function fn (a, b, c) -> ['a', 'b', 'c'] // type2 ['a', 'b', fn] -> [a ',' b '] // type3function fn () {} fn. $ inject = [' a ',' c '] -> [' a ']Código fuente:
función anotate (fn, strictdi, nombre) {var $ inject, fnText, argDecl, último; if (typeof fn === 'function') {if (! ($ inject = fn. $ inject)) {$ inject = []; if (fn.length) {// if (strictdi) {if (! isstring (nombre) ||! name) {name = fn.name || anonfn (fn); } lanzar $ inyectorminerr ('strictdi', '{0} no está usando anotación explícita y no se puede invocar en modo estricto', nombre); } // Eliminar el comentario fnText = fn.ToString (). Reemplazar (strip_comments, ''); // Seleccione todos los parámetros fn (a, b, c, d) -> 'a, b, c, d' argDecl = fntext.match (fn_args); // dividido en matriz foreach (argDecl [1] .split (fn_arg_split), function (arg) {arg.replace (fn_arg, function (all, subserscore, name) {$ inject.push (name);});}); }); } fn. $ inject = $ inject; }} else if (isarray (fn)) {last = fn.length - 1; ASSTERTARGFN (fn [último], 'fn'); $ inyect = fn.slice (0, último); } else {ASSTERTARGFN (fn, 'fn', true); } return $ inject;}3.3 GetService
// Cuando no hay servicio en el caché, ingrese más, caché [serviceName] = instanciating para hacer una marca // porque la próxima fábrica de llamadas (serviceName), es en realidad una llamada recursiva // function (serviceName) {// var Provider = ProviderInjector.get (serviceNeNeMeSfixUffix); // devolver la instancia.invoke (Provider. ServiceName); //} // instanceInjector.invoke (proveedor. $ get obtendrá los parámetros que deben inyectarse e inyectar //, por lo tanto, después de marcarlo, puede determinar si hay una función de dependencia circular getSeS getService (ServiceName) {if (cache.hasownpreperty (servicename)) $ inyectorminerr ('CDEP', 'Dependencia circular encontrada: {0}', ServiceName + '<-' + (ERR) {if (cache [ServiceName] === Instantante) {Eliminar caché [ServiceName];3.4 Invocar
function Invoke (fn, self, locals, serviceName) {if (typeof locals === 'string') {ServiceName = locals; locales = nulo; } var args = [], // Obtenga la lista de parámetros $ inyect = annotate (fn, strictdi, serviceName), longitud, i, key; para (i = 0, longitud = $ inject.length; i <longitud; i ++) {key = $ inject [i]; if (typeof key! == 'string') {throw $ injectMinerr ('itkn', 'token de inyección incorrecta! } // Locals prioridad args.push (locales && locals.hasownproperty (clave)? Locals [clave]: getService (key)); } if (isarray (fn)) {fn = fn [longitud]; } return fn.apply (self, args);}3.5 instanciar
function instantiate (type, locales, serviceName) {var constructor = function () {}, instancia, returnedValue; // Cuando el tipo es una matriz, obtenga el último parámetro, como: ['$ Window', function ($ win) {}] constructor.prototype = (isarray (type)? Type [type.length - 1]: type) .prototype; instancia = new Constructor (); // llamar a invocar para ejecutar el método de tipo returnDeDValue = invoke (tipo, instancia, locales, servicio de servicio); return isObject (returnedValue) || ISFUNTION (RETRONDEDVALUE)? returnedValue: instancia;}La función de instancia es instanciar el tipo, y los parámetros se pueden pasar automáticamente al constructor durante el proceso de instancia.