โลกของการเขียนโปรแกรมคอมพิวเตอร์เป็นกระบวนการของการสรุปชิ้นส่วนที่เรียบง่ายอย่างต่อเนื่องและการจัดระเบียบ abstractions เหล่านี้ JavaScript ก็ไม่มีข้อยกเว้น เมื่อเราใช้ JavaScript ในการเขียนแอปพลิเคชันเราใช้รหัสที่เขียนโดยผู้อื่นเช่นห้องสมุดโอเพนซอร์สที่มีชื่อเสียงหรือเฟรมเวิร์ก เมื่อโครงการของเราเติบโตขึ้นโมดูลมากขึ้นเรื่อย ๆ ที่เราต้องพึ่งพา ในเวลานี้วิธีการจัดระเบียบโมดูลเหล่านี้อย่างมีประสิทธิภาพกลายเป็นปัญหาที่สำคัญมาก การฉีดพึ่งพาช่วยแก้ปัญหาของวิธีการจัดระเบียบโมดูลการพึ่งพารหัสได้อย่างมีประสิทธิภาพ คุณอาจเคยได้ยินคำว่า "การฉีดพึ่งพาการพึ่งพา" ในบางกรอบหรือห้องสมุดเช่นกรอบส่วนหน้าชื่อที่มีชื่อเสียง AngularJs การฉีดพึ่งพาเป็นหนึ่งในคุณสมบัติที่สำคัญมาก อย่างไรก็ตามการฉีดพึ่งพาไม่มีอะไรใหม่เลย แต่ก็มีมานานแล้วในภาษาการเขียนโปรแกรมอื่น ๆ เช่น PHP ในเวลาเดียวกันการฉีดขึ้นอยู่กับการพึ่งพาไม่ซับซ้อนเท่าที่ควรจะเป็น ในบทความนี้เราจะได้เรียนรู้แนวคิดของการฉีดพึ่งพาใน JavaScript และอธิบายด้วยวิธีที่เข้าใจง่ายวิธีการเขียนรหัส "รูปแบบการฉีดพึ่งพา"
การตั้งค่าเป้าหมาย
สมมติว่าเรามีสองโมดูลในขณะนี้ ฟังก์ชั่นของโมดูลแรกคือการส่งคำขอ AJAX ในขณะที่ฟังก์ชั่นของโมดูลที่สองคือการใช้เป็นเส้นทาง
การคัดลอกรหัสมีดังนี้:
var service = function () {
return {name: 'service'};
-
var router = function () {
return {ชื่อ: 'เราเตอร์'};
-
ในเวลานี้เราเขียนฟังก์ชั่นที่ต้องใช้สองโมดูลที่กล่าวถึงข้างต้น:
การคัดลอกรหัสมีดังนี้:
var dosomething = function (อื่น ๆ ) {
var s = service ();
var r = เราเตอร์ ();
-
ที่นี่เพื่อให้รหัสของเราน่าสนใจยิ่งขึ้นพารามิเตอร์นี้ต้องได้รับพารามิเตอร์เพิ่มเติมอีกหลายตัว แน่นอนว่าเราสามารถใช้รหัสข้างต้นได้ แต่ไม่ว่าจะอยู่ในแง่ใดก็ตามรหัสข้างต้นดูเหมือนจะยืดหยุ่นน้อยกว่าเล็กน้อย เราควรทำอย่างไรถ้าชื่อโมดูลที่เราต้องใช้จะกลายเป็น Servicexml หรือ ServiceJSON หรือถ้าเราต้องการใช้โมดูลปลอมบางอย่างเพื่อการทดสอบ ในเวลานี้เราไม่สามารถแก้ไขฟังก์ชั่นได้เอง ดังนั้นสิ่งแรกที่เราต้องทำคือผ่านโมดูลที่ขึ้นอยู่กับเป็นพารามิเตอร์ไปยังฟังก์ชันรหัสมีดังนี้:
การคัดลอกรหัสมีดังนี้:
var dosomething = function (บริการ, เราเตอร์, อื่น ๆ ) {
var s = service ();
var r = เราเตอร์ ();
-
ในรหัสข้างต้นเราผ่านโมดูลที่เราต้องการอย่างสมบูรณ์ แต่สิ่งนี้ทำให้เกิดปัญหาใหม่ สมมติว่าเราเรียกวิธีการ Dosomething ในส่วนพี่ชายของรหัส ในเวลานี้เราควรทำอย่างไรถ้าเราต้องการการพึ่งพาครั้งที่สาม ในเวลานี้มันไม่ใช่วิธีที่ฉลาดในการแก้ไขรหัสการเรียกใช้ฟังก์ชันทั้งหมด ดังนั้นเราต้องใช้รหัสชิ้นหนึ่งเพื่อช่วยให้เราทำสิ่งนี้ นี่คือปัญหาที่หัวฉีดพึ่งพากำลังพยายามแก้ไข ตอนนี้เราสามารถตั้งเป้าหมายของเรา:
1. เราควรจะสามารถลงทะเบียนการพึ่งพาได้
2. หัวฉีดการพึ่งพาควรได้รับฟังก์ชั่นจากนั้นส่งคืนฟังก์ชั่นที่สามารถรับทรัพยากรที่ต้องการได้
3. รหัสไม่ควรซับซ้อน แต่ควรง่ายและเป็นมิตร
4. หัวฉีดพึ่งพาควรรักษาขอบเขตฟังก์ชันที่ผ่าน
5. ฟังก์ชั่นที่ส่งผ่านควรจะได้รับพารามิเตอร์ที่กำหนดเองไม่ใช่แค่การพึ่งพาที่อธิบายไว้
ต้องการเมธอด
บางทีคุณอาจเคยได้ยินเกี่ยวกับ RequireJS ที่มีชื่อเสียงซึ่งเป็นห้องสมุดที่สามารถแก้ปัญหาการฉีดพึ่งพาได้ดี:
การคัดลอกรหัสมีดังนี้:
กำหนด (['บริการ', 'เราเตอร์'], ฟังก์ชั่น (บริการ, เราเตอร์) {
-
-
แนวคิดของ RequireJS คือก่อนอื่นเราควรอธิบายโมดูลที่ต้องการจากนั้นเขียนฟังก์ชั่นของคุณเอง ในหมู่พวกเขาลำดับของพารามิเตอร์มีความสำคัญมาก สมมติว่าเราจำเป็นต้องเขียนโมดูลที่เรียกว่าหัวฉีดที่สามารถใช้ไวยากรณ์ที่คล้ายกัน
การคัดลอกรหัสมีดังนี้:
var dosomething = injector.resolve (['บริการ', 'เราเตอร์'], ฟังก์ชั่น (บริการ, เราเตอร์, อื่น ๆ ) {
คาดหวัง (บริการ (). ชื่อ) .to.be ('บริการ');
คาดหวัง (เราเตอร์ (). ชื่อ) .to.be ('เราเตอร์');
คาดหวัง (อื่น ๆ ) .to.be ('อื่น ๆ ');
-
Dosomething ("อื่น ๆ ");
ก่อนที่จะดำเนินการต่อสิ่งหนึ่งที่ควรทราบคือในฟังก์ชั่นของ Dosomething เราใช้ไลบรารีการยืนยันที่คาดหวัง js เพื่อให้แน่ใจว่าถูกต้องของรหัส นี่คือเล็กน้อยคล้ายกับ TDD (การพัฒนาที่ขับเคลื่อนด้วยการทดสอบ)
ตอนนี้เราเริ่มเขียนโมดูลหัวฉีดอย่างเป็นทางการ ก่อนอื่นควรเป็นโมโนเมอร์เพื่อให้สามารถมีฟังก์ชั่นเดียวกันในทุกส่วนของแอปพลิเคชันของเรา
การคัดลอกรหัสมีดังนี้:
var injector = {
การพึ่งพา: {}
ลงทะเบียน: ฟังก์ชั่น (คีย์, ค่า) {
this.dependencies [key] = value;
-
แก้ไข: ฟังก์ชั่น (deps, func, ขอบเขต) {
-
-
วัตถุนี้ง่ายมากมีเพียงสองฟังก์ชั่นและตัวแปรสำหรับวัตถุประสงค์ในการจัดเก็บ สิ่งที่เราต้องทำคือตรวจสอบอาร์เรย์ DEPS แล้วมองหาคำตอบในตัวแปรตาม ส่วนที่เหลือคือการใช้วิธี. Apply เพื่อเรียกตัวแปร func ที่เราผ่าน:
การคัดลอกรหัสมีดังนี้:
แก้ไข: ฟังก์ชั่น (deps, func, ขอบเขต) {
var args = [];
สำหรับ (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
args.push (this.dependencies [d]);
} อื่น {
โยนข้อผิดพลาดใหม่ ('ไม่สามารถแก้ไขได้' + d);
-
-
return function () {
func.apply (ขอบเขต || {}, args.concat (array.prototype.slice.call (อาร์กิวเมนต์, 0)));
-
-
หากคุณต้องการระบุขอบเขตรหัสด้านบนจะทำงานตามปกติ
ในรหัสข้างต้นบทบาทของ array.prototype.slice.call (อาร์กิวเมนต์, 0) คือการแปลงตัวแปรอาร์กิวเมนต์เป็นอาร์เรย์จริง จนถึงตอนนี้รหัสของเราผ่านการทดสอบอย่างสมบูรณ์ แต่ปัญหาที่นี่คือเราต้องเขียนโมดูลที่ต้องการสองครั้งและเราไม่สามารถจัดเรียงพวกเขาได้โดยพลการ พารามิเตอร์พิเศษจะตามมาด้วยการพึ่งพาทั้งหมดเสมอ
วิธีการสะท้อน
จากคำอธิบายในวิกิพีเดียการสะท้อนกลับหมายถึงความจริงที่ว่าวัตถุสามารถปรับเปลี่ยนโครงสร้างและพฤติกรรมของตัวเองในระหว่างการวิ่ง ใน JavaScript เพียงแค่ใส่มันคือความสามารถในการอ่านซอร์สโค้ดของวัตถุและวิเคราะห์ซอร์สโค้ด หรือกลับไปที่วิธีการ Dosomething ของเราหากคุณเรียกวิธี DoSomething.toString () คุณสามารถรับสตริงต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
"ฟังก์ชั่น (บริการเราเตอร์อื่น ๆ ) {
var s = service ();
var r = เราเตอร์ ();
-
ด้วยวิธีนี้ตราบใดที่เราใช้วิธีนี้เราสามารถรับพารามิเตอร์ที่เราต้องการได้อย่างง่ายดายและที่สำคัญกว่านั้นคือชื่อของพวกเขา นี่เป็นวิธีที่ใช้โดย AngularJs เพื่อใช้การฉีดพึ่งพา ในรหัส AngularJS เราสามารถเห็นนิพจน์ปกติต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
/^function/s*[^/(]*/(/s*([^/)]*)/)/m
เราสามารถแก้ไขวิธีการแก้ไขเป็นรหัสต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
แก้ไข: function () {
var func, deps, ขอบเขต, args = [], self = this;
func = อาร์กิวเมนต์ [0];
deps = func.toString (). การจับคู่ (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .replace (//g, '') .split (',');
ขอบเขต = อาร์กิวเมนต์ [1] || -
return function () {
var a = array.prototype.slice.call (อาร์กิวเมนต์, 0);
สำหรับ (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self.dependencies [d] && d! = '' self.dependencies [d]: a.shift ());
-
func.apply (ขอบเขต || {}, args);
-
-
เราใช้นิพจน์ทั่วไปข้างต้นเพื่อให้ตรงกับฟังก์ชั่นที่เรากำหนดไว้และเราจะได้รับผลลัพธ์ต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
["ฟังก์ชั่น (บริการ, เราเตอร์, อื่น ๆ )", "บริการ, เราเตอร์, อื่น ๆ "]
ณ จุดนี้เราต้องการเฉพาะรายการที่สอง แต่เมื่อเราลบช่องว่างพิเศษและหั่นสตริงด้วยเราจะได้อาร์เรย์ DEPS รหัสต่อไปนี้เป็นส่วนที่เราแก้ไข:
การคัดลอกรหัสมีดังนี้:
var a = array.prototype.slice.call (อาร์กิวเมนต์, 0);
-
args.push (self.dependencies [d] && d! = '' self.dependencies [d]: a.shift ());
ในรหัสข้างต้นเราสำรวจโครงการการพึ่งพาหากมีรายการที่ขาดหายไปหากมีชิ้นส่วนที่ขาดหายไปในโครงการการพึ่งพาเราจะได้รับจากวัตถุอาร์กิวเมนต์ หากอาร์เรย์เป็นอาร์เรย์ที่ว่างเปล่าการใช้วิธีการกะจะส่งคืนไม่ได้กำหนดโดยไม่ต้องโยนข้อผิดพลาด จนถึงตอนนี้หัวฉีดรุ่นใหม่มีลักษณะเช่นนี้:
การคัดลอกรหัสมีดังนี้:
var dosomething = injector.resolve (ฟังก์ชั่น (บริการ, อื่น ๆ , เราเตอร์) {
คาดหวัง (บริการ (). ชื่อ) .to.be ('บริการ');
คาดหวัง (เราเตอร์ (). ชื่อ) .to.be ('เราเตอร์');
คาดหวัง (อื่น ๆ ) .to.be ('อื่น ๆ ');
-
Dosomething ("อื่น ๆ ");
ในรหัสข้างต้นเราสามารถสร้างความสับสนให้กับลำดับการพึ่งพาได้ตามต้องการ
แต่ไม่มีอะไรสมบูรณ์แบบ มีปัญหาร้ายแรงมากกับการฉีดขึ้นอยู่กับวิธีการสะท้อนกลับ ข้อผิดพลาดเกิดขึ้นเมื่อรหัสง่ายขึ้น นี่เป็นเพราะในระหว่างการทำให้ง่ายขึ้นของรหัสชื่อของการเปลี่ยนแปลงพารามิเตอร์ซึ่งจะทำให้การพึ่งพาได้รับการแก้ไข ตัวอย่างเช่น:
การคัดลอกรหัสมีดังนี้:
var dosomething = function (e, t, n) {var r = e (); var i = t ()}
ดังนั้นเราจึงต้องการวิธีแก้ปัญหาต่อไปนี้เช่นใน AngularJS:
การคัดลอกรหัสมีดังนี้:
var dosomething = injector.resolve (['บริการ', 'เราเตอร์', ฟังก์ชั่น (บริการ, เราเตอร์) {
-
สิ่งนี้คล้ายกับโซลูชัน AMD ที่ฉันเห็นในตอนแรกดังนั้นเราจึงสามารถรวมสองวิธีข้างต้นและรหัสสุดท้ายมีดังนี้:
การคัดลอกรหัสมีดังนี้:
var injector = {
การพึ่งพา: {}
ลงทะเบียน: ฟังก์ชั่น (คีย์, ค่า) {
this.dependencies [key] = value;
-
แก้ไข: function () {
var func, deps, ขอบเขต, args = [], self = this;
if (typeof arguments [0] === 'string') {
func = อาร์กิวเมนต์ [1];
deps = อาร์กิวเมนต์ [0]. แทนที่ ( / / g, '') .split (',');
ขอบเขต = อาร์กิวเมนต์ [2] || -
} อื่น {
func = อาร์กิวเมนต์ [0];
deps = func.toString (). การจับคู่ (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .replace (//g, '') .split (',');
ขอบเขต = อาร์กิวเมนต์ [1] || -
-
return function () {
var a = array.prototype.slice.call (อาร์กิวเมนต์, 0);
สำหรับ (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self.dependencies [d] && d! = '' self.dependencies [d]: a.shift ());
-
func.apply (ขอบเขต || {}, args);
-
-
-
วิธีการแก้ไขรุ่นนี้สามารถยอมรับพารามิเตอร์สองหรือสามตัว นี่คือรหัสทดสอบ:
การคัดลอกรหัสมีดังนี้:
var dosomething = injector.resolve ('เราเตอร์, บริการ', ฟังก์ชั่น (a, b, c) {
คาดหวัง (a (). ชื่อ) .to.be ('เราเตอร์');
คาดหวัง (b) .to.be ('อื่น ๆ ');
คาดหวัง (c (). ชื่อ) .to.be ('บริการ');
-
Dosomething ("อื่น ๆ ");
คุณอาจสังเกตเห็นว่าไม่มีอะไรระหว่างสองจุลภาคและนั่นไม่ใช่ความผิดพลาด ตำแหน่งว่างนี้เหลือสำหรับพารามิเตอร์อื่น นี่คือวิธีที่เราควบคุมลำดับของพารามิเตอร์
บทสรุป
ในเนื้อหาข้างต้นเราแนะนำวิธีการฉีดพึ่งพาหลายวิธีใน JavaScript ฉันหวังว่าบทความนี้จะช่วยให้คุณเริ่มใช้เทคนิคการฉีดพึ่งพาและเขียนรหัสสไตล์การฉีดพึ่งพา