มาพูดถึงต้นกำเนิดของการปิดกัน
ฟังก์ชั่น a () {var i = 0; function b () {console.log (i);} return b;} var c = a (); c ();โดยทั่วไปเมื่อมีฟังก์ชั่นที่ไม่ระบุชื่อภายในฟังก์ชั่นใช้ตัวแปรของตัวเองและฟังก์ชั่นที่ไม่ระบุชื่อจะถูกส่งคืนการปิดจะถูกสร้างขึ้นเช่นรหัสด้านบน
ในเวลานี้แม้ว่าการสิ้นสุดของการโทรจะถูกทำลายฉันจะมีอยู่และจะไม่หายไป เมื่อ A ถูกกำหนดไว้ล่าม JS จะตั้งค่าขอบเขตห่วงโซ่ของฟังก์ชั่น A ให้กับสภาพแวดล้อมที่กำหนดไว้ เมื่อ A ถูกดำเนินการ A จะป้อนสภาพแวดล้อมการดำเนินการที่สอดคล้องกัน หลังจากสร้างสภาพแวดล้อมการดำเนินการแล้วจะมีแอตทริบิวต์ขอบเขตขอบเขตแล้วสร้างวัตถุที่ใช้งานอยู่แล้วตั้งค่าไว้ที่ด้านบนของโซ่ขอบเขต
ตอนนี้ห่วงโซ่ขอบเขตของ A มีวัตถุที่ใช้งานอยู่ของ A และหน้าต่าง
จากนั้นเพิ่มแอตทริบิวต์อาร์กิวเมนต์ลงในวัตถุที่ใช้งานอยู่
ในเวลานี้การอ้างอิงถึงฟังก์ชั่นการส่งคืน B ของ A ถูกมอบให้กับ c ห่วงโซ่ขอบเขตของ B มีการอ้างอิงวัตถุที่ใช้งานอยู่ของ A ดังนั้น C สามารถเข้าถึงวัตถุที่ใช้งานอยู่ของ A ในเวลานี้จะไม่เป็น GC หลังจากกลับมา
ข้างต้นเป็นการแนะนำสั้น ๆ เกี่ยวกับการปิด ถ้าคุณพูดมากเกินไปมันจะง่ายต่อการเดินทาง มาจบที่นี่สั้น ๆ แล้วเข้าสู่ฉากจริงเพื่ออธิบาย
ฉากจริง
ความสงสัยของเพื่อนร่วมงาน
เพื่อนร่วมงานของฉันขอให้ฉันอ่านรหัส:
var user = function (opts) {var scope = this; สำหรับ (var k ใน opts) {scope ['get' + k] = function () {return opts [k];}; ขอบเขต ['set' + k] = function (v) {return opts [k] = v;};รหัสนั้นง่ายมากและฉันหวังว่าจะสร้างวิธีการรับ/ชุดสำหรับวัตถุที่เข้ามา แต่เขาพบปัญหาการปิดที่นี่:
เหตุผลสำหรับปัญหานี้คือ K ที่ใช้ภายในในค่าผลตอบแทนคือ "อายุ" เสมอ k นี้เกิดจากวัตถุที่ใช้งานอยู่ที่ใช้ร่วมกันโดยฟังก์ชั่น getXXX ดังนั้นจึงค่อนข้างง่ายที่จะแก้ไขที่นี่
var user = function (opts) {var scope = this; สำหรับ (var k ใน opts) {(ฟังก์ชั่น (k) {scope ['get' + k] = function () {return opts [k];}; scope ['set' + k] = function (v) {return opts [k] = v;};}; 11});สร้างฟังก์ชั่นการดำเนินการทันทีภายใน For Loop และ Pass K ในเวลานี้ฟังก์ชั่น GetXXX แบ่งปัน "K" ของฟังก์ชั่นที่ไม่ระบุชื่อแต่ละฟังก์ชัน
สร้าง ID ที่ไม่ซ้ำกัน
การสร้าง ID ที่ไม่ซ้ำกันเป็นวิธีคลาสสิกในการใช้การปิด
ฟังก์ชั่น getUuid () {var id = 0; return function () {return ++ id;}} var uuid = getUuid ();รหัสนี้มีความหมายมาก เราดำเนินการ uuid () อย่างต่อเนื่องในเบราว์เซอร์และจะได้รับค่าที่แตกต่างกัน แต่ถ้าเราใช้ getuuid () () ค่าจะยังคงเหมือนเดิมทุกครั้ง
เหตุผลสำหรับปัญหานี้คือเรากำหนดผลลัพธ์หลังจากการดำเนินการ getuuid ให้กับ UUID ในเวลานี้ UUID บันทึกการอ้างอิงถึงฟังก์ชั่นที่ไม่ระบุชื่อและฟังก์ชั่นที่ไม่ระบุชื่อจะบันทึกวัตถุที่ใช้งานอยู่ของ getuuid ดังนั้น ID จึงไม่ถูกทำลาย
หากคุณเรียกมันโดยตรงวัตถุที่ใช้งานจะถูกสร้างใหม่ทุกครั้งดังนั้น ID จึงไม่สามารถบันทึกได้
รหัสที่น่าสนใจ
util.tryUrl = function (url) {var iframe = document.createElement ('iframe'); iframe.height = 1; iframe.width = 1; iframe.frameBorder = 0; iframe.style.position = 'Absolute'; '-9999px'; document.body.appendchild (iframe); util.tryurl = function (url) {iframe.src = url;}; u.tryurl (url);};รหัสนี้น่าสนใจมาก เมื่อเราเรียกมันว่าเป็นครั้งแรกเราจะสร้างวัตถุ iframe และวัตถุ iframe จะมีอยู่เมื่อครั้งที่สอง เราจะทำให้รหัสง่ายขึ้นที่นี่
var getUuid = function () {var i = 0; getUuid = function () {return i ++;}; return getUuid ();};หลังจากการปรับนี้ไม่มีฟังก์ชั่นการส่งคืน แต่เรายังคงปิดการปิด
การมอบหมายงานและการปิดงาน
เราทุกคนรู้ว่า jQuery on ased is use lexitation แต่มันก็ยังต้องใช้ความพยายามอย่างมากที่จะเข้าใจอย่างแท้จริง
การปิดเป็นรากฐานที่สำคัญของการดำเนินงานของคณะผู้แทนเหตุการณ์ สุดท้ายให้จบการศึกษาปิดการศึกษาในวันนี้ด้วยการมอบหมายงาน
เพิ่มไปยังหน้าของเราไปยังโครงสร้าง DOM ต่อไปนี้
<อินพุต id = "อินพุต" value = "อินพุต" type = "ปุ่ม"/> <div id = "div"> ฉันเป็น div </div> <span id = "span"> ฉันขยาย </span> <div id = "wrapper"> <อินพุต id = "inner" ค่า = "
หากเราใช้ Zepto เราใช้วิธีการต่อไปนี้เพื่อผูกเหตุการณ์
$ .on ('คลิก', 'ตัวเลือก', fn)เราไม่มี Zepto ที่นี่เพียงแค่ใช้มันด้วยตัวเราเอง
หลักการของการมอบหมายงาน
ก่อนอื่นรากฐานที่สำคัญของการใช้งานตัวแทนเหตุการณ์คือฟองสบู่ ในที่สุดการคลิกในหน้าเว็บก็จะเป็นฟองไปยังองค์ประกอบหลักดังนั้นเราจึงสามารถจับเหตุการณ์ทั้งหมดได้ที่เอกสาร
หลังจากรู้ปัญหานี้เราสามารถใช้วิธีการผูกมัดเหตุการณ์ที่ได้รับมอบหมายง่ายๆด้วยตนเอง:
ฟังก์ชั่นมอบหมาย (ตัวเลือก, ประเภท, fn) {document.addeventListener (พิมพ์, fn, false);} มอบหมาย ('#อินพุต', 'คลิก', ฟังก์ชั่น () {console.log ('ttt');});รหัสนี้เป็นการใช้งานที่ง่ายที่สุด ก่อนอื่นเราจะดำเนินการคลิกเหตุการณ์ไม่ว่าเราจะคลิกที่ใด แน่นอนว่านี่ไม่ใช่สถานการณ์ที่เราต้องการเห็นดังนั้นเราจึงทำการประมวลผลเพื่อเรียกเหตุการณ์ที่ควรมีเมื่อคลิก
นี่คือคำถามที่จริงจังอีกสองสามข้อ:
①เนื่องจากเหตุการณ์ของเราถูกผูกไว้กับเอกสารฉันจะรู้ได้อย่างไรว่าฉันคลิกองค์ประกอบใดบ้าง?
②แม้ว่าฉันจะได้รับองค์ประกอบการคลิกปัจจุบันตาม E.Target ฉันจะรู้ได้อย่างไรว่าองค์ประกอบใดมีเหตุการณ์
③แม้ว่าฉันสามารถกำหนดองค์ประกอบของการคลิกปัจจุบันที่จำเป็นในการดำเนินการเหตุการณ์ตามตัวเลือกฉันจะค้นหาเหตุการณ์ที่เป็นได้อย่างไร
หากปัญหาข้างต้นสามารถแก้ไขได้กระบวนการที่ตามมาของเราจะค่อนข้างง่าย
ตรวจสอบว่าองค์ประกอบการคลิกทริกเกอร์เหตุการณ์หรือไม่
ก่อนอื่นเมื่อเราคลิกเราสามารถใช้ E.Target เพื่อรับองค์ประกอบการคลิกปัจจุบันจากนั้นค้นหา DOM พาเรนต์ของมันตามตัวเลือก หากพบเหตุการณ์ควรถูกเรียกใช้
เนื่องจากสิ่งเหล่านี้สามารถตัดสินใจได้เมื่อถูกทริกเกอร์เราจึงต้องเขียนฟังก์ชั่นการโทรกลับ FN ใหม่ดังนั้นหลังจากการดำเนินการอย่างง่าย:
var arr = []; var slice = arr.slice; var extend = function (src, obj) {var o = {}; สำหรับ (var k ใน src) {o [k] = src [k];} สำหรับ (var k ใน obj) {o [k] = obj [k]; ฟังก์ชั่น (e) {// องค์ประกอบที่พบโดยตัวเลือก var selectorel = document.querySelector (ตัวเลือก); // องค์ประกอบคลิกปัจจุบัน var el = e.target; // พิจารณาว่าองค์ประกอบที่พบโดยตัวเลือกมีองค์ประกอบคลิกปัจจุบันหรือไม่ หากรวมอยู่ในเหตุการณ์ควรถูกเรียกใช้/********************************************************************************* [evt] .concat (slice.call (อาร์กิวเมนต์, 1)); callback.apply (selectorel, evt); var s = '';} var s = '';}; document.addeventListener (ประเภท, handler, false);};เพื่อให้เราสามารถขยายสาย:
มอบหมาย ('#อินพุต', 'คลิก', function () {console.log ('อินพุต');}); มอบหมาย ('#div', 'คลิก', ฟังก์ชั่น () {console.log ('div');}); delegate ('#wrapper', 'คลิก', ' () {console.log ('span');}); delegate ('#inner', 'คลิก', function () {console.log ('inner');});ลองวิเคราะห์โปรแกรมทั้งหมดสั้น ๆ
①เราเรียกผู้แทนเพื่อเพิ่มเหตุการณ์ในร่างกาย
②เมื่อมีผลผูกพันเราเขียนการโทรกลับในนั้น
③เมื่อคลิก (จำนวนครั้งที่เหตุการณ์ที่มีผลผูกพันจะทำให้เกิดการคลิก) องค์ประกอบปัจจุบันจะได้รับเพื่อตรวจสอบว่าองค์ประกอบที่ค้นหาโดยตัวเลือกนั้นมีหรือไม่ หากมีมันอยู่เหตุการณ์จะถูกทริกเกอร์
④เนื่องจากการปิดจะเกิดขึ้นทุกครั้งที่คุณลงทะเบียนที่นี่การโทรกลับที่เข้ามาจะได้รับการดูแลรักษาดังนั้นคุณสามารถค้นหาฟังก์ชั่นการโทรกลับของคุณเองทุกครั้งที่คุณเรียกมันว่า (มีประโยชน์มากสำหรับการทำความเข้าใจการปิดที่นี่)
⑤ในที่สุดเขียนเป้าหมายปัจจุบันของการจัดการเหตุการณ์ใหม่ดังนั้นการมอบหมายงานจะสิ้นสุดลง
PS: ฉันมีปัญหากับการดำเนินการที่นี่เช่นการประมวลผลของเหตุการณ์ แต่เป็นการสาธิตฉันจะไม่สนใจมัน เพื่อนที่สนใจสามารถตรวจสอบการใช้งาน Zepto ด้วยตนเอง
ปัญหาการมอบหมายงาน
การมอบหมายงานสามารถปรับปรุงประสิทธิภาพได้ แต่สิ่งที่น่ารำคาญอีกอย่างหนึ่งก็คือมันไม่มีประโยชน์ที่จะหยุดฟองสบู่
ใช้รหัสด้านบนเป็นตัวอย่าง มีองค์ประกอบภายในและองค์ประกอบห่อหุ้ม พวกเขาห่อหุ้มความสัมพันธ์ของกันและกัน
อย่างไรก็ตามคำสั่งการดำเนินการของมันไม่ได้เป็นคำสั่งของฟองอากาศภายในและภายนอกเนื่องจากเหตุการณ์ทั้งหมดถูกผูกไว้กับเอกสารดังนั้นคำสั่งการดำเนินการที่นี่จะถูกกำหนดโดยคำสั่งการลงทะเบียน
นี่คือคำถาม: วิธีการ "หยุดฟอง"
ดำเนินการเสร็จสมบูรณ์ในด้านใน
E.STOPIMMEDIATEPROPAGATION ()
มันสามารถบรรลุวัตถุประสงค์ แต่ก็ยังต้องการให้องค์ประกอบภายในต้องลงทะเบียนก่อน
นอกจากนี้มีเพียงเหตุการณ์เดียวเท่านั้นที่ผูกพันกับองค์ประกอบที่ซ้อนกันและ E.Target ตัดสินใจว่าเหตุการณ์ใดที่จะดำเนินการ สำหรับรายละเอียดโปรดพิจารณาด้วยตัวคุณเอง
ปัญหาข้างต้นอาจพบได้จริงเมื่อใช้กระดูกสันหลัง