Introduction
L'injecteur est utilisé pour effectuer une injection de paramètres automatique, par exemple
fonction fn ($ http, $ scope, asservice) {}Lorsque NG s'exécute, $ http, $ scope, Asice sera automatiquement transmis en tant que paramètres d'exécution.
C'est en fait facile de le comprendre, l'injecteur a fait deux choses
Le code source suivant analyse comment implémenter les deux choses ci-dessus.
structure
createInjector -> createInternalInjector return: instanceInjector
Ainsi, CreateInjector () renvoie l'instanceInjector, la structure est la suivante:
{invoquer: invoquer, instancier: instanciate, get: getService, annotate: annotate, a: function (name) {return providerCache.hasownproperty (name + providersuffix) || cache.hasownproperty (nom); }}Analyse du code source
1. CreateInjector
fonction createInjector (moduleStOload, strictdi) {strictdi = (strictdi === true); var instanciation = {}, providersuffix = 'fournisseur', path = [], chardedModules = new hashmap ([], true), // pré-configurer $ fournir le service d'enregistrement d'appel dans LoadModules, etc. ProviderCache = {$ SupportObject (valeur), constant: supportObject (constant), décorateur: décorateur}}, // provideRinjecteur, instanceInjector deux injecteurs // instanceInjector fournit un service et d'autres injections externe, et provideRinjector fournit un fournisseur en interne pour obtenir un fournisseur "Proviseur inconnu: {0}", path.join ('<-'));}, strictDi)), instanceCache = {}, instanceInjector = (instanceCache. $ Injector = createInternAnjector (instanceCache, function (Servicename); instanceinject // Module de chargement ForEach (LoadModules (moduleStOload), fonction (fn) {instanceInjector.invoke (fn || noop);}); return instanceInjector;}2. $ Fournir
$ fournis: {fournisseur: supportObject (fournisseur), usine: supportObject (usine), service: supportObject (service), valeur: supportObject (valeur), constante: supportObject (constant), décorateur: décorateur}2.1 supportObject
Pour les méthodes d'emballage, la méthode avant l'emballage accepte deux paramètres (clé, valeur). La méthode emballée peut prendre en charge les paramètres de l'objet passant, c'est-à-dire plusieurs touches -> valeurs.
fonction supportObject (Delegate) {return function (key, value) {if (isObject (key)) {foreach (key, reverseParams (delegate)); } else {return Delegate (key, valeur); }};}2.2 fournisseur
Examiner comment le fournisseur, le service et l'usine sont utilisés
app.factory ('ServiceName', function () {return {getName: function () {}, setName: function () {}}}); app.service ('ServiceName', function () {this.getName = function () {} this.setName = function () {}}); app. function ($ httpprovider) {// injecter $ httpprovider this. $ get = function () {return {getName: function () {}, setName: function () {}}; AssertNothasownProperty (nom, «service»); // Lorsque le fournisseur_ est fn ou array, d'autres fournisseurs peuvent être injectés dans des paramètres // parce que lorsque provideRinjector.instantiate (provider_) peut être transmis dans d'autres fournisseurs qui dépendent // c'est aussi la différence entre le fournisseur et le service et les méthodes d'usine if (isfunction (provider_) || ISArray (provider_) {provider_ = provider } if (! Provider _. $ get) {throw $ injectorminerr ('pget', "fournisseur '{0}' doit définir $ get factory méthode.", name); } return providerCache [name + providersuffix] = provider _;} fonction factory (name, factoryfn) {return provider (name, {$ get: factoryfn}); } fonction du service (nom, constructeur) {return factory (name, ['$ injector', fonction ($ inject }Enfin résumé à la mise en œuvre du fournisseur, cachez le fournisseur au fournisseur pour appel
Ce qui est différent des autres, c'est la mise en œuvre de constante, qui est enregistrée respectivement dans ProviderCache et InstanceCache, de sorte que le fournisseur et le service peuvent être injectés.
Fonction constante (nom, valeur) {ASSERTNOTHASOWNPROPERTY (NAME, 'CONSTANT'); providerCache [name] = valeur; instanceCache [name] = valeur;}2.3 Revoir les modules de chargement
fonction runInvokequeue (file d'attente) {var i, ii; pour (i = 0, ii = queue.length; i <ii; i ++) {var invokeargs = file d'attente [i], provider = provideriNjecteur.get (invokEargs [0]); // Le format de la file d'attente est stocké dans [$ fournir, usine, arguments] // Après le remplacement, $ fournis.factory.apply ($ fournir, arguments); // Il s'agit d'appeler $ Provid Factory, Service et autres fournisseurs [invokeargs [1]]. Appliquer (fournisseur, invokeargs [2]); }}2.4 décorateur
Exemple:
module.config (function ($ fournis) {$ fournis.decorator ('mail', function ($ delegate) {$ delegate.addcc = function (cc) {this.cc.push (cc);}; return $ delegate;});})En utilisant l'exemple, on peut voir que le délégué du paramètre passé $ est l'instance de service d'origine, et vous devez ajouter une méthode à cette instance, qui est le soi-disant décorateur
Code source:
Function Decorator (ServiceName, décorfn) {var origprovider = provideriNjecteur.get (ServiceName + Providersuffix), orig $ get = origprovider. $ get; origprovider. $ get = function () {// générer l'instance de service requise via le fournisseur obtenu ci-dessus, et l'injectez-le dans la liste des paramètres avec $ delegate var originstance = instanceInject.invoke (orig $ get, origprovider); return instanceInjector.invoke (décorfn, null, {$ délégué: origine}); };}3. Créer un injection
3.1 Structure globale
// Obtenez-le de Cache, sinon, appelez l'usine pour le créer. Voir l'analyse GetService pour plus de détails
Fonction CreateInternalinjector (Cache, Factory) {fonction getService (ServiceName) {} function invoke (fn, self, locaux, ServiceName) {} function instanciate (type, locaux, serviceName) {} return}} // Obtenez la liste des paramètres de la méthode d'injection Annotate: Annotate, // Confirmez s'il contient le fournisseur ou le service a: fonction (nom) {return providerCache.hasownproperty (nom + providersuffix) || cache.hasownproperty (nom); }};}3.2 Annoter
Obtenez la liste des paramètres de FN
// type1function fn (a, b, c) -> ['a', 'b', 'c'] // type2 ['a', 'b', fn] -> ['a', 'b'] // type3function fn () {} fn. $ inject = ['a', 'c'] -> ['a', 'c']Code source:
fonction annotate (fn, strictdi, name) {var $ inject, fntext, argdecl, dernier; 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} n'utilise pas d'annotation explicite et ne peut pas être invoquée en mode strict', nom); } // Supprimez le commentaire fntext = fn.toString (). Remplace (strip_comments, ''); // sélectionnez tous les paramètres fn (a, b, c, d) -> 'a, b, c, d' argdecl = fntext.match (fn_args); // Split en array foreach (argdecl [1] .split (fn_arg_split), function (arg) {arg.replace (fn_arg, function (all, soulignement, nom) {$ inject.push (name);});}); }); } fn. $ inject = $ inject; }} else if (isArray (fn)) {last = fn.length - 1; assertargfn (fn [dernier], 'fn'); $ inject = fn.slice (0, dernier); } else {assertargfn (fn, 'fn', true); } return $ inject;}3.3 GetService
// Lorsqu'il n'y a pas de service dans le cache, entrez ailleurs, cache [ServiceName] = Instanciation pour faire une marque // parce que l'usine d'appel suivant (ServiceName), il s'agit en fait d'un appel récursif // fonction (ServiceName) {// var provider = provideriject ServiceName); //} // instanceInjector.invoke (fournisseur. $ get sortira les paramètres qui doivent être injectés et injectés // Par conséquent, après l'avoir marqué, vous pouvez déterminer s'il existe une fonction de dépendance circulaire getService (ServiceName) {if (cache.hasownproperty (ServiceName) {if (cache] $ injectorminerr (CDEP ',' Dépendance circulaire trouvée: {0} ', ServiceName +' <- '+ path.join (' <- '); (err) {if (cache [ServiceName] === instanciation) {delete cache [ServiceName];3.4 Invoquer
fonction invoke (fn, self, locaux, ServiceName) {if (typeof locaux === 'string') {ServiceName = locaux; Locaux = null; } var args = [], // Obtenez la liste des paramètres $ inject = annotate (fn, strictdi, ServiceName), la longueur, i, key; pour (i = 0, longueur = $ inject.length; i <longueur; i ++) {key = $ inject [i]; if (typeof key! == 'string') {throw $ injectorminerr ('itkn', 'token d'injection incorrect! Nom du service attendu comme chaîne, got {0}', key); } // Locals priority args.push (locaux && locaux.hasownproperty (key)? locaux [key]: getService (key)); } if (isArray (fn)) {fn = fn [longueur]; } return fn.apply (self, args);}3.5 Instancier
fonction instanciate (type, locaux, ServiceName) {var constructor = function () {}, instance, returnedValue; // Lorsque le type est le tableau, obtenez le dernier paramètre tel que: ['$ fenêtre', fonction ($ win) {}] constructor.prototype = (isArray (type)? Type [type.length - 1]: type) .prototype; instance = nouveau constructeur (); // Appel Invoke pour exécuter la méthode Type reternedValue = invoke (type, instance, locaux, ServiceName); return iSObject (returnedValue) || ISFUNCTION (RETOURDEDVALUE)? returnEdValue: instance;}La fonction d'Instanciate est d'instancier le type, et les paramètres peuvent être automatiquement transmis dans le constructeur pendant le processus d'instanciation.