การปิดเป็นเรื่องยากในภาษาจาวาสคริปต์และคุณสมบัติของมัน แอปพลิเคชันขั้นสูงจำนวนมากพึ่งพาการปิดเพื่อนำไปใช้ ฉันได้สัมผัสกับแนวคิดของการปิดเป็นเวลานาน แต่ฉันสับสนและไม่สามารถเข้าใจได้ว่าการปิด JavaScript คืออะไรและสิ่งที่พวกเขามีประโยชน์ วันนี้ฉันเห็นบทความเกี่ยวกับการปิด JavaScript (ลิงก์ต้นฉบับ) บนอินเทอร์เน็ต มันอธิบายได้ดีมาก ตอนนี้ฉันเข้าใจอย่างสมบูรณ์ว่าการปิด JavaScript เป็นสิ่งมหัศจรรย์และจุดประสงค์ของการปิด ฉันจะเขียนพวกเขาที่นี่เพื่อแบ่งปันกับคุณ ฉันหวังว่าเพื่อนที่ไม่เข้าใจการปิด JavaScript จะเข้าใจการปิดหลังจากอ่านพวกเขา! เนื้อหาต่อไปนี้ส่วนใหญ่มาจากข้อความต้นฉบับ ฉันเพิ่มความคิดเห็นของรหัสการเรนเดอร์การทำงานและการปรับเปลี่ยนเล็กน้อยในข้อความต้นฉบับเพื่อความเข้าใจที่ง่าย!
1. ขอบเขตของตัวแปร
เพื่อให้เข้าใจการปิดคุณต้องเข้าใจขอบเขตตัวแปรพิเศษของ JavaScript ก่อน
ใน JavaScript ขอบเขตของตัวแปรแบ่งออกเป็นสองประเภท: ตัวแปรทั่วโลกและตัวแปรท้องถิ่น
ใน JavaScript ตัวแปรส่วนกลางสามารถอ่านได้โดยตรงภายในฟังก์ชั่น
var n =; // กำหนดตัวแปร global nfunction f () {การแจ้งเตือน ("เข้าถึงตัวแปรส่วนกลาง n, n ="+n); // เข้าถึงตัวแปรส่วนกลาง n} f (); -ผลการทำงาน:
แต่วิธีอื่น ๆ เป็นไปไม่ได้ตัวแปรท้องถิ่นภายในฟังก์ชั่นไม่สามารถอ่านได้นอกฟังก์ชั่น
ฟังก์ชั่น f () {var n =; // กำหนดตัวแปรโลคัล n} การแจ้งเตือน ("เข้าถึงตัวแปรโลคัล n นอกฟังก์ชั่น, n ="+n); // เข้าถึงตัวแปรโลคัล n นอกฟังก์ชั่นข้อผิดพลาด: n ไม่ได้กำหนดไว้ผลการทำงาน:
มีสถานที่ที่ควรทราบที่นี่ เมื่อประกาศตัวแปรภายในคุณต้องใช้คำสั่ง VAR ถ้าไม่เป็นจริงมันเป็นตัวแปรระดับโลกที่ประกาศ!
ฟังก์ชั่น f () {n =;} f (); การแจ้งเตือน ("n ไม่ได้ประกาศด้วย var ภายในฟังก์ชัน f1 ในเวลานี้ n เป็นตัวแปรส่วนกลาง, /r /n หลักฐาน: n ="+n+", ผลลัพธ์ของ window.n == n คือ:"+(window.n == n);ผลการทำงาน:
2. จะอ่านตัวแปรท้องถิ่นจากภายนอกได้อย่างไร?
ด้วยเหตุผลต่าง ๆ บางครั้งเราจำเป็นต้องได้รับตัวแปรท้องถิ่นภายในฟังก์ชั่น อย่างไรก็ตามดังที่ได้กล่าวไว้ก่อนหน้านี้ภายใต้สถานการณ์ปกติสิ่งนี้ไม่สามารถทำได้และสามารถทำได้ผ่านการแก้ปัญหาเท่านั้น
นั่นคือการกำหนดฟังก์ชั่นอื่นภายในฟังก์ชั่น
ฟังก์ชั่น f () {var n =; // ตัวแปรท้องถิ่น n ภายใน f ฟังก์ชัน f // กำหนดฟังก์ชัน f ฟังก์ชั่น f () {// ภายในฟังก์ชัน f, การแจ้งเตือน (n); -ในรหัสด้านบนฟังก์ชั่น F2 รวมอยู่ในฟังก์ชั่น F1 และตัวแปรท้องถิ่นทั้งหมดภายใน F1 สามารถมองเห็นได้ที่ F2 แต่วิธีอื่น ๆ ก็เป็นไปไม่ได้ ตัวแปรท้องถิ่นภายใน F2 นั้นมองไม่เห็นที่ F1 นี่คือโครงสร้าง "ขอบเขตโซ่" ที่ไม่ซ้ำกับภาษาจาวาสคริปต์ วัตถุลูกจะมองขึ้นไปตามระดับตามระดับสำหรับตัวแปรวัตถุแม่ทั้งหมด ดังนั้นตัวแปรทั้งหมดของวัตถุหลักจะมองเห็นได้จากวัตถุลูกไม่เช่นนั้นจะไม่เป็นความจริง เนื่องจาก F2 สามารถอ่านตัวแปรท้องถิ่นใน F1 ได้ตราบใดที่ F2 ใช้เป็นค่าส่งคืนเราไม่สามารถอ่านตัวแปรภายในนอก F1 ได้หรือไม่? บางคนอาจมีคำถาม F2 เป็นฟังก์ชั่นมันจะถูกส่งคืนเป็นค่าส่งคืนของฟังก์ชัน F1 ได้อย่างไร ในความเป็นจริงมันก็โอเค ชื่อฟังก์ชั่นใน JavaScript นั้นเป็นตัวแปรดังนั้นฟังก์ชั่นสามารถใช้เป็นตัวแปรปกติได้ นั่นคือไม่เพียง แต่ฟังก์ชันหนึ่งจะถูกส่งผ่านไปยังฟังก์ชั่นอื่นเช่นพารามิเตอร์ผ่าน แต่ยังสามารถส่งคืนฟังก์ชันหนึ่งเป็นค่าส่งคืนของฟังก์ชันอื่น
ฟังก์ชั่น f () {var n =; // ตัวแปรท้องถิ่น n // f ฟังก์ชั่นประกาศภายใน f ฟังก์ชัน f f () {การแจ้งเตือน (n); } return f; // ใช้ฟังก์ชัน f เป็นค่าส่งคืนของฟังก์ชัน f} var result = f (); // ค่าส่งคืนหลังจาก F เรียกว่าเป็นฟังก์ชัน F และผลลัพธ์คือ function f function (); // 999, โทร F2 ฟังก์ชั่นผลการทำงาน:
3. แนวคิดของการปิด
ฟังก์ชั่น F2 ในส่วนก่อนหน้าของรหัสคือการปิด คำจำกัดความของ "การปิด" ในเอกสารมืออาชีพต่าง ๆ เป็นนามธรรมมาก ตัวอย่างเช่นมีคำจำกัดความการปิด: "การปิดจาวาสคริปต์เป็นตัวแปรที่ได้รับจากฟังก์ชั่นหรือขอบเขตระดับก่อนหน้าในขอบเขตอื่นและตัวแปรเหล่านี้จะไม่ถูกทำลายเนื่องจากการดำเนินการของฟังก์ชั่นระดับก่อนหน้านี้เสร็จสมบูรณ์" เป็นเรื่องยากสำหรับฉันที่จะเข้าใจคำจำกัดความปิดเช่นนี้ ความเข้าใจของฉันคือการปิดเป็นฟังก์ชั่นที่สามารถอ่านตัวแปรภายในฟังก์ชั่นอื่น ๆ เนื่องจากในภาษา JavaScript เฉพาะฟังก์ชั่นย่อยภายในฟังก์ชั่นเท่านั้นที่สามารถอ่านตัวแปรท้องถิ่นการปิดสามารถเข้าใจได้ง่ายว่าเป็น "ฟังก์ชั่นที่กำหนดไว้ในฟังก์ชั่น" ดังนั้นในสาระสำคัญการปิดคือสะพานเชื่อมต่อด้านในและด้านนอกของฟังก์ชั่น
4. จุดประสงค์ในการปิด
การปิดสามารถใช้ในหลายสถานที่ มันมีการใช้งานที่ใหญ่ที่สุดสองครั้งหนึ่งคือตัวแปรภายในฟังก์ชั่นสามารถอ่านได้ตามที่กล่าวไว้ข้างต้นและอื่น ๆ คือค่าของตัวแปรเหล่านี้จะถูกเก็บไว้ในหน่วยความจำเสมอ
จะเข้าใจประโยคนี้ได้อย่างไร? โปรดดูรหัสด้านล่าง
ฟังก์ชั่น f () {var n =; // nadd เป็นตัวแปรส่วนกลางที่ไม่ได้ประกาศโดยใช้ var ตอนนี้ตัวแปรนี้ชี้ไปที่ฟังก์ชั่นที่ไม่ระบุชื่อที่ประกาศภายในฟังก์ชัน f nadd = function () {n+=} ฟังก์ชัน f () {Alert (n); } return f; } var result = f (); // ผลลัพธ์คือฟังก์ชัน f ผลลัพธ์ (); // การโทรครั้งแรกไปยังฟังก์ชันผลลัพธ์ nadd (); // nadd หมายถึงฟังก์ชั่นที่ไม่ระบุชื่อที่ประกาศภายในฟังก์ชัน F, nadd () เป็นฟังก์ชันที่ไม่ระบุชื่อ (); // การโทรครั้งที่สองไปยังฟังก์ชันผลลัพธ์ 1000ผลการทำงาน:
ในรหัสนี้ผลลัพธ์คือฟังก์ชั่นปิด F2 มันทำงานทั้งหมดสองครั้งค่าแรกคือ 999 และค่าที่สองคือ 1,000 นี่พิสูจน์ได้ว่าตัวแปรท้องถิ่นในฟังก์ชั่น F1 ถูกเก็บไว้ในหน่วยความจำและไม่ได้รับการล้างโดยอัตโนมัติหลังจากเรียก F1 โดยอัตโนมัติ
ทำไมสิ่งนี้ถึงเกิดขึ้น? เหตุผลก็คือ F1 เป็นฟังก์ชั่นหลักของ F2 และ F2 ถูกกำหนดให้กับตัวแปรทั่วโลกซึ่งทำให้ F2 อยู่ในหน่วยความจำเสมอและการมีอยู่ของ F2 ขึ้นอยู่กับ F1 ดังนั้น F1 จึงอยู่ในหน่วยความจำเสมอและจะไม่ถูกรีไซเคิลโดยกลไกการรวบรวมขยะหลังจากการโทรเสร็จสิ้น
อีกจุดสำคัญในรหัสนี้คือบรรทัด "nadd = function () {n+= 1}" ถูกใช้ก่อน NADD ดังนั้น NADD จึงเป็นตัวแปรส่วนกลางไม่ใช่ตัวแปรท้องถิ่น ประการที่สองค่าของ NADD เป็นฟังก์ชั่นที่ไม่ระบุชื่อและฟังก์ชั่นนิรนามนี้เองก็เป็นการปิดดังนั้น NADD จึงเทียบเท่ากับ setter ซึ่งสามารถทำงานกับตัวแปรท้องถิ่นภายในฟังก์ชั่นนอกฟังก์ชั่น
5. หมายเหตุเกี่ยวกับการใช้การปิด
1) เนื่องจากการปิดจะทำให้ตัวแปรทั้งหมดในฟังก์ชันถูกเก็บไว้ในหน่วยความจำและการใช้หน่วยความจำมีขนาดใหญ่มากการปิดไม่สามารถถูกทารุณกรรมไม่เช่นนั้นจะทำให้เกิดปัญหาประสิทธิภาพของหน้าเว็บและอาจนำไปสู่การรั่วไหลของหน่วยความจำใน IE วิธีแก้ปัญหาคือการลบตัวแปรท้องถิ่นทั้งหมดที่ไม่ได้ใช้ก่อนออกจากฟังก์ชั่น
2) การปิดจะเปลี่ยนค่าของตัวแปรภายในฟังก์ชั่นหลักนอกฟังก์ชั่นหลัก ดังนั้นหากคุณใช้ฟังก์ชั่นหลักเป็นวัตถุให้ใช้การปิดเป็นวิธีการสาธารณะและใช้ตัวแปรภายในเป็นคุณสมบัติส่วนตัวโปรดระวังอย่าเปลี่ยนค่าของตัวแปรภายในของฟังก์ชันหลักที่จะ
6. การคิดคำถาม
หากคุณสามารถเข้าใจผลลัพธ์การทำงานของรหัสสองชิ้นต่อไปนี้คุณควรพิจารณาทำความเข้าใจกลไกการทำงานของการปิด
รหัสตัวอย่าง 1:
ชื่อ var = "หน้าต่าง"; var object = {ชื่อ: "my object", getNameFunc: function () {return function () {return this.name; - - การแจ้งเตือน (Object.getNameFunc () () ());ผลการทำงาน:
รหัสตัวอย่างสอง:
ชื่อ var = "หน้าต่าง"; var object = {ชื่อ: "my object", getNameFunc: function () {var that = this; return function () {return that.name; - - การแจ้งเตือน (Object.getNameFunc () ());ผลการทำงาน:
ความคิดเห็นต่อไปนี้จะถูกเพิ่มลงในรหัสเพื่อวิเคราะห์ผลการทำงานของสองตัวอย่างรหัสข้างต้น:
รหัสตัวอย่าง 1:
การวิเคราะห์มีดังนี้:
/*ใน JavaScript วัตถุ JavaScript Global, ฟังก์ชั่นทั่วโลกและตัวแปรทั่วโลกที่เราประกาศว่าจะกลายเป็นสมาชิกของวัตถุหน้าต่างโดยอัตโนมัติ ตัวแปรทั่วโลกเป็นคุณสมบัติของวัตถุหน้าต่าง ฟังก์ชั่นทั่วโลกเป็นวิธีการของวัตถุหน้าต่าง */var name = "window"; // ประกาศชื่อตัวแปรส่วนกลางและในเวลานี้ชื่อตัวแปรส่วนกลางจะกลายเป็นแอตทริบิวต์ของวัตถุหน้าต่าง // หลักฐาน: การแจ้งเตือน ("window.name:"+window.name); // คุณสามารถเข้าถึงชื่อในรูปแบบของหน้าต่าง เวลาวัตถุวัตถุตัวแปรส่วนกลางจะกลายเป็นแอตทริบิวต์ของวัตถุหน้าต่าง var object = {ชื่อ: "วัตถุของฉัน", // ชื่อแอตทริบิวต์ของวัตถุ getNameFunc: function () {// getNameFunc ฟังก์ชั่นวัตถุวัตถุ // ค่าที่กลับมาของวัตถุ หมายถึงวัตถุใด // พิสูจน์ว่าสิ่งนี้ในฟังก์ชั่นที่ไม่ระบุชื่อหมายถึงวัตถุหน้าต่างแทน ObjectAlert ("สิ่งนี้ == ผลลัพธ์ของวัตถุคือ:"+(สิ่งนี้ == วัตถุ)); การแจ้งเตือน ("สิ่งนี้ == ผลลัพธ์หน้าต่างคือ:"+(หน้าต่างนี้ ==)); return this.name; // เนื่องจากสิ่งนี้แสดงถึงวัตถุหน้าต่างดังนั้นสิ่งนี้จะเป็นชื่อของวัตถุหน้าต่าง "หน้าต่าง"}; }}; // พิสูจน์: วัตถุวัตถุโกลบอลเป็นแอตทริบิวต์ของการแจ้งเตือนวัตถุหน้าต่าง ("window.Object:"+window.Object); แจ้งเตือน ("window.object.name:"+window.object.name);/*หลังจากเรียกเมธอด getNameFunc ในเวลานี้ RETFN แสดงถึงวิธีที่ไม่ระบุชื่อ ตอนนี้มันเทียบเท่ากับการให้ชื่อที่ไม่ระบุชื่อเป็นชื่อ retfn ในเวลานี้ฟังก์ชั่น RETFN จะกลายเป็นฟังก์ชั่นของวัตถุหน้าต่าง*/var retfn = object.getNameFunc (); แจ้งเตือน (retfn ()); // การเรียกวิธีการที่ไม่ระบุชื่อที่ส่งคืน มันเป็นวัตถุหน้าต่าง // การพิสูจน์: ฟังก์ชั่น retfn เป็นฟังก์ชั่นของการแจ้งเตือนวัตถุหน้าต่าง ("window.retfn ():"+window.retfn ()); // คุณสามารถเรียกวิธี retfn ในรูปแบบของ window.retfn () (ชื่อวัตถุ)รหัสตัวอย่างสอง:
การวิเคราะห์มีดังนี้:
var name = "The Window"; // ชื่อตัวแปรโกลบอล // Object Object Object Object = {ชื่อ: "My Object", getNameFunc: function () {/*วัตถุนี้แสดงถึงสิ่งนี้ในเวลานี้? สิ่งนี้แสดงถึงวัตถุวัตถุ วัตถุใดเรียกฟังก์ชั่นที่ตั้งอยู่? สิ่งนี้หมายถึงวัตถุที่ถูกดำเนินการว่า = สิ่งนี้และนั่นยังแสดงถึงวัตถุวัตถุ*/var ที่ = this; // นั่นคือตัวแปรท้องถิ่นที่ประกาศในฟังก์ชัน getNameFunc // พิสูจน์ว่าสิ่งนี้ในฟังก์ชัน getNameFunc แสดงวัตถุวัตถุแทนที่จะเป็น windowalert การแจ้งเตือน ("สิ่งนี้ == ผลลัพธ์ของหน้าต่างคือ:"+(this == window)); // พิสูจน์ว่าหมายถึงการแจ้งเตือนวัตถุวัตถุ ("นั่น == ผลลัพธ์ของวัตถุคือ:"+(นั่น == วัตถุ)); return function () {/*ที่เป็นตัวแปรท้องถิ่นที่ประกาศในฟังก์ชัน getNameFunc ภายใต้สถานการณ์ปกติหลังจากการเรียกฟังก์ชัน getNameFunc เสร็จสมบูรณ์ตัวแปรท้องถิ่นที่จะรีไซเคิลโดย GC ของ JavaScript ทำให้พื้นที่หน่วยความจำที่ถูกครอบครองโดยตัวแปรท้องถิ่นที่ แต่ตอนนี้สามารถใช้งานได้ตามปกติและยังไม่ได้รีไซเคิล เหตุผลก็คือ GetNameFunc เป็นฟังก์ชันหลักของฟังก์ชั่นที่ไม่ระบุชื่อ หลังจากเรียกฟังก์ชั่น getNameFunc แล้วฟังก์ชันที่ไม่ระบุชื่อจะถูกส่งกลับและกำหนดให้กับตัวแปร Global RetFN ซึ่งทำให้ฟังก์ชันที่ไม่ระบุชื่ออยู่ในหน่วยความจำเสมอและการดำรงอยู่ของฟังก์ชั่นที่ไม่ระบุชื่อนั้นขึ้นอยู่กับฟังก์ชั่น getNameFunc ดังนั้นฟังก์ชั่น getNameFunc อยู่ในหน่วยความจำเสมอและจะไม่ถูกนำกลับมาใช้ใหม่โดยกลไกการรวบรวมขยะหลังจากการโทรเสร็จสิ้น เนื่องจากฟังก์ชั่น getNameFunc อยู่ในหน่วยความจำเสมอตัวแปรท้องถิ่นที่ประกาศภายในฟังก์ชั่น getNameFunc จะมีอยู่ในหน่วยความจำเสมอ เนื่องจากมีอยู่แน่นอนว่ามันสามารถใช้งานต่อไปได้ */return that.name; // ที่แสดงถึงวัตถุวัตถุเพื่อให้ชื่อเข้าถึงชื่อวัตถุวัตถุ "my object"}; }}; var retfn = object.getNameFunc (); // หลังจากเรียกเมธอด getNameFunc วิธีที่ไม่ระบุชื่อจะถูกส่งคืน ในเวลานี้ RETFN แสดงถึงวิธีการที่ไม่ระบุชื่อซึ่งตอนนี้เทียบเท่ากับการให้ชื่อที่ไม่ระบุชื่อเป็นชื่อ Retfn Alert (retfn ());ในที่สุดฉันก็แนบตัวอย่างที่ฉันเขียนเมื่อฉันเรียนรู้การปิด JavaScript ก่อน:
<script type = "text/javascript"> function a () {var i =; // ประกาศตัวแปรท้องถิ่น i ภายในฟังก์ชั่น a/ประกาศฟังก์ชั่น subfunction bfunction b () {แจ้งเตือน ("i ="+(++ i)); // เข้าถึงตัวแปรท้องถิ่น ชี้ไปที่ฟังก์ชั่น b. ข. ตัวแปรที่ฉันใช้ หลังจากดำเนินการ c () หน้าต่างจะปรากฏขึ้นเพื่อแสดงค่าของ i (ครั้งแรก) และรหัสนี้จริง ๆ แล้วสร้างการปิดเพราะตัวแปร C นอกฟังก์ชั่น A หมายถึงฟังก์ชัน B ภายในฟังก์ชัน กล่าวคือ: เมื่อฟังก์ชั่นภายในของฟังก์ชั่น A ถูกอ้างอิงโดยตัวแปรภายนอกฟังก์ชั่น A การปิด "ปิด" ที่เรียกว่าจะถูกสร้างขึ้น หลังจากถูกดำเนินการและส่งคืนการปิดทำให้กลไกการรวบรวมขยะ JavaScript GC จะไม่รีไซเคิลทรัพยากรที่ถูกครอบครองโดย A เนื่องจากการดำเนินการของฟังก์ชั่นภายใน B ของความต้องการที่จะพึ่งพาตัวแปรใน A */A (); // จะมีพื้นที่ในหน่วยความจำแน่นอน หลังจากดำเนินการ () GC จะรีไซเคิลพื้นที่หน่วยความจำที่จัดสรรสำหรับ i var c = a (); // การใช้งานนี้, GC จะไม่ถือว่าฉันเป็นขยะและ c (); // เทียบเท่ากับการโทร b () ผลลัพธ์คือ: i = c (); // ผลลัพธ์คือ: i = c (); //ผลการทำงาน:
เนื้อหาข้างต้นเป็นคำอธิบายโดยละเอียดของรหัสปิด JavaScript (ปิด) ของจุดความรู้ JavaScript สรุป (16) แนะนำโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน!