การแนะนำ
การปิดเป็นฟังก์ชั่นที่ได้รับอนุญาตให้เข้าถึงตัวแปรในขอบเขตฟังก์ชันอื่น
การปิดเป็นเรื่องยากที่จะเข้าใจใน JavaScript แอปพลิเคชันขั้นสูงจำนวนมากพึ่งพาการปิดเพื่อนำไปใช้ ก่อนอื่นดูตัวอย่างด้านล่าง:
ฟังก์ชั่นด้านนอก () {var i = 100; ฟังก์ชั่น inner () {console.log (i); -ในรหัสข้างต้นตามขอบเขตของตัวแปรตัวแปรท้องถิ่นทั้งหมดในฟังก์ชั่นด้านนอกจะมองเห็นได้จากฟังก์ชั่นภายใน ตัวแปรท้องถิ่นในฟังก์ชั่นภายในจะมองไม่เห็นนอกฟังก์ชั่นภายในดังนั้นตัวแปรท้องถิ่นในฟังก์ชันภายในไม่สามารถอ่านได้นอกฟังก์ชั่นภายใน
เนื่องจากฟังก์ชั่นภายในสามารถอ่านตัวแปรโลคัลของฟังก์ชั่นด้านนอกตราบใดที่ใช้ภายในเป็นค่าส่งคืนตัวแปรภายในสามารถอ่านได้โดยตรงนอก ouer
ฟังก์ชั่นด้านนอก () {var i = 100; ฟังก์ชั่น inner () {console.log (i); } return inner;} var rs = outer (); rs ();ฟังก์ชั่นนี้มีสองลักษณะ:
หลังจากดำเนินการ var rs = outer () ด้วยวิธีนี้แล้ว RS ที่แท้จริงจะชี้ไปที่ฟังก์ชันภายใน รหัสนี้เป็นการปิดจริง กล่าวคือเมื่อฟังก์ชั่นด้านในภายในฟังก์ชั่นด้านนอกถูกอ้างอิงโดยตัวแปรนอกฟังก์ชั่นด้านนอกจะมีการปิดการปิด
ขอบเขต
กล่าวง่ายๆคือขอบเขตคือช่วงของตัวแปรและฟังก์ชั่นที่เข้าถึงได้นั่นคือขอบเขตควบคุมการมองเห็นและวงจรชีวิตของตัวแปรและฟังก์ชั่น ใน JavaScript ขอบเขตของตัวแปรเป็นระดับโลกและท้องถิ่น
ขอบเขตระดับโลก
var num1 = 1; ฟังก์ชั่น fun1 () {num2 = 2;}วัตถุสามวัตถุข้างต้น NUM1, NUM2 และ FUN1 เป็นขอบเขตทั่วโลกทั้งหมด ควรสังเกตที่นี่ว่าตัวแปรที่กำหนดการมอบหมายโดยตรงในตอนท้ายจะถูกประกาศโดยอัตโนมัติว่ามีขอบเขตทั่วโลก
ขอบเขตท้องถิ่น
function wrap () {var obj = "ฉันถูกห่อด้วยห่อและด้านนอกของ wrap ไม่สามารถเข้าถึงฉันได้โดยตรง"; ฟังก์ชั่น innerfun () {// ภายนอกไม่สามารถเข้าถึงฉันได้}}ห่วงโซ่ขอบเขต
ทุกอย่างใน JavaScript เป็นวัตถุ วัตถุเหล่านี้มีคุณสมบัติ [[ขอบเขต]] ซึ่งมีคอลเลกชันของวัตถุในขอบเขตที่สร้างโดยฟังก์ชั่น คอลเลกชันนี้เรียกว่าห่วงโซ่ขอบเขตของฟังก์ชั่นซึ่งกำหนดว่าข้อมูลใดที่สามารถเข้าถึงได้โดยฟังก์ชั่น
ฟังก์ชั่นเพิ่ม (a, b) {return a+b;}เมื่อมีการสร้างฟังก์ชั่นคุณสมบัติ [[ขอบเขต]] ของมันจะเพิ่มขอบเขตส่วนกลางโดยอัตโนมัติ
var sum = เพิ่ม (3,4);
เมื่อมีการเรียกฟังก์ชันวัตถุภายในที่เรียกว่าบริบทการดำเนินการจะถูกสร้างขึ้น วัตถุนี้ z กำหนดสภาพแวดล้อมเมื่อฟังก์ชั่นถูกดำเนินการ นอกจากนี้ยังมีห่วงโซ่ขอบเขตของตัวเองสำหรับความละเอียดของตัวระบุและห่วงโซ่ขอบเขตของมันจะเริ่มต้นเป็นวัตถุที่มีอยู่ใน [[ขอบเขต]] ของฟังก์ชั่นการทำงานปัจจุบัน
ในระหว่างการดำเนินการฟังก์ชั่นทุกครั้งที่พบตัวแปรกระบวนการแยกวิเคราะห์ตัวระบุจะถูกส่งผ่านเพื่อตัดสินใจว่าจะรับและจัดเก็บข้อมูลที่ไหน กระบวนการนี้เริ่มต้นจากหัวของห่วงโซ่ขอบเขตนั่นคือค้นหาตัวระบุชื่อเดียวกันจากวัตถุที่ใช้งานอยู่ หากพบให้ใช้ตัวแปรที่สอดคล้องกับตัวระบุนี้ หากไม่พบให้ค้นหาต่อไปเพื่อค้นหาวัตถุถัดไปในห่วงโซ่ขอบเขตหากไม่พบวัตถุทั้งหมด (วัตถุสุดท้ายคือวัตถุระดับโลก) ไม่พบตัวระบุจะถูกพิจารณาว่าไม่ได้กำหนด
การปิด
การปิดเป็นเพียงฟังก์ชั่นที่เข้าถึงตัวแปรภายนอก
var quo = function (สถานะ) {return {getStatus: function () {return status; -สถานะจะถูกบันทึกไว้ใน Quo จะส่งคืนวัตถุวิธี getStatus ในวัตถุนี้หมายถึงตัวแปรสถานะนั่นคือฟังก์ชั่น getStatus เข้าถึงสถานะตัวแปรภายนอก
var newValue = quo ('string'); // ส่งคืนวัตถุที่ไม่ระบุชื่ออ้างอิงโดย newValue ด้วย newValue.getStatus (); // เข้าถึงสถานะตัวแปรภายในของ quoหากวิธีการ getStatus ไม่พร้อมใช้งานสถานะจะถูกรีไซเคิลโดยอัตโนมัติหลังจาก quo ('sting') มันเป็นเพราะวัตถุที่ไม่ระบุชื่อที่ส่งคืนถูกอ้างอิงโดยวัตถุระดับโลกและวัตถุที่ไม่ระบุชื่อขึ้นอยู่กับสถานะดังนั้นมันจะป้องกันการปล่อยสถานะ
ตัวอย่าง:
// รูปแบบข้อผิดพลาด var test = function (โหนด) {var i; สำหรับ (i = 0; i <nodes.length; i ++) {nodes [i] .onclick = function (e) {alert (i); -ฟังก์ชั่นที่ไม่ระบุชื่อจะสร้างการปิดและ i มันเข้าถึงได้คือฉันในฟังก์ชั่นการทดสอบภายนอกดังนั้นแต่ละโหนดจริง ๆ แล้วหมายถึง i เดียวกัน
// การปรับปรุงโซลูชันการทดสอบ var = ฟังก์ชั่น (โหนด) {var i; สำหรับ (i = 0; i <nodes.length; i ++) {nodes [i] .onclick = function (i) {return function () {Alert (i); - }(ฉัน); -แต่ละโหนดจะถูกผูกไว้กับเหตุการณ์ เหตุการณ์นี้ได้รับพารามิเตอร์และรันทันทีผ่านใน i เนื่องจากมันถูกส่งผ่านตามมูลค่าแต่ละลูปจะสร้างการสำรองข้อมูลใหม่สำหรับปัจจุบัน i
บทบาทของการปิด
ฟังก์ชั่นด้านนอก () {var i = 100; ฟังก์ชั่น inner () {console.log (i ++); } return inner;} var rs = outer (); rs (); // 100rs (); // 101rs (); // 102ในรหัสข้างต้น RS คือฟังก์ชั่นการปิดภายใน RS วิ่งทั้งหมดสามครั้งครั้งแรกคือ 100 ครั้งที่สองคือ 101 และครั้งที่สามคือ 102 นี่แสดงให้เห็นว่าตัวแปรท้องถิ่น I ในฟังก์ชั่นด้านนอกได้ถูกเก็บไว้ในหน่วยความจำและไม่ได้รับการเคลียร์โดยอัตโนมัติเมื่อมีการเรียก
จุดประสงค์ของการปิดคือหลังจากการดำเนินการด้านนอกเสร็จสิ้นและส่งคืนการปิดทำให้กลไกการรวบรวมขยะของ JavaScript (คอลเลกชันคว้า) ไม่ได้รีไซเคิลหน่วยความจำที่ถูกครอบครองโดยด้านนอกเนื่องจากการดำเนินการของฟังก์ชันด้านในของด้านนอกขึ้นอยู่กับตัวแปรในด้านนอก (คำอธิบายอื่น: ด้านนอกเป็นฟังก์ชั่นหลักของภายในด้านในจะถูกกำหนดให้กับตัวแปรทั่วโลกทำให้ภายในอยู่ในหน่วยความจำตลอดเวลาและการมีอยู่ของภายในขึ้นอยู่กับด้านนอกเพราะบางด้านอยู่ในหน่วยความจำเสมอและจะไม่เก็บขยะและรีไซเคิลหลังจากการโทรเสร็จ)
การปิดมีสิทธิ์ในการเข้าถึงตัวแปรทั้งหมดภายในฟังก์ชั่น
เมื่อฟังก์ชั่นส่งคืนการปิดขอบเขตของฟังก์ชันจะถูกบันทึกไว้ในหน่วยความจำจนกว่าจะไม่มีการปิด
การปิดและตัวแปร
เนื่องจากกลไกห่วงโซ่ขอบเขตการปิดสามารถรับค่าสุดท้ายที่มีตัวแปรใด ๆ ในฟังก์ชัน ดูตัวอย่างต่อไปนี้:
ฟังก์ชั่น f () {var rs = []; สำหรับ (var i = 0; i <10; i ++) {rs [i] = function () {return i; - } return rs;} var fn = f (); สำหรับ (var i = 0; i <fn.length; i ++) {console.log ('function fn [' + i + '] () ค่าส่งคืน:' + fn [i] ());};ฟังก์ชั่นจะส่งคืนอาร์เรย์ บนพื้นผิวดูเหมือนว่าแต่ละฟังก์ชั่นควรส่งคืนค่าดัชนีของตัวเอง ในความเป็นจริงแต่ละฟังก์ชั่นส่งคืน 10 นี่เป็นเพราะห่วงโซ่ขอบเขตของฟังก์ชั่นแรกมีวัตถุที่ใช้งานอยู่ของฟังก์ชั่น F และพวกเขาอ้างถึงตัวแปรเดียวกัน i เมื่อฟังก์ชั่น F ส่งคืนค่าของตัวแปร I คือ 10 ในเวลานี้แต่ละฟังก์ชั่นจะบันทึกวัตถุตัวแปรเดียวกันของตัวแปร i เราสามารถบังคับให้ปิดการทำงานตามที่คาดไว้โดยการสร้างฟังก์ชั่นที่ไม่ระบุชื่ออื่น
ฟังก์ชั่น f () {var rs = []; สำหรับ (var i = 0; i <10; i ++) {rs [i] = function (num) {return function () {return num; - }(ฉัน); } return rs;} var fn = f (); สำหรับ (var i = 0; i <fn.length; i ++) {console.log ('function fn [' + i + '] () ค่าส่งคืน:' + fn [i] ());};ในรุ่นนี้แทนที่จะกำหนดการปิดให้กับอาร์เรย์โดยตรงเรากำหนดฟังก์ชั่นที่ไม่ระบุชื่อและกำหนดผลลัพธ์ของการดำเนินการฟังก์ชันที่ไม่ระบุชื่อทันทีไปยังอาร์เรย์ ที่นี่ฟังก์ชั่นที่ไม่ระบุชื่อมีพารามิเตอร์ NUM เมื่อเรียกแต่ละฟังก์ชั่นเราจะผ่านตัวแปร i เนื่องจากพารามิเตอร์ถูกส่งผ่านตามค่าตัวแปรที่ฉันจะถูกคัดลอกไปยังพารามิเตอร์ NUM ภายในฟังก์ชั่นที่ไม่ระบุชื่อนี้การปิดการเข้าถึง NUM จะถูกสร้างและส่งคืน ด้วยวิธีนี้แต่ละฟังก์ชั่นในอาเรย์ RS มีสำเนาตัวแปร NUM ของตัวเองดังนั้นค่าที่แตกต่างกันสามารถส่งคืนได้
วัตถุนี้ปิด
var name = 'jack'; var o = {ชื่อ: 'bingdian', getName: function () {return function () {return this.name; - }} console.log (o.getName () () ()); // jackvar name = 'jack'; var o = {ชื่อ: 'bingdian', getName: function () {var self = this; return function () {return self.name; - }} console.log (o.getName () () ()); // Bingdianหน่วยความจำรั่วไหล
ฟังก์ชั่นกำหนดค่า () {var el = document.getElementById ('demo'); el.onclick = function () {console.log (el.id); }} actementHandler ();รหัสข้างต้นสร้างการปิดเป็นตัวจัดการเหตุการณ์ EL ELEMENT และการปิดนี้จะสร้างการอ้างอิงแบบวงกลม ตราบใดที่ฟังก์ชั่นที่ไม่ระบุชื่อมีอยู่จำนวนการอ้างอิง EL อย่างน้อย 1 เพราะหน่วยความจำที่อยู่จะไม่ถูกรีไซเคิล
ฟังก์ชั่นกำหนดค่า () {var el = document.getElementById ('demo'); var id = el.id; el.onclick = function () {console.log (id); } el = null;} actionHandler ();การตั้งค่าตัวแปร El Null สามารถใช้งานวัตถุ DOM และตรวจสอบให้แน่ใจว่าใช้หน่วยความจำตามปกติ
เลียนแบบขอบเขตระดับบล็อก
คำสั่งที่ตั้งไว้ในคู่ของการจัดฟันแบบหยิก ({และ}) เป็นของบล็อกและตัวแปรทั้งหมดที่กำหนดไว้ในสิ่งนี้จะมองไม่เห็นนอกบล็อกรหัสซึ่งเราเรียกขอบเขตระดับบล็อก
(ฟังก์ชัน () {// ขอบเขตระดับบล็อก}) ();การประยุกต์ใช้การปิด
ปกป้องความปลอดภัยของตัวแปรในฟังก์ชั่น ดังในตัวอย่างก่อนหน้านี้เฉพาะฟังก์ชั่นภายในเท่านั้นที่สามารถเข้าถึงฉันในฟังก์ชั่นด้านนอก แต่ไม่สามารถเข้าถึงได้ผ่านช่องทางอื่น ๆ ดังนั้นจึงปกป้องความปลอดภัยของ i
รักษาตัวแปรในหน่วยความจำ ดังในตัวอย่างก่อนหน้านี้เนื่องจากการปิดฉันอยู่ในฟังก์ชั่นด้านนอกมักจะมีอยู่ในหน่วยความจำดังนั้นทุกครั้งที่ Rs () จะถูกดำเนินการฉันจะถูกเพิ่ม 1