ก่อนอื่นเรามาเข้าใจกันว่าคันเร่งคืออะไร
1. คำจำกัดความ
หากคุณกระชับก๊อกน้ำจนกว่าน้ำจะไหลออกมาในรูปแบบของหยดน้ำคุณจะพบว่าทุกครั้งในขณะที่น้ำหยดจะไหลออกมา
กล่าวคือวงจรการดำเนินการถูกตั้งค่าล่วงหน้าและเมื่อเวลาที่เรียกว่าการกระทำมากกว่าหรือเท่ากับรอบการดำเนินการการดำเนินการจะถูกดำเนินการและจากนั้นรอบใหม่จะถูกป้อน
คำจำกัดความอินเตอร์เฟส:
* เมื่อการควบคุมความถี่ส่งคืนฟังก์ชั่นเรียกอย่างต่อเนื่องความถี่การดำเนินการของการดำเนินการจะถูก จำกัด เวลา/ การหน่วงเวลา* @param การหน่วงเวลา {number} เวลาหน่วง, หน่วยมิลลิวินาที* @param การกระทำ {ฟังก์ชั่น} ร้องขอฟังก์ชั่นที่เกี่ยวข้องและฟังก์ชั่นที่ต้องเรียกใช้ในแอปพลิเคชันจริง2. การใช้งานอย่างง่าย
var throttle = function (การหน่วงเวลา, การกระทำ) {var last = 0 return function () {var curr = +วันที่ใหม่ () ถ้า (curr - สุดท้าย> ล่าช้า) {action.apply (นี้, อาร์กิวเมนต์) สุดท้าย = curr}}}ให้ฉันอธิบายฟังก์ชั่นการควบคุมปริมาณนี้อย่างรอบคอบด้านล่าง
ในเหตุการณ์ DOM เบราว์เซอร์เหตุการณ์บางอย่างจะถูกเรียกอย่างต่อเนื่องกับการดำเนินการของผู้ใช้ ตัวอย่างเช่น: ปรับขนาดหน้าต่างเบราว์เซอร์เลื่อนหน้าเบราว์เซอร์และ Mousemove กล่าวคือเมื่อผู้ใช้ทริกเกอร์การทำงานของเบราว์เซอร์เหล่านี้หากวิธีการจัดการเหตุการณ์ที่สอดคล้องกันถูกผูกไว้กับสคริปต์วิธีนี้จะถูกเรียกอย่างต่อเนื่อง
นี่ไม่ใช่สิ่งที่เราต้องการเพราะบางครั้งหากวิธีการจัดการเหตุการณ์มีขนาดค่อนข้างใหญ่การดำเนินงาน DOM เช่นซับซ้อนและการกระตุ้นเหตุการณ์ดังกล่าวอย่างต่อเนื่องจะทำให้เกิดการสูญเสียประสิทธิภาพส่งผลให้ประสบการณ์การใช้งานลดลง (การตอบสนอง UI ช้าเบราว์เซอร์ติดอยู่ ฯลฯ ) ดังนั้นโดยปกติแล้วเราจะเพิ่มตรรกะให้กับเหตุการณ์ที่เกี่ยวข้องเพื่อชะลอการดำเนินการ
โดยทั่วไปเราใช้รหัสต่อไปนี้เพื่อใช้ฟังก์ชั่นนี้:
var count = 0; function testfn () {console.log (count ++); } // เมื่อเบราว์เซอร์ปรับขนาด // 1 ล้างตัวจับเวลาก่อนหน้า // 2 เพิ่มตัวจับเวลาเพื่อชะลอฟังก์ชันจริง testfn โดย 100 มิลลิวินาทีเพื่อทริกเกอร์หน้าต่าง onResize = function () {ตัวจับเวลา var = null; ClearTimeout (ตัวจับเวลา); timer = settimeout (function () {testfn ();}, 100);};นักเรียนระวังจะพบว่ารหัสข้างต้นนั้นผิดจริง นี่เป็นปัญหาที่สามเณรจะทำ: ค่าส่งคืนของฟังก์ชั่น settimeout ควรถูกบันทึกไว้ในตัวแปรระดับโลกที่สัมพันธ์กันมิฉะนั้นจะมีการสร้างตัวจับเวลาใหม่ทุกครั้งที่ปรับขนาดขนาดซึ่งจะไม่บรรลุผลที่เราส่ง
ดังนั้นเราจึงแก้ไขรหัส:
var timer = null; window.onResize = function () {clearTimeout (ตัวจับเวลา); timer = settimeout (function () {testfn ();}, 100);};ในเวลานี้รหัสเป็นเรื่องปกติ แต่มีปัญหาใหม่อีกอย่างหนึ่ง - ตัวจับเวลาตัวแปรทั่วโลกถูกสร้างขึ้น นี่คือสิ่งที่เราไม่อยากเห็น หากหน้านี้มีฟังก์ชั่นอื่น ๆ จะเรียกว่าตัวจับเวลา รหัสที่แตกต่างกันจะทำให้เกิดความขัดแย้งมาก่อน เพื่อแก้ปัญหานี้เราจำเป็นต้องใช้คุณสมบัติภาษาของ JavaScript: ปิดการปิด ผู้อ่านสามารถเรียนรู้เกี่ยวกับความรู้ที่เกี่ยวข้องใน MDN รหัสที่แก้ไขมีดังนี้:
/*** ฟังก์ชั่นวิธีการควบคุมปริมาณ* ฟังก์ชั่น @param fn fn การโทรล่าช้าฟังก์ชั่น* @param number delay วิธีการหน่วงเวลา* @return ใช้เวลานานเท่าใดวิธีการทำงานสำหรับการล่าช้าการดำเนินการ*/var throttle = ฟังก์ชั่น (fn, ล่าช้า) {var timer = null; return function () {cleartimeout (ตัวจับเวลา); timer = settimeout (function () {fn ();}, ล่าช้า); }}; window.onresize = เค้น (testfn, 200, 1000);เราใช้ฟังก์ชั่นการปิด (การควบคุมคันเร่ง) เพื่อวางตัวจับเวลาภายในและส่งคืนฟังก์ชั่นการประมวลผลการหน่วงเวลา ด้วยวิธีนี้ตัวแปรตัวจับเวลาจะมองไม่เห็นด้านนอก แต่ตัวแปรตัวจับเวลาสามารถเข้าถึงได้เมื่อฟังก์ชั่นการหน่วงเวลาภายในถูกทริกเกอร์
แน่นอนว่าวิธีการเขียนนี้ไม่ใช่เรื่องง่ายสำหรับสามเณรที่จะเข้าใจ เราสามารถเปลี่ยนวิธีการเขียนเพื่อทำความเข้าใจ:
var throttle = function (fn, delay) {var timer = null; return function () {cleartimeout (ตัวจับเวลา); timer = settimeout (function () {fn ();}, ล่าช้า); - var f = เค้น (testfn, 200); window.onresize = function () {f ();};นี่คือมุมมอง: ฟังก์ชั่นที่ส่งคืนโดยคันเร่งหลังจากถูกเรียกว่าเป็นฟังก์ชั่นจริงที่ต้องเรียกเมื่อมีการเรียกใช้ onresize
ตอนนี้ดูเหมือนว่าวิธีนี้ใกล้เคียงกับความสมบูรณ์แบบ แต่ไม่ใช่กรณีในการใช้งานจริง ตัวอย่างเช่น:
หากผู้ใช้ปรับขนาดขนาดหน้าต่างเบราว์เซอร์อย่างต่อเนื่องฟังก์ชั่นการประมวลผลการหน่วงเวลาจะไม่ถูกดำเนินการครั้งเดียว
ดังนั้นเราจำเป็นต้องเพิ่มฟังก์ชั่นอื่น: เมื่อผู้ใช้ทริกเกอร์ปรับขนาดมันควรจะถูกเรียกอย่างน้อยหนึ่งครั้งภายในระยะเวลาหนึ่ง เนื่องจากภายในระยะเวลาหนึ่งเงื่อนไขการตัดสินนี้อาจใช้เวลามิลลิวินาทีในปัจจุบันและแต่ละฟังก์ชั่นเรียกใช้เวลาปัจจุบันจากเวลาการโทรครั้งสุดท้ายและจากนั้นตัดสินว่าหากความแตกต่างมากกว่าระยะเวลาหนึ่งมันจะถูกส่งโดยตรง
สิ่งที่ต้องชี้ให้เห็นในรหัสต่อไปนี้คือ:
ฟังก์ชั่นของตัวแปรก่อนหน้านั้นคล้ายกับตัวจับเวลา ทั้งสองเป็นตัวระบุที่บันทึกครั้งสุดท้ายและต้องเป็นตัวแปรระดับโลกที่สัมพันธ์กัน
หากกระบวนการลอจิกเป็นไปตามลอจิก ในระยะสั้นมันเป็นเรื่องปัจจุบันเมื่อเทียบกับครั้งต่อไป
/*** ฟังก์ชั่นวิธีการควบคุมปริมาณ* @param ฟังก์ชั่นการโทรล่าช้าฟังก์ชั่น* @param หมายเลขล่าช้าระยะเวลานานเท่าใด* @param หมายเลขอย่างน้อยระยะเวลาที่เรียกใช้* @return function วิธีการสำหรับการล่าช้าการดำเนินการ*/var throttle = ฟังก์ชั่น (fn, ล่าช้าอย่างน้อย) {var timer = null; var previous = null; return function () {var now = +date ใหม่ (); ถ้า (ก่อนหน้า) ก่อนหน้า = ตอนนี้; if (ตอนนี้ - ก่อนหน้า> อย่างน้อย) {fn (); // รีเซ็ตเวลาเริ่มต้นสุดท้ายเป็นเวลาสิ้นสุดของเวลานี้ก่อนหน้า = ตอนนี้; } else {cleartimeout (ตัวจับเวลา); timer = settimeout (function () {fn ();}, ล่าช้า); -ฝึกฝน:
เราจำลองฉากการควบคุมปริมาณเมื่อหน้าต่างเลื่อนนั่นคือเมื่อผู้ใช้เลื่อนหน้าลงเราจำเป็นต้องควบคุมปริมาณวิธีการบางอย่างเช่น: การคำนวณตำแหน่ง DOM ฯลฯ ซึ่งต้องใช้การทำงานอย่างต่อเนื่องขององค์ประกอบ DOM
รหัสที่สมบูรณ์มีดังนี้:
<! doctype html> <html lang = "en"> <head> <meta charset = "utf-8"> <title> throttle </title> </head> <body> <div style = "ความสูง: 5000px"> Document.getElementById ('Demo'); function testfn () {demo.innerhtml += 'testfn ถูกเรียกว่า' +++ count +'เวลา <br>';} var throttle = ฟังก์ชั่น (fn, ล่าช้าอย่างน้อย) {var timer = null; var previous = null; return function () {var now = +date ใหม่ (); ถ้า (ก่อนหน้า) ก่อนหน้า = ตอนนี้; ถ้า (อย่างน้อย && ตอนนี้ - ก่อนหน้า> อย่างน้อย) {fn (); // รีเซ็ตเวลาเริ่มต้นสุดท้ายเป็นเวลาสิ้นสุดของเวลานี้ก่อนหน้า = ตอนนี้; ClearTimeout (ตัวจับเวลา); } else {cleartimeout (ตัวจับเวลา); timer = settimeout (function () {fn (); previous = null;}, ล่าช้า); - window.onscroll = เค้น (testfn, 200); // window.onscroll = เค้น (testfn, 500, 1000); </script> </body> </html>เราใช้สองกรณีเพื่อทดสอบเอฟเฟกต์คือการเพิ่มอย่างน้อยทริกเกอร์อย่างน้อยก็อย่างน้อยและไม่เพิ่ม:
// กรณี 1window.onscroll = เค้น (testfn, 200); // case 2window.onscroll = เค้น (testfn, 200, 500);
กรณีที่ 1 ปรากฏเป็น: testFN จะไม่ถูกเรียกในระหว่างกระบวนการเลื่อนหน้า (ไม่สามารถหยุดได้) และจะถูกเรียกอีกครั้งจนกว่าจะหยุดซึ่งหมายความว่าการตั้งถิ่นฐานครั้งสุดท้ายในคันเร่งจะถูกดำเนินการเอฟเฟกต์ดังแสดงในรูป:
กรณีที่ 2 ปรากฏเป็น: ในระหว่างกระบวนการเลื่อนหน้า (ไม่สามารถหยุดได้) TestFN จะล่าช้า 500ms เป็นครั้งแรก (จากตรรกะการหน่วงเวลาอย่างน้อย) จากนั้นดำเนินการอย่างน้อยทุก 500ms เอฟเฟกต์ดังแสดงในรูป
ดังที่แสดงไว้ข้างต้นผลกระทบที่เราต้องการบรรลุได้รับการแนะนำและมีตัวอย่าง ฉันหวังว่ามันจะเป็นประโยชน์กับเพื่อนที่ต้องการ ผู้อ่านสามารถคิดเกี่ยวกับการเพิ่มประสิทธิภาพเสริมที่ตามมาด้วยตัวเองเช่น: ฟังก์ชั่นการชี้นี้การประหยัดค่าคืน ฯลฯ อย่างไรก็ตามมันรู้สึกดีที่จะเข้าใจกระบวนการนี้อย่างระมัดระวัง!