ในกระบวนการเรียนรู้ส่วนหน้า เราจะเผชิญกับปัญหามากมายอย่างหลีกเลี่ยงไม่ได้ ดังนั้นวันนี้เราจะพูดถึงคำถามสองข้อจากมุมมองของผู้เริ่มต้น:
การปิดคืออะไร?
หน้าที่ของการปิดคืออะไร?
ที่จริงแล้ว การปิดเกิดขึ้นทุกที่เมื่อเราเรียนรู้ JavaScript คุณเพียงแค่ต้องสามารถจดจำและยอมรับมันได้ การปิดไม่ใช่เครื่องมือที่ต้องเรียนรู้ไวยากรณ์หรือรูปแบบใหม่เพื่อใช้ เราแทบไม่จำเป็นต้องสร้างการปิดโดยเจตนาเมื่อเขียนโค้ด
ฉันเชื่อว่าตอนนี้เพื่อน ๆ หลายคนกำลังพึมพำอยู่ในใจ ขอบเขตคำศัพท์นี้คืออะไร อย่าเพิ่งตกใจ เพียงแค่ฟังฉันช้าๆ กล่าวอีกนัยหนึ่ง ขอบเขตคำศัพท์ถูกกำหนดโดยตำแหน่งที่คุณวางตัวแปรและขอบเขตระดับบล็อกเมื่อคุณเขียนโค้ด ดังนั้นขอบเขตจะยังคงไม่เปลี่ยนแปลงเมื่อตัววิเคราะห์คำศัพท์ประมวลผลโค้ด (โดยส่วนใหญ่) ——"JavaScript You Don't Know"
มาดูตัวอย่างกันก่อน:
function test(){ วาร์ อาร์ = [] สำหรับ(var i=0;i<10;i++){ arr[i]=ฟังก์ชั่น(){ console.log(i); - - กลับถึง - var myArr = ทดสอบ() // myArr[0]() // myArr[1]() - สำหรับ(var j = 0; j < 10; j++){ myArr[เจ]() - //เพื่อหลีกเลี่ยงความน่าเบื่อ จึงมีการใช้การวนซ้ำครั้งที่สองที่นี่เพื่อเรียกใช้ฟังก์ชันในลูปแรกในฟังก์ชันทดสอบและพิมพ์ผลลัพธ์สิบรายการ
ก่อน มาวิเคราะห์โค้ดนี้ก่อน: เมื่อโค้ดนี้ถูกดำเนินการ ตามสามัญสำนึก ควรวิเคราะห์ โดยจะพิมพ์ตัวเลข 10 ตัวจาก 0 ถึง 9 ตามลำดับ แต่ for loop ไม่ได้ใช้เวลาในการรัน (ละเว้นเป็นไมโครวินาที) เมื่อการทดสอบฟังก์ชันส่งคืน arr จะมี 10 function(){console log(i );} ฟังก์ชันในอาร์เรย์จะไม่ถูกดำเนินการในขณะนี้ เมื่อ var myArr = test() เรียกใช้ฟังก์ชันทดสอบ เนื่องจากเวลาดำเนินการของ for loop ถูกละเว้น ตอนนี้ i อยู่ที่ 10 แล้ว ดังนั้นอะไรคือ พิมพ์เป็น 10 เต็ม 10
ผมเชื่อว่าคงมีคนถามในเวลานี้ว่าเกี่ยวอะไรกับการปิดที่เรากำลังจะพูดถึง? แล้วถ้าเราแก้ไขโค้ดนี้เล็กน้อยแล้วเปลี่ยนเป็นแบบสะสมเราจะนำไปใช้ได้อย่างไร?
ฉันเชื่อว่าในเวลานี้จะต้องมีคนสำคัญใครจะพูดว่ามันไม่ง่ายเหรอ?
เปลี่ยนคำจำกัดความ var เป็นคำจำกัดความ let เพื่อให้ for วงแรกกลายเป็นขอบเขตระดับบล็อก จากนั้นจึงกลายเป็นตัวสะสมได้ แน่นอนว่าไม่มีปัญหา
แต่สิ่งที่เรากำลังพูดถึงในวันนี้คือวิธีใช้งานตัวสะสมใน ES5 จากนั้นเรามาดูโค้ดต่อไปนี้:
function test(){ วาร์ อาร์ = [] สำหรับ(var i=0;i<10;i++){ (ฟังก์ชัน(เจ){ arr[j]=ฟังก์ชั่น(){ console.log(เจ); - })(ฉัน) - กลับถึง - var myArr = ทดสอบ() สำหรับ(var j = 0; j < 10; j++){ myArr[เจ]() }
เพื่อนที่ระมัดระวังจะพบว่านี่คือการเปลี่ยนเนื้อหาของฟังก์ชันในลูปให้เป็นฟังก์ชันที่ดำเนินการเอง แต่ผลลัพธ์ที่ได้ในขณะนี้คือการส่งออกตัวเลขสิบตัวจาก 0 ถึง 9 ตามลำดับ ซึ่งรวมถึงแพ็คเกจการปิดด้วย เมื่อเราเริ่มรันโค้ดนี้ วินาที for loop จะถูกเรียกสิบครั้ง เมื่อแต่ละฟังก์ชันดำเนินการด้วยตนเอง จะมีการสร้างออบเจ็กต์ AO ของฟังก์ชันดำเนินการด้วยตนเอง ชื่อแอตทริบิวต์คือ j โดยปกติแล้ว หลังจากดำเนินการฟังก์ชันการดำเนินการแล้ว วัตถุ AO ควรจะถูกทำลาย อย่างไรก็ตาม เมื่อดำเนินการ myarr[j] () วัตถุ AO ของ arr[j] ที่ด้านบนของห่วงโซ่ขอบเขต ตอนนี้ฉันค้นหาชื่อแอตทริบิวต์ j แล้ว แต่ไม่พบ ฉันค้นหาตามขอบเขตและพบมันในวัตถุ AO ของฟังก์ชันดำเนินการด้วยตนเอง ดังนั้นเมื่อฟังก์ชันดำเนินการด้วยตนเองสิ้นสุดลง AO ของมันจะสิ้นสุดลง วัตถุจะไม่ถูกนำกลับมาใช้ใหม่โดยกลไกการรวบรวมขยะ มิฉะนั้นข้อผิดพลาดจะถูกรายงานเมื่อดำเนินการ myarr[j] () และการปิดจะเกิดขึ้น
เมื่อ
ฟังก์ชัน
ฟังก์ชัน ข(){ วาร์ bbb = 234 console.log(aaa); - วาร์ aaa = 123 return b // b เกิดใน a แต่ถูกบันทึกไว้} วาร์โกล = 100 var สาธิต = a()ขั้นแรกเราใช้การรวบรวมล่วงหน้าเพื่อวิเคราะห์โค้ดของ
demo()
ขั้นแรก ให้กำหนดวัตถุ GO ทั่วโลก และค้นหาการประกาศตัวแปรส่วนกลางจะถูกใช้เป็นชื่อแอตทริบิวต์ของ GO ไม่ได้กำหนดไว้ ค้นหาได้ในการประกาศฟังก์ชัน ชื่อฟังก์ชันจะถูกใช้เป็นชื่อแอตทริบิวต์ของอ็อบเจ็กต์ GO และค่าจะถูกกำหนดให้กับเนื้อหาของฟังก์ชัน ในขณะนี้ ควรเป็น GO{ glob: undefinition--->100; b: fb() ; ฟังก์ชัน a {} } และสุดท้าย ฟังก์ชันพรีคอมไพล์ b ในฟังก์ชัน a เพื่อสร้าง AO ของ b { b: ไม่ได้กำหนด --->234} ในขณะนี้ ลำดับของห่วงโซ่ขอบเขตคือ 1 วัตถุ AO ของฟังก์ชัน b; 2. วัตถุ AO ของฟังก์ชัน a; 3. วัตถุ GO ทั่วโลก เมื่อเราพิมพ์ aaa ในฟังก์ชัน b เราจะเริ่มจากด้านบนของลูกโซ่ขอบเขต หากไม่มี aaa ในวัตถุ AO ของฟังก์ชัน b เราจะค้นหาลงไปตามลูกโซ่ขอบเขตเพื่อค้นหา AO ของฟังก์ชันระดับที่สอง a วัตถุคือการค้นหาค่า aaa เป็น 123 และส่งออกผลลัพธ์
หากเราไม่ได้วิเคราะห์จากมุมมองของการคอมไพล์ล่วงหน้า เราจะคิดว่า aaa ควรรายงานข้อผิดพลาดในเวลานี้ เมื่อดำเนินการ var demo = a() เมื่อการดำเนินการของฟังก์ชัน a สิ้นสุดลง จากนั้นวัตถุ AO จะสอดคล้องกัน a ควรถูกทำลาย ตามการวิเคราะห์สามัญสำนึก: เมื่อเราดำเนินการสาธิต ห่วงโซ่ขอบเขตควรสร้างวัตถุ AO และวัตถุ GO ของ b ในขณะนี้ มีเพียงวัตถุ AO ของ b และไม่มีวัตถุ AO ของ a ไม่ควรพิมพ์ค่า aaa แต่ขณะนี้ค่า aaa คือ 123 ซึ่งหมายความว่าวัตถุ AO ของ a ยังไม่ถูกทำลาย แล้วทำไม? เหตุผลก็คือมีการสร้างการปิดที่นี่ เมื่อการดำเนินการ var demo = a() เสร็จสิ้น กลไกการรวบรวมขยะจะถาม Brother a ฟังก์ชั่น ฉันคิดว่าคุณได้ดำเนินการเสร็จสิ้นแล้ว ? แต่ในเวลานี้ ฟังก์ชัน a ทำได้แค่ส่ายหัวอย่างช่วยไม่ได้และพูดว่า "พี่ชาย ฉันไม่แน่ใจว่าฉันได้ประหารชีวิตสำเร็จแล้วหรือไม่ หลังจากที่ฉันประหารชีวิตแล้ว ฉันได้สร้าง b แต่ b ไม่ได้อยู่ภายใต้การควบคุมของฉัน ดังนั้นฉันก็เลยเป็นเช่นนั้น" ไม่แน่ใจว่าถูกเรียกมาหรือเปล่า เลยไม่แน่ใจว่าได้ดำเนินการเก็บขยะเสร็จหรือยัง เพราะคุณไม่รู้ ฉันจะไม่รีไซเคิลมัน ฉันควรรายงานข้อผิดพลาด ดังนั้นในขณะนี้ออบเจ็กต์ AO จะไม่ถูกรีไซเคิล
ฉันเชื่อว่าจากตัวอย่างทั้งสองนี้ คุณมีความเข้าใจทั่วไปเกี่ยวกับการปิดแล้ว ต่อไป เราจะมาพูดถึงหน้าที่ของการปิด
หน้าที่ของการปิดคือ
- สามารถ
- การใช้ตัวแปรสาธารณะ ตัวอย่างเช่น:
- ตัวสะสม (3.js)
- แคช
- เพื่อให้เกิดการห่อหุ้ม การแปรรูป
- และการพัฒนาคุณลักษณะแบบโมดูลาร์เพื่อป้องกันการปนเปื้อนของตัวแปรทั่วโลก
เรามายกตัวอย่างการทำงานของการปิด ( 3.js)
จำนวนนับ = 0 ฟังก์ชั่นเพิ่ม() { นับคืน++ - console.log(เพิ่ม()); console.log(เพิ่ม()); console.log(add());
นี่เป็นรหัสสะสมที่ค่อนข้างธรรมดา แต่หากในระหว่างการฝึกงานหรือแม้กระทั่งในที่ทำงาน บริษัท ต้องการให้คุณห่อหุ้มตัวสะสมเป็นรหัสโมดูลาร์ ดังนั้นในเวลานี้เพื่อประโยชน์ของ module เราพยายามหลีกเลี่ยงการกำหนดตัวแปรส่วนกลางให้มากที่สุดเท่าที่จะเป็นไปได้ แต่เราจะบรรลุมันได้อย่างไรโดยไม่กำหนดตัวแปรส่วนกลาง ในเวลานี้ เราสามารถใช้การปิดได้
ฟังก์ชั่นเพิ่ม() { จำนวนนับ = 0 ฟังก์ชัน ก() { ++นับ console.log(นับ); - กลับ - var res = เพิ่ม() ละเอียด() ละเอียด() //หลังจากฟังก์ชัน add สิ้นสุดลง อ็อบเจ็กต์ AO ของ add จะไม่ถูกทำลาย เนื่องจากหลังจากที่ฟังก์ชัน add ถูกดำเนินการแล้ว การส่งคืนจะสร้างการปิดโดยไม่รู้ว่ามันถูกเรียกหรือไม่ เพื่อให้สามารถห่อหุ้มเป็นโมดูลโดยไม่ต้องใช้ ตัวแปรทั่วโลกแบบสะสม
นี่คือความเห็นส่วนตัวของผมเกี่ยวกับการปิดและหน้าที่ของมันครับ ตอนนี้ผมมีความเข้าใจแค่เพียงผิวเผินเท่านั้น หลังจากศึกษาและปรับปรุงแล้ว ก็จะมีบทความเกี่ยวกับการปิดตามมาอีกครับ ถูกต้องและก้าวหน้าไปด้วยกัน