การเขียนโปรแกรมแบบแยกส่วนเป็นรูปแบบการเขียนโปรแกรม JavaScript ที่พบบ่อยมาก โดยทั่วไปสามารถทำให้รหัสเข้าใจง่ายขึ้น แต่มีแนวทางปฏิบัติที่ยอดเยี่ยมมากมายที่ไม่เป็นที่รู้จัก
ฐาน
มาสั้น ๆ เป็นครั้งแรกในสั้น ๆ บางรูปแบบแบบโมดูลาร์ตั้งแต่ Eric Miraglia (ผู้พัฒนาของ Yui) เผยแพร่บล็อกครั้งแรกเมื่อสามปีก่อนเพื่ออธิบายรูปแบบแบบแยกส่วน หากคุณคุ้นเคยกับโหมดโมดูลาร์เหล่านี้อยู่แล้วคุณสามารถข้ามส่วนนี้ได้โดยตรงและเริ่มอ่านจาก "โหมดขั้นสูง"
การปิดโดยไม่ระบุชื่อ
นี่คือโครงสร้างพื้นฐานที่ทำให้ทุกอย่างเป็นไปได้และยังเป็นคุณสมบัติที่ดีที่สุดของ JavaScript เราจะสร้างฟังก์ชั่นที่ไม่ระบุชื่อและดำเนินการทันที รหัสทั้งหมดจะทำงานภายในฟังก์ชั่นนี้และอยู่ในการปิดที่ให้การแปรรูปซึ่งเพียงพอที่จะเปิดใช้งานตัวแปรในการปิดเหล่านี้เพื่อทำงานตลอดอายุการใช้งานของแอปพลิเคชันของเรา
การคัดลอกรหัสมีดังนี้:
(การทำงาน () {
// ... vars และฟังก์ชั่นทั้งหมดอยู่ในขอบเขตนี้เท่านั้น
// ยังคงสามารถเข้าถึง globals ทั้งหมดได้
-
สังเกตวงเล็บด้านนอกสุดของคู่นี้ห่อฟังก์ชั่นที่ไม่ระบุชื่อ เนื่องจากลักษณะทางภาษาของ JavaScript สิ่งนี้จำเป็นสำหรับวงเล็บ คำสั่งที่เริ่มต้นด้วยฟังก์ชั่นคำหลักใน JS ถือเป็นฟังก์ชันที่เปิดเผยเสมอ ห่อรหัสนี้ในวงเล็บเพื่อให้ล่ามทราบว่านี่เป็นนิพจน์ฟังก์ชั่น
การนำเข้าตัวแปรทั่วโลก
JavaScript มีคุณสมบัติที่เรียกว่าตัวแปรทั่วโลกโดยนัย ไม่ว่าจะใช้ชื่อตัวแปรที่ใดล่ามจะพบคำสั่งประกาศ VAR ของตัวแปรในการย้อนกลับตามห่วงโซ่ขอบเขต หากไม่พบคำสั่งประกาศ VAR ตัวแปรนี้จะถูกพิจารณาว่าเป็นตัวแปรส่วนกลาง หากตัวแปรนี้ใช้ในคำสั่งการกำหนดและไม่มีตัวแปรอยู่ตัวแปรส่วนกลางจะถูกสร้างขึ้น ซึ่งหมายความว่าใช้งานง่ายหรือสร้างตัวแปรทั่วโลกในการปิดแบบไม่ระบุชื่อ น่าเสียดายที่สิ่งนี้ทำให้รหัสเขียนยากมากที่จะรักษาเพราะสำหรับความรู้สึกที่เข้าใจง่ายของผู้คนมันเป็นไปไม่ได้ที่จะบอกได้อย่างรวดเร็วว่าตัวแปรเหล่านั้นเป็นระดับโลก
โชคดีที่ฟังก์ชั่นที่ไม่ระบุชื่อของเราให้วิธีแก้ปัญหาง่ายๆ เพียงผ่านตัวแปรส่วนกลางเป็นพารามิเตอร์ในฟังก์ชั่นที่ไม่ระบุชื่อของเราคุณจะได้รับรหัสที่ชัดเจนและเร็วกว่าตัวแปรทั่วโลกโดยนัย นี่คือตัวอย่าง:
การคัดลอกรหัสมีดังนี้:
(ฟังก์ชั่น ($, yahoo) {
// ตอนนี้สามารถเข้าถึง globals jQuery (เป็น $) และ yahoo ในรหัสนี้
} (jQuery, yahoo));
การส่งออกโมดูล
บางครั้งคุณไม่เพียงต้องการใช้ตัวแปรทั่วโลกเท่านั้นคุณยังต้องการประกาศให้ใช้ซ้ำ เราสามารถทำได้อย่างง่ายดายโดยการรับพวกเขา - ผ่านค่าส่งคืนของฟังก์ชั่นที่ไม่ระบุชื่อ การทำเช่นนี้จะทำให้โมเดลโมดูลพื้นฐานเสร็จสมบูรณ์และต่อไปนี้จะเป็นตัวอย่างที่สมบูรณ์:
การคัดลอกรหัสมีดังนี้:
var module = (function () {
var my = {}
PrivateVariable = 1;
ฟังก์ชั่น PrivateMethod () {
-
-
my.moduleProperty = 1;
my.moduleMethod = function () {
-
-
คืนของฉัน;
-
โปรดทราบว่าเราได้ประกาศโมดูลทั่วโลกที่เรียกว่าโมดูลซึ่งมีคุณสมบัติสาธารณะ 2 ประการ: วิธีที่เรียกว่าโมดูลโมดูลเมโมดและตัวแปรที่เรียกว่า module.moduleproperty นอกจากนี้ยังคงรักษาสถานะส่วนตัวในตัวที่ใช้การปิดฟังก์ชั่นที่ไม่ระบุชื่อ ในเวลาเดียวกันเราสามารถนำเข้าตัวแปรทั่วโลกที่ต้องการได้อย่างง่ายดายและใช้รูปแบบโมดูลนี้ตามที่เราได้เรียนรู้มาก่อน
โหมดขั้นสูง
พื้นฐานที่อธิบายไว้ในส่วนข้างต้นนั้นเพียงพอที่จะจัดการกับสถานการณ์ต่าง ๆ และตอนนี้เราสามารถพัฒนาโมเดลโมดูลาร์นี้เพื่อสร้างโครงสร้างที่ทรงพลังและปรับขนาดได้มากขึ้น เริ่มต้นด้วยโมดูลโมดูลและแนะนำโหมดขั้นสูงเหล่านี้ทีละหนึ่ง
โหมดซูม
โมดูลทั้งหมดจะต้องมีข้อ จำกัด ของโหมดโมดูลาร์ในไฟล์เดียว ทุกคนที่มีส่วนร่วมในโครงการขนาดใหญ่จะเข้าใจถึงคุณค่าของการแยกไฟล์หลายไฟล์ด้วย JS โชคดีที่เรามีการใช้งานที่ยอดเยี่ยมในการขยายโมดูล ก่อนอื่นเรานำเข้าโมดูลเพิ่มคุณสมบัติลงไปและในที่สุดก็ส่งออก นี่คือตัวอย่าง - ซูมเข้าจากโมดูลดั้งเดิม:
การคัดลอกรหัสมีดังนี้:
var module = (ฟังก์ชั่น (my) {
my.anothermethod = function () {
// วิธีเพิ่ม ...
-
คืนของฉัน;
} (โมดูล));
เราใช้คำหลัก VAR เพื่อให้แน่ใจว่ามีความสอดคล้องแม้ว่าจะไม่จำเป็นที่นี่ หลังจากดำเนินการรหัสนี้โมดูลของเรามีวิธีการสาธารณะใหม่ที่เรียกว่า module.anothermethod ไฟล์ที่ขยายนี้จะรักษาสถานะส่วนตัวในตัวและวัตถุที่นำเข้าของตัวเอง
โหมดซูมกว้าง
ตัวอย่างของเราข้างต้นต้องใช้โมดูลการเริ่มต้นของเราที่จะดำเนินการก่อนแล้วจึงขยายโมดูลที่จะดำเนินการและแน่นอนบางครั้งอาจไม่จำเป็นต้องมีความจำเป็น หนึ่งในสิ่งที่ดีที่สุดแอปพลิเคชัน JavaScript สามารถทำได้เพื่อปรับปรุงประสิทธิภาพคือการเรียกใช้สคริปต์แบบอะซิงโครนัส เราสามารถสร้างโมดูล Multipart ที่ยืดหยุ่นและเปิดใช้งานให้โหลดตามลำดับใด ๆ ผ่านโหมดการซูมกว้าง แต่ละไฟล์จะต้องจัดระเบียบในโครงสร้างต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
var module = (ฟังก์ชั่น (my) {
// เพิ่มความสามารถ ...
คืนของฉัน;
} (โมดูล || {}));
ในรูปแบบนี้การแสดงออกของ VAR ทำให้จำเป็น โปรดทราบว่าหากโมดูลไม่ได้เริ่มต้นคำสั่งนำเข้านี้จะสร้างโมดูล ซึ่งหมายความว่าคุณสามารถใช้เครื่องมือเช่น LabJS เพื่อโหลดไฟล์โมดูลทั้งหมดของคุณในแบบคู่ขนานโดยไม่ถูกบล็อก
โหมดการขยายตัวแน่น
โหมดตัวเพิ่มขนาดกว้างนั้นดีมาก แต่ก็จะมีข้อ จำกัด บางประการในโมดูลของคุณ สิ่งสำคัญที่สุดคือคุณไม่สามารถเขียนทับคุณสมบัติของโมดูลได้อย่างปลอดภัย คุณไม่สามารถใช้คุณสมบัติจากไฟล์อื่น ๆ เมื่อเริ่มต้น (แต่คุณสามารถใช้งานได้เมื่อทำงาน) โหมดการขยายตัวแน่นมีลำดับที่โหลดและอนุญาตให้มีคุณสมบัติเหนือกว่า นี่คือตัวอย่างง่ายๆ (ซูมในโมดูลดั้งเดิมของเรา):
การคัดลอกรหัสมีดังนี้:
var module = (ฟังก์ชั่น (my) {
var old_modulemethod = my.modulemethod;
my.moduleMethod = function () {
// วิธีการแทนที่มีการเข้าถึงเก่าผ่าน old_modulemethod ...
-
คืนของฉัน;
} (โมดูล));
เราแทนที่การใช้โมดูลโมดูลโมดูลเมนในตัวอย่างข้างต้น แต่เมื่อจำเป็นเราสามารถรักษาข้อมูลอ้างอิงไปยังวิธีดั้งเดิมได้
โคลนและมรดก
การคัดลอกรหัสมีดังนี้:
var module_two = (ฟังก์ชั่น (เก่า) {
var my = {}
สำคัญ;
สำหรับ (คีย์ในเก่า) {
if (old.hasownproperty (key)) {
[คีย์] ของฉัน = เก่า [คีย์];
-
-
var super_modulemethod = old.modulemethod;
my.moduleMethod = function () {
// วิธีการแทนที่บนโคลนเข้าถึง super ผ่าน super_modulemethod
-
คืนของฉัน;
} (โมดูล));
รุ่นนี้น่าจะเป็นตัวเลือกที่ยืดหยุ่นที่สุด มันทำให้รหัสดูเรียบร้อย แต่นั่นมาจากค่าใช้จ่ายของความยืดหยุ่น ดังที่ฉันเขียนไว้ข้างต้นหากคุณสมบัติเป็นวัตถุหรือฟังก์ชั่นมันจะไม่ถูกคัดลอก แต่จะกลายเป็นข้อมูลอ้างอิงที่สองกับวัตถุหรือฟังก์ชั่น หากคุณแก้ไขหนึ่งในนั้นคุณจะแก้ไขอีกอย่างในเวลาเดียวกัน (หมายเหตุของนักแปล: เพราะพวกเขาเป็นเพียงหนึ่งเดียว!) สิ่งนี้สามารถแก้ไขได้โดยกระบวนการโคลนนิ่งแบบเรียกซ้ำ แต่การโคลนฟังก์ชั่นอาจไม่สามารถแก้ไขได้บางทีมันอาจจะสามารถแก้ไขได้ด้วยการประเมิน ดังนั้นฉันกำลังบอกวิธีนี้ในบทความนี้คำนึงถึงความสมบูรณ์ของบทความเท่านั้น
ตัวแปรส่วนตัวข้ามไฟล์
มีข้อ จำกัด ที่สำคัญในการแยกโมดูลออกเป็นหลายไฟล์: แต่ละไฟล์จะรักษาตัวแปรส่วนตัวของตัวเองและไม่สามารถเข้าถึงตัวแปรส่วนตัวสำหรับไฟล์อื่น ๆ แต่ปัญหานี้สามารถแก้ไขได้ นี่คือตัวอย่างของโมดูลรุ่นกว้างที่เก็บรักษาตัวแปรส่วนตัวข้ามไฟล์:
การคัดลอกรหัสมีดังนี้:
var module = (ฟังก์ชั่น (my) {
var _private = my._private = my._private || -
_seal = my._seal = my._seal || การทำงาน () {
ลบ my._private;
ลบ my._seal;
ลบ my._unseal;
-
_unseal = my._unseal = my._unseal || การทำงาน () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
-
// การเข้าถึง _private, _seal และ _unseal อย่างถาวร
คืนของฉัน;
} (โมดูล || {}));
ไฟล์ทั้งหมดสามารถตั้งค่าคุณสมบัติในตัวแปร _Private ที่เกี่ยวข้องและเข้าใจว่าไฟล์อื่น ๆ สามารถเข้าถึงได้ เมื่อโหลดโมดูลนี้แอปพลิเคชันสามารถเรียกโมดูลได้ _seal () เพื่อป้องกันการโทรภายนอกไปยัง _private ภายใน หากโมดูลนี้จำเป็นต้องได้รับการเลื่อนใหม่วิธีการภายในในไฟล์ใด ๆ สามารถเรียก _unseal () ก่อนโหลดไฟล์ใหม่และโทร _seal () อีกครั้งหลังจากดำเนินการไฟล์ใหม่ ฉันใช้รูปแบบนี้ในที่ทำงานตอนนี้และฉันไม่ได้เห็นที่อื่น ฉันคิดว่านี่เป็นรูปแบบที่มีประโยชน์มากและคุ้มค่าที่จะเขียนบทความเกี่ยวกับโมเดลนี้เอง
กึ่ง
โหมดขั้นสูงสุดท้ายของเรานั้นง่ายที่สุด มีตัวอย่างที่ยอดเยี่ยมมากมายในการสร้าง submodules มันเหมือนกับการสร้างโมดูลปกติ:
การคัดลอกรหัสมีดังนี้:
module.sub = (function () {
var my = {};
-
คืนของฉัน;
-
แม้ว่ามันจะดูง่าย แต่ฉันคิดว่ามันคุ้มค่าที่จะกล่าวถึงที่นี่ Submodules มีข้อได้เปรียบขั้นสูงทั้งหมดของโมดูลทั่วไปรวมถึงโหมดการขยายและสถานะการแปรรูป
สรุปแล้ว
โหมดขั้นสูงส่วนใหญ่สามารถรวมกันเพื่อสร้างโหมดที่มีประโยชน์มากขึ้น หากฉันต้องการแนะนำรูปแบบแบบแยกส่วนสำหรับการออกแบบแอพพลิเคชั่นที่ซับซ้อนฉันจะเลือกที่จะรวมโหมดการขยายกว้างตัวแปรส่วนตัวและ submodules
ฉันไม่ได้พิจารณาประสิทธิภาพของโหมดเหล่านี้ แต่ฉันอยากจะเปลี่ยนเป็นวิธีคิดที่ง่ายกว่า: หากโหมดโมดูลาร์มีประสิทธิภาพที่ดีมันสามารถทำงานได้อย่างยอดเยี่ยมในการลดการดาวน์โหลดไฟล์สคริปต์นี้เร็วขึ้น การใช้โหมดการขยายแบบกว้างช่วยให้ดาวน์โหลดแบบขนานที่ไม่ปิดกั้นได้อย่างง่ายซึ่งเพิ่มความเร็วในการดาวน์โหลด เวลาเริ่มต้นอาจช้ากว่าวิธีอื่นเล็กน้อย แต่ก็คุ้มค่าหลังจากชั่งน้ำหนักข้อดีและข้อเสีย ตราบใดที่การนำเข้าตัวแปรทั่วโลกมีความแม่นยำประสิทธิภาพการทำงานของรันไทม์ควรได้รับผลกระทบและยังเป็นไปได้ที่จะบรรลุความเร็วในการทำงานที่เร็วขึ้นใน submodules โดยการทำให้ห่วงโซ่อ้างอิงสั้นลงด้วยตัวแปรส่วนตัว
โดยสรุปนี่คือตัวอย่างของโมดูลเด็กที่โหลดตัวเองแบบไดนามิกลงในโมดูลพาเรนต์ (การสร้างหากไม่มีโมดูลพาเรนต์) เพื่อความเรียบง่ายฉันลบตัวแปรส่วนตัวและแน่นอนว่ามันง่ายมากที่จะเพิ่มตัวแปรส่วนตัว รูปแบบการเขียนโปรแกรมนี้ช่วยให้ฐานรหัสโครงสร้างลำดับชั้นที่ซับซ้อนทั้งหมดสามารถโหลดได้แบบขนานผ่าน submodules
การคัดลอกรหัสมีดังนี้:
var util = (ฟังก์ชั่น (parent, $) {
var my = parent.ajax = parent.ajax || -
my.get = function (url, params, callback) {
// ตกลงดังนั้นฉันกำลังโกงนิดหน่อย :)
ส่งคืน $ .getJson (URL, params, callback);
-
// ฯลฯ ...
ผู้ปกครองกลับ;
} (util || {}, jQuery));
บทความนี้สรุปแนวทางปฏิบัติที่ดีที่สุดในปัจจุบันของ "การเขียนโปรแกรมแบบแยกส่วน JavaScript" และอธิบายวิธีการปฏิบัติ แม้ว่านี่จะไม่ใช่การสอนหลัก แต่คุณสามารถเข้าใจได้ด้วยความเข้าใจเพียงเล็กน้อยเกี่ยวกับไวยากรณ์พื้นฐานของ JavaScript