Dans les événements DOM du navigateur, certains événements seront déclenchés en continu avec les opérations de l'utilisateur. Par exemple : redimensionnez la fenêtre du navigateur (resize), faites défiler la page du navigateur (scroll) et déplacez la souris (mousemove). C'est-à-dire que lorsque l'utilisateur déclenche ces opérations du navigateur, si la méthode de traitement des événements correspondante est liée au script, cette méthode sera déclenchée en continu.
Ce n'est pas ce que nous voulons, car parfois si la méthode de traitement des événements est relativement volumineuse, le fonctionnement du DOM est compliqué et de tels événements sont déclenchés en continu, cela entraînera des pertes de performances et réduira l'expérience utilisateur (réponse de l'interface utilisateur est lent, le navigateur se bloque, etc.). De manière générale, nous ajouterons une logique d’exécution différée à l’événement correspondant.
Habituellement, nous utilisons le code suivant pour implémenter cette fonction :
var COUNT = 0; function testFn() { console.log(COUNT++); } // 浏览器resize的时候 // 1. 清除之前的计时器 // 2. 添加一个计时器让真正的函数testFn延后100毫秒触发 window.onresize = function () { var timer = null; clearTimeout(timer); timer = setTimeout(function() { testFn(); }, 100); };
Les étudiants attentifs découvriront que le code ci-dessus est en fait faux. C'est un problème que les novices se poseront : La valeur de retour. de la fonction setTimeout doit être stockée dans une variable globale relative, sinon un nouveau timer sera généré à chaque redimensionnement, ce qui n'obtiendra pas l'effet que nous avons envoyé
Nous avons donc modifié le code :
var timer = null; window.onresize = function () { clearTimeout(timer); timer = setTimeout(function() { testFn(); }, 100); };
À l'heure actuelle, le code est normal, mais il y a un nouveau problème : une minuterie variable globale est générée. C'est ce que nous ne voulons pas voir. Si cette page a d'autres fonctions également appelées minuterie, différents codes entreront en conflit auparavant. Afin de résoudre ce problème, nous devons utiliser une fonctionnalité du langage JavaScript : fermeture fermetures. Les lecteurs ayant des connaissances pertinentes peuvent se rendre sur MDN pour en savoir plus. Le code modifié est le suivant :
/** * 函数节流方法 * @param Function fn 延时调用函数 * @param Number delay 延迟多长时间 * @return Function 延迟执行的方法 */ var throttle = function (fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } }; window.onresize = throttle(testFn, 200, 1000);
Nous utilisons une fonction de fermeture (throttle throttling) pour mettre la minuterie à l'intérieur et renvoyer la fonction de traitement du retard, de sorte que la variable timer est invisible pour le monde extérieur, mais la variable timer est également accessible lorsque la fonction de retard interne est déclenchée.
Bien sûr, cette façon d'écrire est difficile à comprendre pour les novices. On peut changer la façon d'écrire pour la comprendre :
var throttle = function (fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } }; var f = throttle(testFn, 200); window.onresize = function () { f(); };
La principale chose à comprendre ici est : <🎜. >Throttle est La fonction renvoyée après l'appel est la vraie fonction qui doit être appelée lorsque onresize est déclenché
Maintenant, il semble que cette méthode soit proche de la perfection, mais ce n'est pas le cas en réalité utiliser. Par exemple :Si l'utilisateurDonc nous un autre une fonction doit être ajoutée : lorsque l'utilisateur déclenche le redimensionnement, il doitredimensionne continuellement la fenêtre du navigateur, alors la fonction de traitement différé ne sera pas exécutée une seule fois
se déclencher au moins une fois dans un certain laps de temps Puisque c'est dans un certain laps de temps, alors cette condition de jugement peut prendre l'heure actuelle. millisecondes, à chaque fois Cet appel de fonction soustrait l'heure actuelle de l'heure du dernier appel, puis détermine si la différence est supérieure à une certaine période de temps , elle sera déclenchée directement. Sinon, la logique de retard de. le délai d'attente sera toujours utilisé.
Ce qu'il faut souligner dans le code suivant est :/** * 函数节流方法 * @param Function fn 延时调用函数 * @param Number delay 延迟多长时间 * @param Number atleast 至少多长时间触发一次 * @return Function 延迟执行的方法 */ var throttle = function (fn, delay, atleast) { var timer = null; var previous = null; return function () { var now = +new Date(); if ( !previous ) previous = now; if ( now - previous > atleast ) { fn(); // 重置上一次开始时间为本次结束时间 previous = now; } else { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } } };
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>throttle</title> </head> <body> <p style="height:5000px"> <p id="demo" style="position:fixed;"></p> </p> <script> var COUNT = 0, demo = document.getElementById('demo'); function testFn() {demo.innerHTML += 'testFN 被调用了 ' + ++COUNT + '次<br>';} var throttle = function (fn, delay, atleast) { var timer = null; var previous = null; return function () { var now = +new Date(); if ( !previous ) previous = now; if ( atleast && now - previous > atleast ) { fn(); // 重置上一次开始时间为本次结束时间 previous = now; clearTimeout(timer); } else { clearTimeout(timer); timer = setTimeout(function() { fn(); previous = null; }, delay); } } }; window.onscroll = throttle(testFn, 200); // window.onscroll = throttle(testFn, 500, 1000); </script> </body> </html>
// case 1 window.onscroll = throttle(testFn, 200); // case 2 window.onscroll = throttle(testFn, 200, 500);
cas 1 se comporte comme suit : testFN ne sera pas appelé pendant le processus de défilement de la page (ne peut pas être arrêté), jusqu'à ce qu'il soit arrêté une fois, c'est-à-dire que le dernier setTimeout dans la manette est exécuté. la figure (voir le gif original) :
cas 2 se comporte comme suit : pendant le processus de défilement de la page (ne peut pas être arrêté), testFN sera exécuté avec un délai de 500 ms pour la première fois (à partir d'au moins la logique de retard), puis exécuté au moins toutes les 500 ms. L'effet est comme indiqué sur la figure
Donc. Jusqu'à présent, l'effet que nous souhaitons obtenir est pratiquement achevé. Les lecteurs peuvent découvrir par eux-mêmes certaines optimisations auxiliaires ultérieures, telles que : la fonction vers laquelle pointe, le stockage de la valeur de retour, etc. Citation Ce qui précède est une explication détaillée de la fonction de limitation JavaScript Throttle. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !