ไม่ช้าก็เร็วคุณต้องใช้ผลลัพธ์ที่เป็นนามธรรมของนักพัฒนาอื่น ๆ นั่นคือคุณพึ่งพารหัสของคนอื่น ฉันชอบที่จะพึ่งพาโมดูลฟรี (ปราศจากการพึ่งพา) แต่นั่นเป็นเรื่องยากที่จะบรรลุ แม้แต่ส่วนประกอบกล่องสีดำที่สวยงามที่คุณสร้างขึ้นก็ขึ้นอยู่กับบางสิ่งที่มากหรือน้อย นี่คือสิ่งที่ทำให้การฉีดพึ่งพาได้ดีมาก ความสามารถในการจัดการการพึ่งพาอย่างมีประสิทธิภาพเป็นสิ่งจำเป็นอย่างยิ่ง บทความนี้สรุปการสำรวจปัญหาและวิธีแก้ปัญหาบางอย่างของฉัน
1. เป้าหมาย
ลองนึกภาพเรามีสองโมดูล คนแรกมีหน้าที่รับผิดชอบบริการคำขอ 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. รายการที่สมบูรณ์แบบมาบรรลุผลด้านล่าง
3. ต้องการวิธีการ/AMD
คุณอาจเคยได้ยินเกี่ยวกับ RequireJS ซึ่งเป็นทางเลือกที่ดีในการแก้ปัญหาการฉีดพึ่งพา
การคัดลอกรหัสมีดังนี้:
กำหนด (['บริการ', 'เราเตอร์'], ฟังก์ชั่น (บริการ, เราเตอร์) {
-
-
แนวคิดคือการอธิบายการพึ่งพาที่ต้องการก่อนจากนั้นเขียนฟังก์ชั่นของคุณ ลำดับของพารามิเตอร์ที่นี่มีความสำคัญมาก ดังที่ได้กล่าวไว้ข้างต้นเรามาเขียนโมดูลที่เรียกว่าหัวฉีดที่สามารถยอมรับไวยากรณ์เดียวกันได้
การคัดลอกรหัสมีดังนี้:
var dosomething = injector.resolve (['บริการ', 'เราเตอร์'], ฟังก์ชั่น (บริการ, เราเตอร์, อื่น ๆ ) {
คาดหวัง (บริการ (). ชื่อ) .to.be ('บริการ');
คาดหวัง (เราเตอร์ (). ชื่อ) .to.be ('เราเตอร์');
คาดหวัง (อื่น ๆ ) .to.be ('อื่น ๆ ');
-
Dosomething ("อื่น ๆ ");
ก่อนที่จะดำเนินการต่อฉันควรอธิบายเนื้อหาของฟังก์ชั่นฟังก์ชั่น Dosomething อย่างชัดเจน วิธี.
มาเริ่มโมดูลหัวฉีดของเราซึ่งเป็นรูปแบบซิงเกิลที่ยอดเยี่ยมดังนั้นมันจึงทำงานได้ดีในส่วนต่าง ๆ ของโปรแกรมของเรา
การคัดลอกรหัสมีดังนี้:
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) เป็นสิ่งจำเป็นในการแปลงตัวแปรอาร์กิวเมนต์เป็นอาร์เรย์จริง จนถึงตอนนี้มันไม่เลว การทดสอบของเราผ่านไป ปัญหาเกี่ยวกับการใช้งานนี้คือเราต้องเขียนชิ้นส่วนที่ต้องการสองครั้งและเราไม่สามารถสับสนคำสั่งซื้อได้ พารามิเตอร์ที่กำหนดเองเพิ่มเติมมักจะอยู่เบื้องหลังการพึ่งพา
4. วิธีการสะท้อนกลับ
ตามคำจำกัดความของวิกิพีเดียการสะท้อนกลับหมายถึงความสามารถของโปรแกรมในการตรวจสอบและแก้ไขโครงสร้างและพฤติกรรมของวัตถุที่รันไทม์ พูดง่ายๆในบริบทของ JavaScript สิ่งนี้หมายถึงเฉพาะซอร์สโค้ดของวัตถุหรือฟังก์ชั่นที่อ่านและวิเคราะห์ ลองทำฟังก์ชั่น Dosomething ที่กล่าวถึงในตอนต้นของบทความกันเถอะ หากคุณส่งออก dosomething.toString () ในคอนโซล คุณจะได้รับสตริงต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
"ฟังก์ชั่น (บริการเราเตอร์อื่น ๆ ) {
var s = service ();
var r = เราเตอร์ ();
-
สตริงที่ส่งคืนโดยวิธีนี้ทำให้เรามีความสามารถในการสำรวจพารามิเตอร์และที่สำคัญกว่านั้นคือการรับชื่อของพวกเขา นี่เป็นวิธีการของ Angular ในการใช้การฉีดพึ่งพา ฉันขี้เกียจนิดหน่อยและสกัดกั้นนิพจน์ทั่วไปที่ได้รับพารามิเตอร์ในรหัสเชิงมุมโดยตรง
การคัดลอกรหัสมีดังนี้:
/^function/s*[^/(]*/(/s*([^/)]*)/)/m
เราสามารถแก้ไขรหัสแก้ไขได้เช่นนี้:
การคัดลอกรหัสมีดังนี้:
แก้ไข: function () {
var func, deps, ขอบเขต, args = [], self = this;
func = อาร์กิวเมนต์ [0];
deps = func.toString (). การจับคู่ (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1]. reflace (//g, '').แยก(',');
ขอบเขต = อาร์กิวเมนต์ [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 ("อื่น ๆ ");
ไม่จำเป็นต้องเขียนการพึ่งพาใหม่และคำสั่งซื้อของพวกเขาสามารถหยุดชะงักได้ มันยังใช้งานได้และเราประสบความสำเร็จในการคัดลอกเวทมนตร์ของ Angular
อย่างไรก็ตามการฝึกฝนนี้ไม่สมบูรณ์แบบซึ่งเป็นปัญหาใหญ่มากกับการฉีดชนิดสะท้อนกลับ การบีบอัดจะทำลายตรรกะของเราเนื่องจากเปลี่ยนชื่อของพารามิเตอร์และเราจะไม่สามารถรักษาความสัมพันธ์การแมปที่ถูกต้องได้ ตัวอย่างเช่น dosometing () อาจมีลักษณะเช่นนี้หลังจากการบีบอัด:
การคัดลอกรหัสมีดังนี้:
var dosomething = function (e, t, n) {var r = e (); var i = t ()}
โซลูชันที่เสนอโดยทีมเชิงมุมดูเหมือนว่า:
var dosomething = injector.resolve (['บริการ', 'เราเตอร์', ฟังก์ชั่น (บริการ, เราเตอร์) {
-
นี่ดูเหมือนวิธีแก้ปัญหาที่เราเริ่มต้นด้วย ฉันไม่สามารถหาทางออกที่ดีกว่าได้ดังนั้นฉันจึงตัดสินใจรวมทั้งสองอย่าง นี่คือรุ่นสุดท้ายของหัวฉีด
การคัดลอกรหัสมีดังนี้:
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]. reflace (//g, '').แยก(',');
ขอบเขต = อาร์กิวเมนต์ [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 dosomething = injector.resolve ('เราเตอร์, บริการ', ฟังก์ชั่น (a, b, c) {
คาดหวัง (a (). ชื่อ) .to.be ('เราเตอร์');
คาดหวัง (b) .to.be ('อื่น ๆ ');
คาดหวัง (c (). ชื่อ) .to.be ('บริการ');
-
Dosomething ("อื่น ๆ ");
คุณอาจสังเกตเห็นว่ามีเครื่องหมายจุลภาคสองตัวหลังจากพารามิเตอร์แรก - โปรดทราบว่านี่ไม่ใช่การพิมพ์ผิด ค่า NULL แสดงถึงพารามิเตอร์ "อื่น ๆ " (ตัวยึดตำแหน่ง) สิ่งนี้แสดงให้เห็นว่าเราควบคุมลำดับของพารามิเตอร์ได้อย่างไร
5. การฉีดขอบเขตโดยตรง
บางครั้งฉันใช้ตัวแปรการฉีดครั้งที่สามซึ่งเกี่ยวข้องกับขอบเขตของฟังก์ชันการทำงาน (กล่าวอีกนัยหนึ่งคือวัตถุนี้) ดังนั้นตัวแปรนี้ไม่จำเป็นในหลายกรณี
การคัดลอกรหัสมีดังนี้:
var injector = {
การพึ่งพา: {}
ลงทะเบียน: ฟังก์ชั่น (คีย์, ค่า) {
this.dependencies [key] = value;
-
แก้ไข: ฟังก์ชั่น (deps, func, ขอบเขต) {
var args = [];
ขอบเขต = ขอบเขต || {};
สำหรับ (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
ขอบเขต [d] = this.dependencies [d];
} อื่น {
โยนข้อผิดพลาดใหม่ ('ไม่สามารถแก้ไขได้' + d);
-
-
return function () {
func.apply (ขอบเขต || {}, array.prototype.slice.call (อาร์กิวเมนต์, 0));
-
-
-
สิ่งที่เราทำคือเพิ่มการพึ่งพาขอบเขต ข้อดีของสิ่งนี้คือนักพัฒนาไม่จำเป็นต้องเขียนพารามิเตอร์การพึ่งพาอีกต่อไป
การคัดลอกรหัสมีดังนี้:
var dosomething = injector.resolve (['บริการ', 'เราเตอร์'], ฟังก์ชั่น (อื่น ๆ ) {
คาดหวัง (this.service (). ชื่อ) .to.be ('บริการ');
คาดหวัง (this.router (). ชื่อ) .to.be ('เราเตอร์');
คาดหวัง (อื่น ๆ ) .to.be ('อื่น ๆ ');
-
Dosomething ("อื่น ๆ ");
6. บทสรุป
ในความเป็นจริงพวกเราส่วนใหญ่ใช้การฉีดพึ่งพา แต่เราไม่ได้ตระหนักถึงมัน แม้ว่าคุณจะไม่ทราบคำศัพท์คุณอาจใช้มันในรหัสของคุณเป็นล้านครั้ง ฉันหวังว่าบทความนี้จะทำให้ความเข้าใจของคุณลึกซึ้งยิ่งขึ้น