Dunia pemrograman komputer sebenarnya adalah proses yang terus -menerus mengabstraksi bagian -bagian sederhana dan mengatur abstraksi ini. Javascript tidak terkecuali. Ketika kami menggunakan JavaScript untuk menulis aplikasi, apakah kami menggunakan kode yang ditulis oleh orang lain, seperti beberapa perpustakaan atau kerangka kerja open source yang terkenal. Ketika proyek kami tumbuh, semakin banyak modul yang perlu kami andalkan. Pada saat ini, bagaimana mengatur modul ini secara efektif telah menjadi masalah yang sangat penting. Injeksi ketergantungan memecahkan masalah bagaimana mengatur modul ketergantungan kode secara efektif. Anda mungkin pernah mendengar istilah "injeksi ketergantungan" dalam beberapa kerangka kerja atau perpustakaan, seperti kerangka kerja front-end yang terkenal AngularJS. Injeksi ketergantungan adalah salah satu fitur yang sangat penting. Namun, injeksi ketergantungan bukanlah hal yang baru sama sekali, itu sudah ada sejak lama dalam bahasa pemrograman lainnya seperti PHP. Pada saat yang sama, injeksi ketergantungan tidak rumit seperti yang dibayangkan. Dalam artikel ini, kita akan mempelajari konsep injeksi ketergantungan dalam JavaScript dan menjelaskan dengan cara yang mudah dipahami bagaimana menulis kode "gaya injeksi ketergantungan".
Pengaturan target
Misalkan kita memiliki dua modul sekarang. Fungsi modul pertama adalah mengirim permintaan AJAX, sedangkan fungsi modul kedua adalah digunakan sebagai perutean.
Salinan kode adalah sebagai berikut:
var service = function () {
return {name: 'service'};
}
var router = function () {
return {name: 'router'};
}
Pada saat ini, kami menulis fungsi yang perlu menggunakan dua modul yang disebutkan di atas:
Salinan kode adalah sebagai berikut:
var dosomething = function (lainnya) {
var s = service ();
var r = router ();
};
Di sini, untuk membuat kode kami lebih menarik, parameter ini perlu menerima beberapa parameter lagi. Tentu saja, kita dapat menggunakan kode di atas, tetapi tidak peduli dari aspek apa pun, kode di atas tampaknya sedikit kurang fleksibel. Apa yang harus kita lakukan jika nama modul yang perlu kita gunakan menjadi ServiceXML atau ServiceJson? Atau bagaimana jika kita ingin menggunakan beberapa modul palsu untuk tujuan pengujian? Pada saat ini, kita tidak bisa hanya mengedit fungsi itu sendiri. Oleh karena itu, hal pertama yang perlu kita lakukan adalah meneruskan modul dependen sebagai parameter ke fungsi, kode adalah sebagai berikut:
Salinan kode adalah sebagai berikut:
var dosomething = fungsi (layanan, router, lainnya) {
var s = service ();
var r = router ();
};
Dalam kode di atas, kami melewati modul yang kami butuhkan sepenuhnya. Tapi ini memunculkan masalah baru. Misalkan kita menyebut metode dosomething di bagian saudara dari kode. Saat ini, apa yang harus kita lakukan jika kita membutuhkan ketergantungan ketiga. Pada saat ini, ini bukan cara bijak untuk mengedit semua kode panggilan fungsi. Jadi kami membutuhkan sepotong kode untuk membantu kami melakukan ini. Ini adalah masalah yang coba dipecahkan oleh injektor ketergantungan. Sekarang kita dapat menetapkan tujuan kita:
1. Kita harus dapat mendaftarkan dependensi
2. Injektor ketergantungan harus menerima fungsi dan kemudian mengembalikan fungsi yang dapat memperoleh sumber daya yang diperlukan
3. Kode tidak boleh rumit, tetapi harus sederhana dan ramah
4. Injektor ketergantungan harus mempertahankan ruang lingkup fungsi yang ditularkan
5. Fungsi yang disahkan harus dapat menerima parameter khusus, bukan hanya dependensi yang dijelaskan
Metode Persyaratan/AMD
Mungkin Anda telah mendengar tentang Persyaratan yang terkenal, yang merupakan perpustakaan yang dapat menyelesaikan masalah injeksi ketergantungan dengan baik:
Salinan kode adalah sebagai berikut:
define (['service', 'router'], function (service, router) {
// ...
});
Gagasan persyaratan adalah bahwa pertama -tama kita harus menggambarkan modul yang diperlukan dan kemudian menulis fungsi Anda sendiri. Di antara mereka, urutan parameter sangat penting. Misalkan kita perlu menulis modul yang disebut injektor yang dapat menerapkan sintaks serupa.
Salinan kode adalah sebagai berikut:
var dosomething = injector.resolve (['service', 'router'], function (service, router, lainnya) {
harapkan (service (). name) .to.be ('service');
harapkan (router (). Nama) .to.be ('router');
harapkan (lainnya) .to.be ('lain');
});
dosomething ("lain");
Sebelum melanjutkan, satu hal yang perlu diperhatikan adalah bahwa dalam badan fungsi dosomething, kami menggunakan Perpustakaan Assertion, harapan.js untuk memastikan kebenaran kode. Berikut ini sedikit mirip dengan TDD (pengembangan uji-driven).
Sekarang kami secara resmi mulai menulis modul injektor kami. Pertama, itu harus menjadi monomer sehingga dapat memiliki fungsionalitas yang sama di setiap bagian aplikasi kami.
Salinan kode adalah sebagai berikut:
var injector = {
dependensi: {},
register: function (tombol, value) {
this.dependencies [key] = value;
},
Resolve: function (deps, func, scope) {
}
}
Objek ini sangat sederhana, dengan hanya dua fungsi dan variabel untuk tujuan penyimpanan. Yang perlu kita lakukan adalah memeriksa array DEP dan kemudian mencari jawaban dalam variabel dependensi. Sisanya adalah menggunakan metode .Apply untuk memanggil variabel fungsi yang kami lewati:
Salinan kode adalah sebagai berikut:
Resolve: function (deps, func, scope) {
var args = [];
untuk (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
args.push (this.dependencies [d]);
} kalau tidak {
melempar kesalahan baru ('can/' t resolve ' + d);
}
}
return function () {
func.Apply (scope || {}, args.concat (array.prototype.slice.call (argumen, 0)));
}
}
Jika Anda perlu menentukan ruang lingkup, kode di atas juga akan berjalan secara normal.
Dalam kode di atas, peran array.prototype.slice.call (argumen, 0) adalah untuk mengubah variabel argumen menjadi array nyata. Sejauh ini, kode kami telah lulus tes dengan sempurna. Tetapi masalahnya di sini adalah bahwa kita harus menulis modul yang diperlukan dua kali, dan kita tidak dapat mengaturnya secara sewenang -wenang. Parameter tambahan selalu diikuti oleh semua dependensi.
Metode refleksi
Menurut penjelasan di Wikipedia, refleksi mengacu pada fakta bahwa suatu objek dapat memodifikasi struktur dan perilakunya sendiri selama pelarian. Dalam JavaScript, sederhananya adalah kemampuan untuk membaca kode sumber suatu objek dan menganalisis kode sumber. Atau kembali ke metode dosomething kami, jika Anda memanggil metode dosomething.tostring (), Anda bisa mendapatkan string berikut:
Salinan kode adalah sebagai berikut:
"fungsi (layanan, router, lainnya) {
var s = service ();
var r = router ();
} "
Dengan cara ini, selama kita menggunakan metode ini, kita dapat dengan mudah mendapatkan parameter yang kita inginkan, dan yang lebih penting, nama mereka. Ini juga merupakan metode yang digunakan oleh AngularJS untuk mengimplementasikan injeksi ketergantungan. Dalam kode AngularJS, kita dapat melihat ekspresi reguler berikut:
Salinan kode adalah sebagai berikut:
/^function/s*[^/(]*/(/s*([^/)]*)/)/m
Kami dapat memodifikasi metode Resolve ke kode berikut:
Salinan kode adalah sebagai berikut:
Resolve: function () {
var func, deps, ruang lingkup, args = [], self = this;
func = argumen [0];
DEPS = func.toString (). Match (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .replace (//g, '') .split (',');
scope = argumen [1] || {};
return function () {
var a = array.prototype.slice.call (argumen, 0);
untuk (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self.dependencies [d] && d! = ''? self.dependencies [d]: a.shift ());
}
func.Apply (SCOPE || {}, args);
}
}
Kami menggunakan ekspresi reguler di atas agar sesuai dengan fungsi yang kami tentukan, dan kami bisa mendapatkan hasil berikut:
Salinan kode adalah sebagai berikut:
["Fungsi (Layanan, Router, Lainnya)", "Layanan, Router, Lainnya"]
Pada titik ini, kami hanya membutuhkan item kedua. Tetapi begitu kami menghapus ruang tambahan dan mengiris tali dengan, kami mendapatkan array DEP. Kode berikut adalah bagian yang kami modifikasi:
Salinan kode adalah sebagai berikut:
var a = array.prototype.slice.call (argumen, 0);
...
args.push (self.dependencies [d] && d! = ''? self.dependencies [d]: a.shift ());
Dalam kode di atas, kami melintasi proyek ketergantungan, jika ada item yang hilang di dalamnya, jika ada bagian yang hilang dalam proyek ketergantungan, kami mendapatkannya dari objek argumen. Jika array adalah array kosong, menggunakan metode shift hanya akan mengembalikan tidak terdefinisi tanpa melempar kesalahan. Sejauh ini, versi baru injektor terlihat seperti ini:
Salinan kode adalah sebagai berikut:
var dosomething = injector.resolve (function (layanan, lainnya, router) {
harapkan (service (). name) .to.be ('service');
harapkan (router (). Nama) .to.be ('router');
harapkan (lainnya) .to.be ('lain');
});
dosomething ("lain");
Dalam kode di atas, kita dapat membingungkan urutan dependensi sesuka hati.
Tapi tidak ada yang sempurna. Ada masalah yang sangat serius dengan injeksi ketergantungan metode refleksi. Kesalahan terjadi ketika kode disederhanakan. Ini karena selama penyederhanaan kode, nama parameter berubah, yang akan menyebabkan dependensi diselesaikan. Misalnya:
Salinan kode adalah sebagai berikut:
var dosomething = fungsi (e, t, n) {var r = e (); var i = t ()}
Jadi kita membutuhkan solusi berikut, seperti di AngularJS:
Salinan kode adalah sebagai berikut:
var dosomething = injector.resolve (['service', 'router', function (service, router) {
}]);
Ini sangat mirip dengan solusi AMD yang saya lihat di awal, sehingga kami dapat mengintegrasikan dua metode di atas, dan kode akhir adalah sebagai berikut:
Salinan kode adalah sebagai berikut:
var injector = {
dependensi: {},
register: function (tombol, value) {
this.dependencies [key] = value;
},
Resolve: function () {
var func, deps, ruang lingkup, args = [], self = this;
if (typeof argumen [0] === 'string') {
func = argumen [1];
deps = argumen [0] .replace ( / / g, '') .split (',');
scope = argumen [2] || {};
} kalau tidak {
func = argumen [0];
DEPS = func.toString (). Match (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .replace (//g, '') .split (',');
scope = argumen [1] || {};
}
return function () {
var a = array.prototype.slice.call (argumen, 0);
untuk (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self.dependencies [d] && d! = ''? self.dependencies [d]: a.shift ());
}
func.Apply (SCOPE || {}, args);
}
}
}
Versi metode Resolve ini dapat menerima dua atau tiga parameter. Ini kode uji:
Salinan kode adalah sebagai berikut:
var dosomething = injector.resolve ('router ,, service', function (a, b, c) {
harapkan (a (). Nama) .to.be ('router');
harapkan (b) .to.be ('lain');
harapkan (c (). Nama) .to.be ('layanan');
});
dosomething ("lain");
Anda mungkin telah memperhatikan bahwa tidak ada apa -apa antara dua koma, dan itu bukan kesalahan. Lowongan ini dibiarkan untuk parameter lainnya. Beginilah cara kami mengontrol urutan parameter.
Kesimpulan
Dalam konten di atas, kami memperkenalkan beberapa metode injeksi ketergantungan di JavaScript. Saya harap artikel ini dapat membantu Anda mulai menggunakan teknik injeksi ketergantungan dan menulis kode gaya injeksi ketergantungan.