应用防抖节流首先需理解以下知识
js 函数内部 return 一个函数,自动调用 toString 方法
fn():执行函数体 fn,执行后得到其返回值fn:不会执行函数体,而是得到函数体的源码。 JS 中 return 一个函数与直接 return 一个函数变量的区别
函数的节流与防抖
function makeCounter() {var count = 0;function counter() {count = count + 1;return count;}return counter(); // 将嵌套函数返回
}
var doCount = makeCounter();
console.log(doCount, "--doCount1"); // 1 '--doCount1'
console.log(doCount, "--doCount2"); // 1 '--doCount2'
console.log(doCount, "--doCount3"); // 1 '--doCount3'
function makeCounter() {var count = 0;function counter() {count = count + 1;return count;}return counter; // 将嵌套函数返回,但只写函数名称
}
var doCount = makeCounter();
console.log(doCount(), "--doCount1"); // 1 '--doCount1'
console.log(doCount(), "--doCount2"); // 2 '--doCount2'
console.log(doCount(), "--doCount3"); // 3 '--doCount3'
return counter 返回的是整一个 cunter()函数。
因此执行 var doCount = makeCounter()时,doCount 将引用 counter 函数及其中的变量环境。
那么 counter 函数及其中的变量环境,就是闭包了
闭包的形成:内部函数引用了外部函数的数据(这里为 count),
因此在 doCount=makeCounter(),则会把这个 count 保存在 doCount 中,函数执行完,count 并不会被销毁。
注意: 为什么上面这段代码没有直接写的 function doCount(){…} 而是把 function 赋值给了变量 doCount 呢?
我们通常会想当然的认为每次调用 doCount() 都会重走一遍 doCount()中的代码块, 但其实不然。
注意 makeCounter 方法中的 return 不是 1,2,3 这样的数值, 而是一个方法,并且把这个方法赋值给了 doCount 变量。
那么在这个 makeCounter 自运行一遍之后, 其实最后赋值给 doCount 的是 count = count + 1; return count; 这段代码。
所以后面每次调用 doCount() 其实都是在调用 count = count + 1; return count;
闭包会持有父方法的局部变量并且不会随父方法销毁而销毁, 所以这个 count 其实就是来自于第一次 makeCounter 执行时创建的变量
什么是防抖和节流?有什么区别?如何实现?
防抖节流详细用法和区别 - 详解版
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
一个经典的比喻:
| 正常执行 | run | run | run | run | run | run | run | run | — | — |
| 防抖: 非立即执行 | — | — | — | — | — | — | — | run | — | — |
| 防抖: 立即执行 | run | — | — | — | — | — | — | — | — | — |


防抖有两种实现方式:非立即执行版 和 立即执行版。
Function.prototype.apply()
// 1
function debounce(func, delay) {let timeout;return function () {let context = this;let args = arguments;if (timeout) {clearTimeout(timeout);}timeout = setTimeout(() => {func.apply(context, args);}, delay);};
}
// 2
function debounce(func, delay) {let timeout;return () => {if (timeout) {clearTimeout(timeout);}timeout = setTimeout(func, delay);};
}
// 使用
debounce(() => {console.log("run");
}, 500);
【记】Vue 中使用防抖函数所遇见的坑
Vue 中使用时,需要定义 timeout,同时在防抖函数中,this 的指向发生了变化,需要在 return 之前获取 vue 实例。
这个时候,你直接使用,还是不行的,只要 debug 就会发现 debounce 返回的 func 没有进去,需要手动执行一下(添加括号)。
要把 timeout 挂在 this 上,否则不起作用
data() {return {timeout: null // 关键在这里}
}
// 1
methods: {debounce(func, delay) {let context = this; // this指向发生变化,需要提出来let args = arguments;return (function () {if (context.timeout) {clearTimeout(context.timeout);}context.timeout = setTimeout(() => {func.apply(context, args);}, delay);})(); // 注意这里有()},
}
// 2
methods: {debounce(func, delay) {let context = this; // this指向发生变化,需要提出来return (function () {if (context.timeout) {clearTimeout(context.timeout);}context.timeout = setTimeout(() => {func();context.timeout = null; // 必须要清空,否则影响另一事件}, delay);})(); // 注意这里有()},
}
// 使用
debounce(() => {console.log("run");
}, 500);
若要封装成公共方法,把 context 作为参数
export const debounce = (func, context, delay = 500) => {return (function () {if (context.timeout) {clearTimeout(context.timeout);} else {func();}context.timeout = setTimeout(() => {context.timeout = null;}, delay);})();
};
// 选择式 API
// 使用
debounce(() => {console.log("run");},this,500
);
var count = 1;// 非立即执行
export const debounce = (func, context, delay = 500) => {console.log(count, "--count--1");return (function () {console.log(context.timeout, "--context.timeout--1");if (context.timeout) {clearTimeout(context.timeout);console.log(context.timeout, "--context.timeout--2");}context.timeout = setTimeout(func, delay);console.log(context.timeout, "--context.timeout--3");count += 1;console.log(count, "--count--2");})();
};
连续点击 3 次
1 '--count--1'
null '--context.timeout--1'
110 '--context.timeout--3'
2 '--count--2'
2 '--count--1'
110 '--context.timeout--1'
110 '--context.timeout--2'
116 '--context.timeout--3'
3 '--count--2'
3 '--count--1'
116 '--context.timeout--1'
116 '--context.timeout--2'
122 '--context.timeout--3'
4 '--count--2'
--ok
前两次 func 不执行,因为被 clearTimeout 了
// 1
function debounce(func, delay) {let timeout;return function () {let context = this;let args = arguments;if (timeout) {clearTimeout(timeout);}timeout = setTimeout(() => {func.apply(context, args);}, delay);};
}
// 2
function debounce(func, delay) {let timeout;return () => {if (timeout) {clearTimeout(timeout);}timeout = setTimeout(func, delay);};
}
// 使用
debounce(() => {console.log("run");
}, 500);
data() {return {timeout: null}
}
// 1
methods: {debounce(func, delay) {let context = this; // this指向发生变化,需要提出来let args = arguments;return (function () {if (context.timeout) {clearTimeout(context.timeout);} else {func.apply(context, args);}context.timeout = setTimeout(() => {context.timeout = null;}, delay);})(); // 注意这里有()},
}
// 2
methods: {debounce(func, delay) {let context = this; // this指向发生变化,需要提出来return (function () {if (context.timeout) {clearTimeout(context.timeout);} else {func();}context.timeout = setTimeout(() => {context.timeout = null;}, delay);})(); // 注意这里有()},
}
// 使用
debounce(() => {console.log("run");
}, 500);
若要封装成公共方法,把 context 作为参数
export const debounce = (func, context, delay = 500) => {return (function () {if (context.timeout) {clearTimeout(context.timeout);} else {func();}context.timeout = setTimeout(() => {context.timeout = null;}, delay);})();
};
// 选择式 API
// 使用
debounce(() => {console.log("run");},this,500
);
var count = 1;export const debounce = (func, context, delay = 500) => {console.log(count, "--count--1");return (function () {console.log(context.timeout, "--context.timeout--1");if (context.timeout) {clearTimeout(context.timeout);console.log(context.timeout, "--context.timeout--2");} else {func();console.log("--func");}context.timeout = setTimeout(() => {context.timeout = null;console.log(context.timeout, "--context.timeout--4");}, delay);console.log(context.timeout, "--context.timeout--3");count += 1;console.log(count, "--count--2");})();
};
延时器方法 setTimeout () 的返回值是一个代表定时器唯一身份标识的编号
连续点击 4 次结果
1 '--count--1'
null '--context.timeout--1'
--func
126 '--context.timeout--3'
2 '--count--2'
2 '--count--1'
126 '--context.timeout--1'
126 '--context.timeout--2'
132 '--context.timeout--3'
3 '--count--2'
3 '--count--1'
132 '--context.timeout--1'
132 '--context.timeout--2'
138 '--context.timeout--3'
4 '--count--2'
4 '--count--1'
138 '--context.timeout--1'
138 '--context.timeout--2'
138 '--context.timeout--3'
5 '--count--2'
null '--context.timeout--4'
null '--context.timeout--4' 因为前面的几个定时器 都被 clearTimeout 了,不会执行JavaScript 防抖 (vue中写法总结)
上一篇:ES6 入门教程 16 Reflect 16.1 概述
下一篇:反义词是什么