ในบทความก่อนหน้านี้เราให้ภาพรวมของการอธิบายล่วงหน้า ก่อนที่จะเขียนโพสต์บล็อกนี้เราวางแผนที่จะเขียนกรณีคลาสสิกสองสามกรณี เมื่อพิจารณาว่ากรณีเหล่านั้นครอบคลุมมากขึ้นเรามีบล็อกโพสต์ทีละขั้นตอนซึ่งง่ายต่อการเรียนรู้และ JavaScript เชิงลึก
ลำดับ
เพื่อนร่วมงานไปสัมภาษณ์และผู้สัมภาษณ์ถามคำถาม: คุณเขียนปิดแล้วฉันจะอ่านหรือไม่? ดังนั้นเพื่อนร่วมงานของฉันจึงเขียนรหัสต่อไปนี้อย่างรวดเร็ว:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น fn () {
การแจ้งเตือน ('Hello JavaScript Closure !!!'); // แช่งมันข้อความ E ไม่ดีคุณสามารถเขียนคำปิดได้หลังจากค้นหาการแปล
-
fn ();
จากนั้นผู้สัมภาษณ์ก็ส่ายหัวแล้วพูดว่า "สิ่งนี้จะเรียกได้ว่าการปิด?" ในท้ายที่สุดพวกเขาทั้งสองมีข้อพิพาทและเพื่อนร่วมงานก็ออกไปอย่างเด็ดขาด ผู้สัมภาษณ์คืออะไร? (เรื่องนี้เป็นเรื่องสมมติอย่างหมดจดหากมีความคล้ายคลึงกันมันเป็นเรื่องบังเอิญอย่างหมดจด)
การปิดอาจเป็นเทคโนโลยี "ระดับไฮเอนด์และยาก" ในสายตาของหลาย ๆ คน แต่ในสายตาของหลาย ๆ คนพวกเขาอาจได้รับการพิจารณาว่าปิด:
ตัวอย่างที่ 1:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น fn () {
return function () {
การแจ้งเตือน ('ตัวอย่าง 1');
-
-
fn () ();
ตัวอย่างที่ 1 PS: อันนี้ดูไม่ก้าวหน้ามากดูเหมือนว่าคนนี้จะไม่ค่อยดีนัก!
ตัวอย่างที่ 2:
การคัดลอกรหัสมีดังนี้:
;(การทำงาน () {
การแจ้งเตือน ('ตัวอย่าง 2');
-
ตัวอย่างที่ 2 PS: สิ่งนี้ดูก้าวหน้ากว่าครั้งก่อนและมีเครื่องหมายอัฒภาคก่อนวงเล็บแรก ทำไมต้องเพิ่มเครื่องหมายอัฒภาค? เอาคำถามนี้มาก่อนแล้วเราจะพูดถึงเรื่องนี้ในภายหลัง
ตัวอย่างที่ 3:
การคัดลอกรหัสมีดังนี้:
~ function fn () {
การแจ้งเตือน ('ตัวอย่าง 3')
-
ตัวอย่างที่ 3 PS: นี่เป็นสิ่งที่ทันสมัยที่สุดมันน่าทึ่งมาก ฉันอ่านน้อยลงอย่าโกหกฉัน!
เจ้านายของ LUN ไม่ค่อยดีนักที่อ่านและเขาสามารถเขียน "ปิด" ทั้งสามนี้เท่านั้น ฉันเชื่อว่าบล็อกเกอร์สามารถเขียน "ปิด" ได้ดีขึ้นเรื่อย ๆ ; ที่นี่โปรดหยุดเรื่องไร้สาระของฉันก่อนจากนั้นศึกษากลไกการทำงานของฟังก์ชั่น ดูเหมือนว่ามีคนรู้อยู่แล้วมันต้องเป็นขอบเขต ฉันไม่ต้องการเพิ่มขอบเขตนี้ในชื่อเรื่องดังนั้นฉันจึงรู้สึกว่ามันเกือบจะมีความหมาย สิ่งเหล่านี้อยู่ด้วยกันแล้วทำไมฉันต้องทำซ้ำ นิสัยเก่าอัปโหลดรหัสก่อน:
การคัดลอกรหัสมีดังนี้:
var n = 10;
ฟังก์ชั่น fn () {
การแจ้งเตือน (n);
var n = 9;
การแจ้งเตือน (n);
-
fn ();
หากต้องการพูดง่ายๆเราวาดรูปภาพ (ต้นแบบใช้ซอฟต์แวร์วาดภาพที่มาพร้อมกับ Windows หากมีภาพที่ดีกว่าโปรดแนะนำให้บล็อกเกอร์) เพื่อวิเคราะห์:
การวิเคราะห์ 1
จากรูปเราจะเห็นสองขอบเขตหนึ่งคือขอบเขตหน้าต่าง (ขอบเขตระดับบนสุด) และอีกขอบเขตหนึ่งเป็นขอบเขตส่วนตัวที่เกิดขึ้นเมื่อเรียกว่า FN; แล้วขอบเขตคืออะไร? ขอบเขตคือสภาพแวดล้อมสำหรับการดำเนินการรหัส ตัวอย่างเช่นสภาพแวดล้อมการเรียนรู้ของนักเรียนเป็นโรงเรียนซึ่งเทียบเท่ากับขอบเขตของเขา หากนักเรียนซุกซนและมักจะไปที่อินเทอร์เน็ตคาเฟ่เพื่อเล่นเกมในเวลากลางคืนมันก็เทียบเท่ากับการสร้างสภาพแวดล้อมส่วนตัวซึ่งเป็นอินเทอร์เน็ตคาเฟ่ เอาล่ะ! Lizi นี้เป็นเช่นนั้นจนกระทั่งเธอดูเหมือนอาจารย์ตัวเองและเธอก็อดถอนได้ แต่ถอนหายใจ: "ถ้าคุณไม่ทำงานหนักเมื่อคุณยังเด็กคุณจะถูกเตะเมื่อคุณโตขึ้น" กลับไปที่หัวข้อกันเถอะ ในความเป็นจริงคำจำกัดความของฟังก์ชั่น FN หมายถึงคำอธิบายของชิ้นส่วนของรหัส (กล่องสีแดงในรูป) เมื่อ FN นี้เรียกว่า (กล่องสีเขียวในรูป) ขอบเขตจะเกิดขึ้น แน่นอนรหัสในขอบเขตนี้จะถูกอธิบายล่วงหน้าก่อนดำเนินการ ฉันจะไม่บอกคุณว่าขอบเขตนี้จะถูกทำลายหลังจากดำเนินการ FN นี้ถูกเรียกอีกครั้งและมันจะสร้างขอบเขตใหม่จากนั้นอธิบายล่วงหน้าก่อนการดำเนินการจากนั้นการดำเนินการโค้ดและในที่สุดก็ถูกทำลายหลังจากดำเนินการ
เข้าใจการปิด
เรารู้ว่าเมื่อมีการเรียกฟังก์ชั่นขอบเขตส่วนตัว (สภาพแวดล้อมการดำเนินการ) จะสร้างขอบเขตส่วนตัว (สภาพแวดล้อมการดำเนินการ) และขอบเขตส่วนตัวนี้เป็นการปิด เมื่อมองย้อนกลับไปการปิดยังคงเป็นตำนาน "ระดับไฮเอนด์และไม่ง่ายที่จะไป" หรือไม่? ลองดูเรื่องราวการสัมภาษณ์ครั้งแรกและตัวอย่างทั้งสามที่ฉันเขียนพวกเขาปิดตัวลงจริง เพื่อความแม่นยำสามตัวอย่างเหล่านี้เป็นรูปแบบการปิดทั่วไป
สถานการณ์แอปพลิเคชัน
ตอนนี้มีข้อกำหนด: มีแท็ก UL ในหน้า HTML และมีแท็ก 5 li ภายใต้ UL คุณต้องการคลิกที่ LI และดัชนี (ดัชนีเริ่มต้นจาก 0) โดยที่ LI ที่ถูกคลิกจะปรากฏขึ้น โครงสร้าง HTML มีดังนี้:
การคัดลอกรหัสมีดังนี้:
<ul id = "ul">
<li> รายการ 1 </li>
<li> รายการ 2 </li>
<li> รายการ 3 </li>
<li> รายการ 4 </li>
<li> รายการ 5 </li>
</ul>
ฉันฉลาดและเขียนรหัสต่อไปนี้อย่างรวดเร็ว:
การคัดลอกรหัสมีดังนี้:
var lis = document.getElementById ('ul'). getElementsByTagname ('li');
สำหรับ (var i = 0, len = lis.length; i <len; i ++) {
lis [i] .onclick = function () {
การแจ้งเตือน (i);
-
-
การทดสอบขั้นสุดท้ายเพื่อดูว่าข้อกำหนดนี้ถูกนำไปใช้อย่างสมบูรณ์แบบหรือไม่:
ฉันพบว่าไม่ว่าฉันจะคลิกกี่ครั้งผลลัพธ์นี้จะปรากฏขึ้นในที่สุด ผลลัพธ์ที่ต้องการคือ: คลิกรายการ 1 ถึงป๊อปอัพ 0, คลิกรายการ 2 เพื่อป๊อปอัพ 1, คลิกรายการ 3 ถึงป๊อปอัพ 2 ... ในขณะนี้ฉันแค่ต้องการใช้ภาพนี้เพื่ออธิบายอารมณ์ปัจจุบันของฉัน:
(มันมีลักษณะอย่างไรเมื่อต้นแบบไม่สามารถทำงานเป็นข้อกำหนดการออกแบบระหว่างการสาธิต)
ฉันควรทำอย่างไร? ทำไม 5 ถึงปรากฏขึ้นเสมอ? มันถูกต้องมากในทางทฤษฎี! ลองวาดภาพเพื่อวิเคราะห์:
ในความเป็นจริงเราเพียงแค่ให้แต่ละ li onclick ซึ่งจริง ๆ แล้วเป็นสตริงคำอธิบายฟังก์ชั่นที่บันทึก เนื้อหาของสตริงนี้เป็นเนื้อหาในกล่องสีแดงของรูปภาพด้านบน ถ้าคุณยังไม่เชื่อฉันมีภาพและความจริง:
Enter: lis [4] .onclick ในคอนโซลโครเมี่ยมและค่าของมันคือคำอธิบายของฟังก์ชั่น เมื่อเราคลิกที่รายการที่ห้ามันเทียบเท่ากับ lis [4] .onclick () หลังจากเรียกคำอธิบายฟังก์ชั่นนี้แล้วเรารู้ว่าเมื่อฟังก์ชั่นถูกดำเนินการแล้วมันจะเป็นขอบเขตส่วนตัว ในขอบเขตส่วนตัวนี้จะถูกอธิบายไว้ล่วงหน้าก่อนจากนั้นรหัสจะถูกดำเนินการ ในเวลานี้เราจะมองหาฉัน ไม่มีฉันอยู่ภายใต้ขอบเขตส่วนตัวปัจจุบันแล้วพบว่าฉันอยู่ใต้ขอบเขตหน้าต่างดังนั้น 5 จะปรากฏขึ้นทุกครั้งที่คุณคลิก
เห็นได้ชัดว่ารหัสข้างต้นไม่สามารถตอบสนองความต้องการนี้ได้ การเขียนรหัสของเราไม่ถูกต้อง ลองนึกถึงเหตุผลของปัญหาหรือไม่ อันที่จริงเหตุผลก็คือทุกครั้งที่ฉันคลิกมันจะถูกอ่านใน I Under Window ในเวลานี้ค่าของฉันอยู่แล้ว 5 แล้วดังนั้นรหัสต่อไปนี้จึงพร้อมใช้งาน:
วิธีที่ 1:
การคัดลอกรหัสมีดังนี้:
var lis = document.getElementById ('ul'). getElementsByTagname ('li');
ฟังก์ชั่น fn (i) {
return function () {
การแจ้งเตือน (i);
-
-
สำหรับ (var i = 0, len = lis.length; i <len; i ++) {
lis [i] .onclick = fn (i);
-
วิธีที่ 2:
การคัดลอกรหัสมีดังนี้:
var lis = document.getElementById ('ul'). getElementsByTagname ('li');
สำหรับ (var i = 0, len = lis.length; i <len; i ++) {
; (ฟังก์ชั่น (i) {
lis [i] .onclick = function () {
การแจ้งเตือน (i);
-
})(ฉัน);
-
วิธีที่ 3:
การคัดลอกรหัสมีดังนี้:
var lis = document.getElementById ('ul'). getElementsByTagname ('li');
สำหรับ (var i = 0, len = lis.length; i <len; i ++) {
lis [i] .onclick = ฟังก์ชั่น fn (i) {
return function () {
การแจ้งเตือน (i);
-
}(ฉัน);
-
ฉันเขียนสามวิธีในหนึ่งลมหายใจและความคิดเหมือนกันซึ่งก็คือการเก็บตัวแปรนี้ฉันอยู่ในตัวแปรส่วนตัว ที่นี่ฉันจะพูดถึงวิธีที่สองเท่านั้น แน่นอนถ้าคุณเข้าใจหนึ่งในนั้นคุณจะเข้าใจส่วนที่เหลือ ตามการประชุมลองวาดภาพและวิเคราะห์ทีละขั้นตอน:
ฉันได้อธิบายการดำเนินการรหัสทั้งหมดโดยละเอียด ควรสังเกตว่าแอตทริบิวต์ onclick ของแต่ละ Li จะต้องครอบครอง (ฟังก์ชั่น (i) {…}) (i) ขอบเขต เมื่อฟังก์ชั่นนี้ถูกดำเนินการมันจะไม่ถูกทำลายเพราะมันถูกครอบครองโดย LI ภายนอก (LI นี้อยู่ภายใต้ขอบเขตหน้าต่าง) ดังนั้นขอบเขตนี้จะไม่ถูกทำลาย เมื่อคลิกที่ li, function () {Alert (i); } จะถูกดำเนินการและขอบเขตจะเกิดขึ้นเช่นกัน ขอบเขตนี้ไม่มีฉัน มันจะไปที่ (function () {…}) (i) ขอบเขตเพื่อค้นหาฉันและในที่สุดก็พบว่าฉันอยู่ในพารามิเตอร์ที่เป็นทางการและค่าของพารามิเตอร์ที่เป็นทางการนี้จะถูกส่งผ่านในระหว่างการวนรอบ; ตัวอย่างนี้ใช้การปิดอย่างชาญฉลาดเพื่อจัดเก็บค่าการแก้ปัญหาอย่างสมบูรณ์แบบ
PS: ฉันเพิ่งพูดว่า (ฟังก์ชั่น (i) {…}) (i) ทำไมฉันถึงเพิ่มเครื่องหมายอัฒภาคในด้านหน้าของมันคือการป้องกันไม่ให้คำสั่งก่อนหน้านี้ลืมที่จะเพิ่มเครื่องหมายอัฒภาคซึ่งทำให้ JavaScript ทำข้อผิดพลาดระหว่างการแยกวิเคราะห์และนั่นคือทั้งหมด แน่นอนว่าหนึ่งในสถานการณ์แอปพลิเคชันข้างต้นคือหลักการใช้งานแท็บ อาจมีวิธีการใช้งานอื่น ๆ เช่นวิธีแอตทริบิวต์ที่กำหนดเองและการค้นหาดัชนีผ่านความสัมพันธ์ของโหนด DOM อาจารย์ใช้วิธีนี้เพียงเพื่อให้เข้าใจถึงการปิดอย่างลึกซึ้งยิ่งขึ้น
สรุป
การปิดไม่สูงเท่าตำนาน หลักคือการเข้าใจคำจำกัดความของฟังก์ชั่นและการโทร ขอบเขตส่วนตัวใหม่จะเกิดขึ้นเมื่อมีการเรียกฟังก์ชั่น เมื่อขอบเขตถูกครอบครองโดยภายนอกขอบเขตจะไม่ถูกทำลาย อาจารย์ของ Lu อ่านน้อยมาก หากมีสิ่งผิดพลาดใด ๆ โปรดแก้ไขให้ฉันโดยบล็อกเกอร์ ในเวลาเดียวกันฉันขอขอบคุณสำหรับการสนับสนุนบทความของ Master of Luke