Perkenalan
Injektor digunakan untuk melakukan injeksi parameter otomatis, misalnya
fungsi fn ($ http, $ scope, aservice) {}Ketika NG berjalan, $ http, $ scope, aservice akan secara otomatis diteruskan sebagai parameter untuk dieksekusi.
Sebenarnya mudah untuk mengetahuinya, injektor melakukan dua hal
Kode sumber berikut menganalisis cara mengimplementasikan dua hal di atas.
struktur
createInjector -> createInternalInjector return: instanceInjector
Jadi createInjector () mengembalikan instanceInjector, strukturnya adalah sebagai berikut:
{Invoke: Invoke, Instantiate: Instantiate, Get: GetService, Annotate: Annotate, memiliki: function (name) {return providerCache.hasownproperty (name + providersuffix) || cache.hasownproperty (nama); }}Analisis Kode Sumber
1. CreateInjector
function createInjector (ModuleStOLoad, strictdi) {strictdi = (strictdi === true); var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], loadedModules = new HashMap([], true), // Pre-configure $provide for call registration service in loadModules, etc. ProviderCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: SupportObject (Value), Constant: SupportObject (Constant), Decorator: Decorator}}, // ProviderInjector, InstanceInjector Two Injectors // InstanceInjector menyediakan layanan dan suntikan lain secara eksternal, dan ProviderInjector menyediakan penyedia internal untuk mendapatkan providerInjector = (providerCache. $ injectorminerr ('unpr', "penyedia yang tidak diketahui: {0}", path.join ('<-')); Providersuffix); // memuat modul foreach (loadModules (ModuleStOload), function (fn) {instanceInjector.invoke (fn || noop);}); return instanceInjector;}2. $ Menyediakan
$ menyediakan: {penyedia: supportObject (penyedia), pabrik: supportObject (pabrik), layanan: supportObject (layanan), nilai: supportObject (nilai), konstan: supportObject (konstan), dekorator: dekorator}2.1 SupportObject
Untuk metode pengemasan, metode sebelum kemasan menerima dua parameter (kunci, nilai). Metode yang dikemas dapat mendukung parameter objek yang lewat, yaitu, beberapa tombol -> nilai.
function supportObject (delegate) {return function (key, value) {if (isObject (key)) {foreach (key, reverseParams (delegate)); } else {return delegate (key, value); }};}2.2 Penyedia
Tinjau bagaimana penyedia, layanan, dan pabrik digunakan
app.factory ('serviceName', function () {return {getName: function () {}, setName: function () {}}}); app.service ('serviceName', function () {this.getName = function () {} this.setName = function () {{{}); function ($ httpprovider) {// menyuntikkan $ httpprovider ini. $ get = function () {return {getName: function () {}, setName: function () {} {{) {); assertnothasownproperty (nama, 'layanan'); // Ketika Provider_ adalah fn atau array, penyedia lain dapat disuntikkan ke dalam parameter // karena ketika providerInjector.instantiate (provider_) dapat diteruskan ke penyedia lain yang bergantung pada // ini juga perbedaan antara penyedia dan layanan dan metode pabrik jika (provider_) || isarray (penyedia_)) {penyedia layanan. } if (! penyedia _. $ get) {throw $ injectorminerr ('pget', "penyedia '{0}' harus mendefinisikan $ get metode pabrik.", name); } return providerCache [name + providersuffix] = penyedia _;} factory fungsi (name, factoryfn) {return penyedia (nama, {$ get: factoryfn}); } layanan fungsi (nama, konstruktor) {return factory (name, ['$ injector', function ($ injector) {return $ injector.instantiate (konstruktor);}]);} nilai fungsi (name, val) {return factory (name, valuefn (val)); }Akhirnya dirangkum untuk implementasi penyedia, cache penyedia providercache untuk panggilan
Yang berbeda dari yang lain adalah implementasi konstan, yang masing -masing disimpan dalam ProviderCache dan InstanceCache, sehingga penyedia dan layanan dapat disuntikkan.
function constant (name, value) {assertNothasOwnProperty (name, 'constant'); ProviderCache [name] = nilai; Instancecache [name] = value;}2.3 Tinjau LoadModules
fungsi runinvokequeue (antrian) {var i, ii; untuk (i = 0, ii = queue.length; i <ii; i ++) {var invokeargs = queue [i], provider = providerInjector.get (InvokeGeARGS [0]); // Format antrian disimpan dalam [$ berikan, pabrik, argumen] // setelah penggantian, $ cust.factory.apply ($ berikan, argumen); // Ini untuk menghubungi $ Provid Factory, Service dan penyedia lainnya [InvokeARGS [1]]. Ajukan (penyedia, Invokeargs [2]); }}2.4 Dekorator
Contoh:
module.config (function ($ invice) {$ advert.decorator ('mail', function ($ delegate) {$ delegate.addcc = function (cc) {this.cc.push (cc);}; return $ delegate;});}))Dengan menggunakan contoh tersebut, dapat dilihat bahwa parameter $ delegasi yang dilewati adalah instance layanan asli, dan Anda perlu menambahkan metode ke contoh ini, yang merupakan dekorator yang disebut
Kode Sumber:
Fungsi dekorator (serviceName, dekorfn) {var origprovider = providerInjector.get (serviceName + providersuffix), orig $ get = origprovider. $ get; origprovider. $ get = function () {// Hasilkan instance layanan yang diperlukan melalui penyedia yang diperoleh di atas, dan menyuntikkannya ke dalam daftar parameter dengan $ delegate var originstance = instanceInjector.invoke (orig $ get, origprovider); return instanceInjector.invoke (dekorfn, null, {$ delegate: originstance}); };}3. Createinternalinjector
3.1 Struktur keseluruhan
// Dapatkan dari cache, jika tidak, hubungi pabrik untuk membuatnya. Lihat Analisis Getservice untuk detailnya
function createInternalInjector(cache, factory) { function getService(serviceName) { } function invoke(fn, self, locals, serviceName){ } function instantiate(Type, locals, serviceName) { } return { // Execute fn, invoke: invoke, // Instantize fn, instantiate: instantiate, // Get the provider or service get: getService, // Dapatkan daftar parameter metode untuk anotasi injeksi: annotate, // konfirmasi apakah itu berisi penyedia atau layanan memiliki: fungsi (nama) {return providercache.hasownproperty (name + providerSuffix) || cache.hasownproperty (nama); }};}3.2 Anotasi
Dapatkan daftar parameter FN
// type1function fn (a, b, c) -> ['a', 'b', 'c'] // type2 ['a', 'b', fn] -> ['a', 'b'] // type3function fn () {} fn. $ inject = ['a', 'c'] ['a,'Kode Sumber:
fungsi 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); } lempar $ injectorminerr ('strictdi', '{0} tidak menggunakan anotasi eksplisit dan tidak dapat dipanggil dalam mode ketat', nama); } // hapus komentar fntext = fn.toString (). Ganti (strip_comments, ''); // Pilih semua parameter fn (a, b, c, d) -> 'a, b, c, d' argdecl = fntext.match (fn_args); // Pisahkan menjadi array foreach (argdecl [1] .split (fn_arg_split), function (arg) {arg.replace (fn_arg, fungsi (semua, underscore, name) {$ inject.push (name);});}); }); } fn. $ inject = $ inject; }} lain if (isArray (fn)) {last = fn.length - 1; assertArgfn (fn [terakhir], 'fn'); $ inject = fn.slice (0, terakhir); } else {assertArgfn (fn, 'fn', true); } return $ inject;}3.3 GetService
// Ketika tidak ada layanan dalam cache, masukkan else, cache [serviceName] = instantiating untuk membuat tanda // karena call factory berikutnya (serviceName), itu sebenarnya adalah panggilan rekursif // function (serviceName) {// var penyedia = providerInjector.get (serviceName + providerSuffix);// returneDorer. servicename);// }// instanceInjector.invoke(provider.$get will get out the parameters that need to be injected and inject // Therefore, after marking it, you can determine whether there is a circular dependency function getService(serviceName) { if (cache.hasOwnProperty(serviceName)) { if (cache[serviceName] === INSTANTIATING) { throw $ injectorminerr ('CDEP', 'Ketergantungan melingkar: {0}', ServiceName + '<-' + Path.join ('<-'))) (err) {if (serviceName] === Instantiating) {Delete Cache [ServiceName];3.4 Invoke
fungsi invoke (fn, self, penduduk setempat, serviceName) {if (typeof penduduk setempat === 'string') {serviceName = penduduk setempat; penduduk setempat = null; } var args = [], // Dapatkan daftar parameter $ inject = annotate (fn, strictdi, serviceName), panjang, i, key; untuk (i = 0, panjang = $ inject.length; i <panjang; i ++) {key = $ inject [i]; if (typeOf key! == 'string') {throw $ injectorminerr ('itkn', 'token injeksi yang salah! Nama layanan yang diharapkan sebagai string, get {0}', key); } // prioritas penduduk setempat args.push (penduduk setempat && locals.hasownproperty (key)? penduduk setempat [key]: getservice (key)); } if (isArray (fn)) {fn = fn [panjang]; } return fn.Apply (self, args);}3.5 Instantiate
fungsi instantiate (type, penduduk setempat, serviceName) {var constructor = function () {}, instance, returnedValue; // Ketika tipe adalah array, dapatkan parameter terakhir seperti: ['$ window', function ($ win) {}] constructor.prototype = (isArray (type)? Type [type.length - 1]: type) .prototype; instance = new constructor (); // Panggilan Invoke untuk mengeksekusi metode tipe returnedValue = Invoke (type, instance, penduduk setempat, nama serviceName); return isObject (returnedValue) || isfunction (returnedValue)? ReturnedValue: instance;}Fungsi instantiate adalah untuk membuat instantiate tipe, dan parameter dapat secara otomatis diteruskan ke konstruktor selama proses instantiasi.