เติมเต็ม:
การปิดเป็นเรื่องยากในภาษาจาวาสคริปต์และคุณสมบัติของมัน แอปพลิเคชันขั้นสูงจำนวนมากพึ่งพาการปิดเพื่อนำไปใช้
คุณสมบัติการปิด
การปิดมีสามลักษณะ:
1. ฟังก์ชั่นฟังก์ชั่นซ้อนกัน
2. ฟังก์ชั่นสามารถอ้างถึงพารามิเตอร์ภายนอกและตัวแปรภายใน
3. พารามิเตอร์และตัวแปรจะไม่ถูกรวบรวมโดยกลไกการรวบรวมขยะ
คำจำกัดความของการปิดและข้อดีและข้อเสียของมัน
การปิดอ้างถึงฟังก์ชั่นที่มีการเข้าถึงตัวแปรในขอบเขตของฟังก์ชั่นอื่น วิธีที่พบบ่อยที่สุดในการสร้างการปิดคือการสร้างฟังก์ชั่นอื่นภายในฟังก์ชั่นหนึ่งและเข้าถึงตัวแปรท้องถิ่นของฟังก์ชั่นนี้ผ่านฟังก์ชั่นอื่น
ข้อเสียของการปิดคือพวกเขาเป็นหน่วยความจำผู้อยู่อาศัยซึ่งจะเพิ่มการใช้หน่วยความจำและการใช้งานที่ไม่เหมาะสมสามารถนำไปสู่การรั่วไหลของหน่วยความจำได้อย่างง่ายดาย
การปิดเป็นคุณสมบัติสำคัญของภาษาจาวาสคริปต์ แอพพลิเคชั่นหลักของการปิดส่วนใหญ่สำหรับ: การออกแบบวิธีการส่วนตัวและตัวแปร
หลังจากดำเนินการฟังก์ชั่นทั่วไปวัตถุที่ใช้งานในพื้นที่จะถูกทำลายและมีเพียงขอบเขตทั่วโลกเท่านั้นที่บันทึกไว้ในหน่วยความจำ แต่สถานการณ์การปิดนั้นแตกต่างกัน!
เพื่อพูดคุยเกี่ยวกับหัวข้อ
1. คำจำกัดความของการปิด?
มาดูคำจำกัดความเกี่ยวกับการปิด:
1. การปิดหมายถึงฟังก์ชั่นที่ได้รับอนุญาตให้เข้าถึงตัวแปรในขอบเขตฟังก์ชันอื่น
2. วัตถุฟังก์ชั่นสามารถเชื่อมโยงผ่านโซ่ขอบเขตและตัวแปรภายในร่างกายฟังก์ชั่นสามารถบันทึกไว้ในขอบเขตของฟังก์ชัน ลักษณะนี้เรียกว่า 'ปิด'
3. ฟังก์ชั่นภายในสามารถเข้าถึงพารามิเตอร์และตัวแปรของฟังก์ชั่นภายนอกที่กำหนด (ยกเว้นสิ่งนี้และอาร์กิวเมนต์)
หากคุณต้องการเรียนรู้แนวคิดของการปิด JS อย่างเป็นระบบคุณสามารถอ้างถึงคอลัมน์ JS e-book ของเว็บไซต์ Wulin.com เพื่อเรียนรู้
มาสรุปคำจำกัดความ
1. คุณสามารถเข้าถึงฟังก์ชั่นของตัวแปรในขอบเขตฟังก์ชันภายนอก
2. ตัวแปรของฟังก์ชั่นภายนอกที่เข้าถึงได้โดยฟังก์ชั่นภายในสามารถบันทึกได้ภายในขอบเขตของฟังก์ชั่นภายนอกโดยไม่ต้องรีไซเคิล-นี่คือแกน ต่อมาเมื่อเราพบการปิดเราควรคิดถึงมัน เราควรมุ่งเน้นไปที่ตัวแปรที่อ้างอิงโดยการปิด
สร้างการปิดง่าย ๆ
var saysname = function () {var name = 'jozo'; return function () {Alert (ชื่อ);}}; var says = sayname (); พูด();ตีความสองประโยคถัดไป:
• var says = sayname (): ส่งคืนฟังก์ชั่นภายในที่ไม่ระบุชื่อที่เก็บไว้ในตัวแปรพูดและอ้างถึงชื่อตัวแปรของฟังก์ชันภายนอก เนื่องจากกลไกการรวบรวมขยะหลังจากฟังก์ชั่น Sayname ถูกดำเนินการชื่อตัวแปรจะไม่ถูกทำลาย
•พูด (): ดำเนินการฟังก์ชั่นภายในที่ส่งคืนและยังคงเข้าถึงชื่อตัวแปรและเอาต์พุต 'Jozo'
2. โซ่ขอบเขตในการปิด
การทำความเข้าใจขอบเขตขอบเขตก็มีประโยชน์สำหรับการทำความเข้าใจการปิด
วิธีการค้นหาของตัวแปรในขอบเขตควรคุ้นเคยมาก แต่อันที่จริงนี่คือสิ่งที่ค้นหาขึ้นไปตามโซ่ขอบเขต
เมื่อเรียกใช้ฟังก์ชัน:
1. ก่อนสร้างบริบทการดำเนินการและห่วงโซ่ขอบเขตที่สอดคล้องกัน
2. เพิ่มอาร์กิวเมนต์และค่าพารามิเตอร์ชื่ออื่น ๆ ไปยังวัตถุที่ใช้งานของฟังก์ชัน (วัตถุการเปิดใช้งาน)
โซ่ขอบเขต: วัตถุที่ใช้งานของฟังก์ชันปัจจุบันมีลำดับความสำคัญสูงสุดตามด้วยวัตถุที่ใช้งานของฟังก์ชันภายนอกและวัตถุที่ใช้งานของฟังก์ชันภายนอกของฟังก์ชันภายนอกจะลดลงตามลำดับจนกระทั่งสิ้นสุดโซ่ขอบเขต - ขอบเขตส่วนกลาง ลำดับความสำคัญคือลำดับของการค้นหาตัวแปร
ก่อนอื่นให้ดูที่ห่วงโซ่ขอบเขตปกติ:
ฟังก์ชั่น saysname (ชื่อ) {return name;} var says = sayname ('jozo');รหัสนี้มีสองขอบเขต: ขอบเขตทั่วโลก; ขอบเขตฟังก์ชัน B.SayName นั่นคือมีเพียงสองวัตถุตัวแปร เมื่อดำเนินการกับสภาพแวดล้อมการดำเนินการที่สอดคล้องกันวัตถุตัวแปรจะกลายเป็นวัตถุที่ใช้งานอยู่และถูกผลักไปที่ปลายด้านหน้าของห่วงโซ่ขอบเขตของสภาพแวดล้อมการดำเนินการนั่นคือมันจะกลายเป็นลำดับความสำคัญสูงสุด คุยกับภาพ:
ภาพนี้ยังมีอยู่ในหนังสือการเขียนโปรแกรมขั้นสูงของ JS และฉันได้วาดอีกครั้ง
เมื่อสร้างฟังก์ชั่น sayname () โซ่ขอบเขตที่มีวัตถุตัวแปรล่วงหน้าถูกสร้างขึ้นนั่นคือโซ่ขอบเขตที่จัดทำดัชนีโดย 1 ในรูปและถูกบันทึกไว้ในแอตทริบิวต์ [[ขอบเขต]] ภายใน เมื่อเรียกใช้ฟังก์ชัน sayname () สภาพแวดล้อมการดำเนินการจะถูกสร้างขึ้นจากนั้นโซ่ขอบเขตจะถูกสร้างขึ้นโดยการคัดลอกวัตถุในแอตทริบิวต์ [[ขอบเขต]] ของฟังก์ชัน หลังจากนั้นวัตถุที่ใช้งานอื่น (จัดทำดัชนีโดย 0 ในรูป) จะถูกสร้างและผลักเข้าไปในปลายด้านหน้าของห่วงโซ่ขอบเขตของสภาพแวดล้อมการดำเนินการ
โดยทั่วไปเมื่อมีการดำเนินการฟังก์ชั่นวัตถุที่ใช้งานในพื้นที่จะถูกทำลายและมีเพียงขอบเขตทั่วโลกเท่านั้นที่จะถูกบันทึกไว้ในหน่วยความจำ อย่างไรก็ตามสถานการณ์ของการปิดนั้นแตกต่างกัน:
มาดูโซ่ปิดขอบเขตของการปิด:
ฟังก์ชั่น saysname (ชื่อ) {return function () {return name;}} var says = sayname ('jozo');อินสแตนซ์การปิดนี้มีขอบเขตมากกว่าหนึ่งรายการสำหรับฟังก์ชันที่ไม่ระบุชื่อมากกว่าตัวอย่างก่อนหน้า:
หลังจากฟังก์ชั่นที่ไม่ระบุชื่อถูกส่งคืนจากฟังก์ชั่น sayname () โซ่ขอบเขตของมันจะเริ่มต้นเป็นวัตถุที่ใช้งานอยู่และวัตถุตัวแปรส่วนกลางที่มีฟังก์ชัน Sayname () ด้วยวิธีนี้ฟังก์ชั่นที่ไม่ระบุชื่อสามารถเข้าถึงตัวแปรและพารามิเตอร์ทั้งหมดที่กำหนดไว้ใน sayname () ที่สำคัญหลังจากการดำเนินการของฟังก์ชัน sayname () วัตถุที่ใช้งานจะไม่ถูกทำลายเนื่องจากห่วงโซ่ขอบเขตของฟังก์ชันที่ไม่ระบุชื่อยังคงหมายถึงวัตถุที่ใช้งานอยู่ กล่าวอีกนัยหนึ่งหลังจากการดำเนินการของฟังก์ชั่น sayname () โซ่ขอบเขตของสภาพแวดล้อมการดำเนินการจะถูกทำลาย แต่วัตถุที่ใช้งานอยู่จะถูกทิ้งไว้ในความทรงจำโดยรู้ว่าฟังก์ชั่นที่ไม่ระบุชื่อจะถูกทำลาย นี่เป็นปัญหาการรั่วไหลของหน่วยความจำที่จะกล่าวถึงในภายหลัง
ฉันไม่ได้เขียนเกี่ยวกับปัญหาโซ่ขอบเขตมากนักและการเขียนสิ่งต่าง ๆ ในหนังสือเล่มนี้ก็น่าเบื่อหน่ายมาก (□) o
3. ตัวอย่างของการปิด
ตัวอย่างที่ 1: การดำเนินการสะสม
// วิธีการ 1VAR A = 0; var add = function () {a ++; console.log (a)} เพิ่ม (); เพิ่ม (); // วิธีการ 2: ปิด var เพิ่ม = (ฟังก์ชัน () {var a = 0; return function () {a ++; console.log (a);}}) () // undefinedAdd (); เพิ่ม ();ในการเปรียบเทียบวิธีที่ 2 นั้นสง่างามมากขึ้นและยังช่วยลดตัวแปรทั่วโลกและแปรรูปตัวแปร
ตัวอย่างที่ 2: เพิ่มเหตุการณ์คลิกในแต่ละ Li
var oli = document.getElementsByTagname ('li'); var i; สำหรับ (i = 0; i <5; i ++) {oli [i] .onclick = function () {Alert (i);}} console.log (i); // 5 // เรียกใช้ฟังก์ชันที่ไม่ระบุชื่อ (ฟังก์ชัน () {แจ้งเตือน (i); // 5} ());ข้างต้นเป็นตัวอย่างคลาสสิก เราทุกคนรู้ว่าผลการดำเนินการคือ 5 โผล่ขึ้นมาและเราก็รู้ว่าการปิดสามารถใช้เพื่อแก้ปัญหานี้ได้ แต่ในตอนแรกฉันยังไม่เข้าใจว่าทำไม 5 คนปรากฏขึ้นและทำไมการปิดสามารถแก้ปัญหานี้ได้ ต่อมาฉันแยกออกและทำให้ชัดเจน:
. ก่อนอื่นมาวิเคราะห์สถานการณ์ก่อนที่จะใช้การปิด: ในการวนรอบเราจะผูกฟังก์ชันที่ไม่ระบุชื่อกับเหตุการณ์ LI คลิกแต่ละครั้งและค่าของตัวแปรที่ฉันกลับมาในฟังก์ชันที่ไม่ระบุชื่อ เมื่อลูปสิ้นสุดลงค่าของตัวแปรที่ฉันจะกลายเป็น 5 ในเวลานี้เราคลิกที่แต่ละ li นั่นคือดำเนินการฟังก์ชันที่ไม่ระบุชื่อที่สอดคล้องกัน (ดูรหัสด้านบน) นี่คือตัวแปรที่ฉัน 5 แล้วดังนั้นการคลิกแต่ละครั้งจะปรากฏขึ้น 5 เนื่องจากฟังก์ชั่นนิรนามแต่ละรายการที่กลับมาที่นี่หมายถึงตัวแปรเดียวกันฉันถ้าเราสร้างตัวแปรใหม่เพื่อบันทึกค่าปัจจุบันของฉันเมื่อลูปถูกดำเนินการ นี่คือความสำเร็จโดยใช้การปิด!
ข. มาวิเคราะห์สถานการณ์เมื่อใช้การปิด:
var oli = document.getElementsByTagname ('li'); var i; สำหรับ (i = 0; i <5; i ++) {oli [i] .onclick = (ฟังก์ชั่น (num) {var a = num; // เพื่อแสดงฟังก์ชั่นการส่งคืนปัญหา () // 5เมื่อมีการดำเนินการสำหรับลูปฟังก์ชั่นที่ไม่ระบุชื่อที่ถูกผูกไว้กับเหตุการณ์การคลิกจะผ่านฉันและดำเนินการทันทีเพื่อส่งคืนฟังก์ชันที่ไม่ระบุชื่อภายใน เนื่องจากพารามิเตอร์จะถูกส่งผ่านตามค่าพารามิเตอร์ที่เป็นทางการจะบันทึกค่าปัจจุบันของ I แล้วกำหนดค่าให้กับตัวแปรท้องถิ่น จากนั้นฟังก์ชั่นที่ไม่ระบุชื่อภายในจะช่วยอ้างอิงของ A นั่นคือรักษาค่าปัจจุบันของ i ดังนั้นหลังจากดำเนินการลูปแล้วให้คลิกแต่ละ LI และฟังก์ชั่นนิรนามที่ส่งคืนจะปรากฏขึ้นค่าของ A ที่บันทึกไว้
4. แอปพลิเคชันปิด
ลองมาดูจุดประสงค์ในการปิด ในความเป็นจริงโดยใช้การปิดเราสามารถทำสิ่งต่าง ๆ ได้มากมาย ตัวอย่างเช่นจำลองรูปแบบรหัสเชิงวัตถุ Express Code อย่างสง่างามและรัดกุมมากขึ้น และปรับปรุงประสิทธิภาพการดำเนินการโค้ดในบางด้าน
1. ฟังก์ชั่นการดำเนินการตนเองที่ไม่ระบุชื่อ
ในสถานการณ์จริงเรามักจะพบกับสถานการณ์ที่บางฟังก์ชั่นจำเป็นต้องดำเนินการเพียงครั้งเดียวและตัวแปรภายในของพวกเขาไม่จำเป็นต้องได้รับการดูแลเช่นการเริ่มต้น UI ดังนั้นเราจึงสามารถใช้การปิดได้:
// เปลี่ยนแบบอักษร li ทั้งหมดเป็นสีแดง (ฟังก์ชั่น () {var els = document.getElementByTagname ('li'); สำหรับ (var i = 0, lng = els.length; i <lng; i ++) {els [i] .style.color = 'สีแดง';}}) ()เราสร้างฟังก์ชั่นที่ไม่ระบุชื่อและดำเนินการทันที เนื่องจากภายนอกไม่สามารถอ้างถึงตัวแปรภายในตัวแปรท้องถิ่นเช่น ELS, I และ LNG จะถูกปล่อยออกมาในไม่ช้าหลังจากดำเนินการบันทึกหน่วยความจำ!
กุญแจสำคัญคือกลไกนี้จะไม่ก่อให้เกิดมลพิษจากวัตถุระดับโลก
2. ใช้รหัสห่อหุ้ม/โมดูลาร์
var person = function () {// ขอบเขตของตัวแปรอยู่ในฟังก์ชันและไม่สามารถเข้าถึงชื่อ var = "default" ภายนอกได้ return {getName: function () {return name; }, setName: function (newName) {name = newName; }}} (); console.log (person.name); // การเข้าถึงโดยตรงผลลัพธ์คือ console.log ที่ไม่ได้กำหนด (person.getName ()); // persons.setName ("jozo"); console.log (person.getName ()); // Jozo3. ใช้วัตถุที่มุ่งเน้น
ด้วยวิธีนี้วัตถุที่แตกต่างกัน (อินสแตนซ์ของคลาส) มีสมาชิกอิสระและรัฐและไม่รบกวนกันและกัน แม้ว่าจะไม่มีกลไกเช่นคลาสใน JavaScript โดยใช้การปิดเราสามารถจำลองกลไกดังกล่าวได้ มาพูดถึงตัวอย่างข้างต้น:
ฟังก์ชันบุคคล () {var name = "default"; return {getName: function () {return name; }, setName: function (newName) {name = newName; - var person1 = person (); พิมพ์ (person1.getName ()); john.setName ("person1"); พิมพ์ (person1.getName ()); // person1 var person2 = person (); พิมพ์ (person2.getName ()); jack.setName ("erson2"); พิมพ์ (erson2.getName ()); // person2สองกรณีของบุคคล Person1 และ person2 อย่าเข้าไปยุ่งกัน! เพราะทั้งสองอินสแตนซ์มีการเข้าถึงสมาชิกชื่ออิสระ
5. การรั่วไหลของหน่วยความจำและการแก้ปัญหา
กลไกการรีไซเคิลขยะ
เมื่อพูดถึงการจัดการหน่วยความจำมันแยกออกจากกลไกการรวบรวมขยะใน JS โดยธรรมชาติ มีสองกลยุทธ์ในการตระหนักถึงการเก็บขยะ: ทำเครื่องหมายการกวาดล้างและการนับอ้างอิง ;
การกำจัดมาร์ค: เมื่อตัวสะสมขยะทำงานมันจะทำเครื่องหมายตัวแปรทั้งหมดที่เก็บไว้ในหน่วยความจำ จากนั้นมันจะลบแท็กของตัวแปรในสภาพแวดล้อมและแท็กของตัวแปรที่อ้างอิงโดยตัวแปรในสภาพแวดล้อม หลังจากนั้นถ้าตัวแปรถูกทำเครื่องหมายอีกครั้งก็หมายความว่าตัวแปรพร้อมที่จะลบ จนถึงปี 2008 เช่น Firefox, Opera, Chrome และ JavaScript ของ Safari ได้ใช้วิธีนี้ทั้งหมด
จำนวนการอ้างอิง: ติดตามจำนวนครั้งที่แต่ละค่ามีการอ้างอิง เมื่อมีการประกาศตัวแปรและค่าของประเภทการอ้างอิงถูกกำหนดให้กับตัวแปรจำนวนครั้งที่ค่านี้จะถูกอ้างอิงคือ 1 ถ้าค่านี้ถูกกำหนดให้กับตัวแปรอื่นจำนวนครั้งที่การอ้างอิงเพิ่มขึ้น 1 ในทางตรงกันข้ามถ้าตัวแปรเบี่ยงเบนจากการอ้างอิงของค่า
ปัญหาใหญ่เกี่ยวกับวิธีนี้คือการอ้างอิงแบบวงกลมนั่นคือ Object A มีตัวชี้ไปยัง B และ Object B ยังมีการอ้างอิงถึง A ซึ่งอาจทำให้หน่วยความจำจำนวนมากถูกรีไซเคิล (หน่วยความจำรั่ว) เนื่องจากการอ้างอิงของพวกเขาไม่สามารถเป็น 0 ในรุ่นแรก (เช่น 4-IE6) หนึ่งในเหตุผลที่การปิดทำให้เกิดการรั่วไหลของหน่วยความจำเป็นข้อบกพร่องของอัลกอริทึมนี้
เรารู้ว่าวัตถุบางอย่างใน IE ไม่ใช่วัตถุ JavaScript ดั้งเดิม ตัวอย่างเช่นวัตถุใน BOM และ DOM ถูกนำไปใช้ในรูปแบบของวัตถุ COM และกลไกการรวบรวมขยะของวัตถุ COM ใช้การนับการอ้างอิง ดังนั้นแม้ว่าเอ็นจิ้น JavaScript ของ IE ใช้กลยุทธ์การล้างแท็กการเข้าถึงวัตถุ COM ยังคงขึ้นอยู่กับการนับการอ้างอิงตราบใดที่วัตถุ COM ได้รับการออกแบบใน IE แต่จะมีปัญหาของการอ้างอิงแบบวงกลม!
ใช้เกาลัด:
window.onload = function () {var el = document.getElementById ("id"); el.onclick = function () {alert (el.id);}}ทำไมรหัสนี้ทำให้หน่วยความจำรั่ว?
el.onclick = function () {alert (el.id);};เมื่อดำเนินการรหัสนี้วัตถุฟังก์ชันที่ไม่ระบุชื่อจะถูกกำหนดให้กับแอตทริบิวต์ onClick ของ EL; จากนั้นฟังก์ชั่นที่ไม่ระบุชื่อหมายถึงวัตถุ EL ภายในและมีการอ้างอิงแบบวงกลมดังนั้นจึงไม่สามารถรีไซเคิลได้
สารละลาย:
window.onload = function () {var el = document.getElementById ("id"); var id = el.id; // unreference el.onclick = function () {Alert (id); } el = null; // ล้างวัตถุที่ใช้งานอยู่ในฟังก์ชั่นภายนอกที่อ้างอิงโดยการปิด}ข้างต้นเป็นบทสรุปของความรู้ที่เกี่ยวข้องเกี่ยวกับ JS Close Scope Chain Chain Collection Memory การรั่วไหลของการรั่วไหลของคุณโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน!