記事ディレクトリ
1.手ぶれ補正とは
イベントレスポンス関数は一定時間後に実行されますが、一定期間内に再度呼び出された場合には実行時間は再計算されます。この関数を実行する前に、指定された時間待機する必要があります。
2. 応用シナリオ
1. onscroll、onresize、oninput、touchmove などの高頻度イベント監視コールバック。
2. ユーザー名、携帯電話番号、電子メール入力確認入力ボックスのオートコンプリートイベント、検索ボックスの入力検索(ユーザーが最後の入力を完了した後にのみデータが要求されます)。
3. ブラウザウィンドウのサイズを変更した後、調整が完了するのを待って、サイズ変更機能を実行します。
3. 実施原則
一般に、関数スロットルと関数アンチシェイクの原理は非常に単純で、SetTimeout を使用して実行する関数を保存し、clearTimeout を使用して適切なタイミングで実行する関数を簡単にクリアできます。クロージャを使用して形成された高度な関数を使用します。
以下の簡単な例を参照してください案例
。
緑色のコンテナ内のマウス移動イベントを監視します。マウスが動き続ける場合は何もせず、500 ミリ秒停止した後に印刷します。防抖触发了
コードは以下のように表示されます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.item {
box-sizing: border-box;
width: 300px;
height: 300px;
padding: 20px;
margin: 10px;
border: 8px solid red;
background-color: yellowgreen;
}
</style>
</head>
<body>
<div class="container">
<div class="item" id="item">
</div>
</div>
<script>
let item = document.getElementById("item");
item.addEventListener("mousemove", debounce(foo));
// 基本的防抖函数
function debounce(foo, delay=500) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer); // 触发了相同事件,取消当前计时,重新开始计时
}
timer = setTimeout(foo, delay); // 第一次执行,开始一个计时器
};
}
// 执行函数
function foo() {
console.log("防抖触发了");
}
</script>
</body>
</html>
手ぶれ補正機能基本实现
:
function debounce(foo, delay = 500) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer); // 触发了相同事件,取消当前计时,重新开始计时
}
timer = setTimeout(foo, delay); // 第一次执行,开始一个计时器
};
}
上記は基本的な手ぶれ補正機能を実装するもので、数行の単純なコードのように見えます。詳細は次のとおりです。
1. 最初の質問: なぜクロージャが使用されるのか (つまり、なぜタイマーが外部で定義されているのか)
まずクロージャとは何かを確認してください。
function fun() {
var str = 0;
function sum() {
//此处就形成了闭包
return ++str; //这里使用了外部函数内的变量,此变量会一直被保存下来
}
return sum;
}
var count = fun();
console.log(count()); // 1
console.log(count()); // 2
console.log(count()); // 3
クロージャコンセプト:
闭包(closure)
別の関数のスコープ内の変数にアクセスできる関数を指します。簡単に理解すると、スコープは別の関数内のローカル変数にアクセスできるということです。
これは、js の独自のスコープ チェーン検索によるもので、グローバル変数は関数内で上方向に直接読み込むことができ、見つかるまでレベルごとに検索されます。(このプロセスは元に戻せません)
クロージャが使用され、外部関数によって定義された変数にアクセスすると、変数は永久に保持されます (過度に使用するとメモリ リークが発生します)。
また、クロージャを使用すると、グローバルに使用するために内部定義された変数を返すこともできます。
クロージャを理解した後、上記の質問に戻ってください。なぜクロージャを使用するのか、実際には、タイマーのタイマー ID が外部で定義されているのです。
なぜなら:
関数timer
内に の定義が置かれている場合、この条件は常に true になるわけではありません。これにより、多数のタイマーが開始されます。この関数が実行されるたびに、新しい setTimeout タイマーが追加され、非常に多くのタイマーが実行されます。手ぶれ補正の効果もありますが、js の実行負荷も増加します。return function
if (timer)
定義を外部に置くことtimer
で、同時に開始されるタイマーは 1 つだけになります。タイマーが複数回来た場合は前のタイマーはクリアされ、新しいタイマーが再開されます。新しいタイマー ID は次のように保存されています。複数のsetTimeoutが生成されtimer
、手ぶれ補正効果が得られます。
この時点で、クロージャを使用する理由の導入は完了しましたが、手ぶれ補正機能でタイマーを使用するため、新たな問題、つまりポインティングの問題が発生します。
2. 2番目の質問: 手ぶれ補正機能におけるこれの指摘の問題:
下記参照:
最初の this が Window を指しており、2 番目の this が item ノード要素を指していることが印刷によってわかります。
3 番目のタイマーのこれもウィンドウを指す理由については、次のようになります。
なぜなら:
JSのタイマーメソッドはWindow配下に定義されていますが、アロー関数を使わずにthisのポイントを能動的に変更する場合、
setIntervalやsetTimeoutのコールバック関数内のthisのポイントはWindowになります。
コンテキストが手ぶれ補正機能の呼び出しに関与している場合は、タイマー内でのこのコンテキストの指定を考慮する必要があります。この方向を変更するには apply を使用する必要があります。
以前の基本的な手ぶれ補正機能は、次のコードに最適化できます。
完整的防抖函数
:
// 防抖
function debounce(foo, delay = 500) {
let timer = null;
return function () {
let _this = this; // 此处保留this
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function(){
foo.apply(_this) // 改变this指向
}, delay);
};
}
4. スロットル機能
スロットル機能の詳細は手ぶれ補正と同じなので、ここでは詳しく説明しません。主に実装方法が少し異なるためです。
コンセプト:
节流(throttel)
イベントがどれだけ頻繁にトリガーされても、対象の関数は設定された間隔で 1 回だけ実行されます。簡単に言うと、時間単位ごとに 1 回だけ実行されます。
アプリケーション シナリオ:スクロール バーがスクロールすると、関数処理はスロットルによって応答数を適切に減らすことができます。
基本的な実装: 基本的にほとんどのニーズを満たすことができます
function throttle(foo, delay = 500) {
let timer = null;
return function () {
if (timer) {
return false; //定时器还在,等冷却时间
}
timer = setTimeout(function(){
foo();
clearTimeout(timer); //函数执行完后清除定时器,表示冷却完成
timer = null; //此处一点要清空
}, delay);
};
}
apply を使用した実装:
function throttle(foo, delay = 500) {
let timer = null;
return function () {
let _this = this;
if (timer) {
return false; //定时器还在,等冷却时间
}
timer = setTimeout(function(){
foo.apply(_this);
clearTimeout(timer); //函数执行完后清除定时器,表示冷却完成
timer = null; //此处一点要清空
}, delay);
};
}
この手ぶれ補正スロットリング機能はインターネット上に数多くのバージョンが公開されていますが、なぜこのように記述されているかを知ることが重要であり、機能を実現できれば十分であり、それほど複雑にする必要はありません。