大家好,欢迎来到IT知识分享网。
防抖
考虑一个场景,有一个按钮点击会触发网络请求,但是我们并不希望每次点击都发起网络请求,而是当用户点击按钮一段时间后没有再次点击的情况才去发起网络请求,对于这种情况我们就可以使用防抖。
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
btn.onclick = debounce(() => {
console.log('点了');
}, 1000)
以上是转载别人的文章,逻辑还是挺好理解的,但是当时就是看不懂其中的args从哪里来,
比如后续写的onclick,把箭头函数等参数传进去,那么会得到debounce里return的匿名函数,那么这个匿名函数的形参arg是什么,
为了探究这个问题,又去看了其他的防抖节流文章,下面这个例子就相当于是提示了arg就是传进去的123,然后翻阅了MDN链接,发现这个应该是function隐式传入的参数,arguments是一个对象,包括了传递进来的参数
function debounce(delay, fun) {
let time = null
return function (...args) {
console.log(arguments);
console.log(args);
console.log(args ==arguments[0]);//true
let self = this
if (time) clearTimeout(time)
time = setTimeout(() => {
fun.apply(self, arguments)
}, delay)
}
}
function debounceTest(arg) {
console.log(arg)
}
(function () {
// 得到返回的方法
let fun = debounce(1000, debounceTest)
for (let i = 0; i < 10; i++) {
// 执行方法并传参
fun(123)
}
})()
MDN文档截图如下,
意思是arguments对象是所有函数都有的,除了箭头函数,如果一个函数不需要形参也会有这个对象,所以在这里就可以用该对象来当做柯里化的内部参数,意为有就附参,没有就算了,在function(args),这里的args就是参数,对应这arguments的第一个即arguments[0],如果是多个参数,就依次排下去,如下
而正是因为arguments是类数组,所以可以用apply来改变this指向,可以把前面的几个参数赋值给改了this的新函数
time = setTimeout(() => {
fun.apply(self, arguments)
}, delay)
其中还用到了闭包,
- timer是闭包函数调用的外部变量
- arguments是每一个funcation函数都会有的对象,在函数调用时,浏览器每次都会传递进两个隐式参数this和arguments,封装实参的对象arguments,在ES6中的箭头函数中使用…rest作为arguments.
- 函数debounce返回的匿名函数调用了timer,导致timer脱离debounce函数的作用域存活于内存中,直到匿名函数也执行完毕,才会被回收。故当点击间隔小于delay毫秒时,timer就会不断更新值,导致setTimeout内的匿名函数无法执行(因为setTimeout内的函数会延迟delay毫秒执行),直到没有新的调用事件时,fn才会正常延迟到delay毫秒后执行。
节流
节流跟防抖的场景不同,防抖是多次触发,却只有最后一次执行,节流是多次触发,但变成有节奏的有规律的执行,
// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的时间
let lastTime = 0
return function(...args) {
// 当前时间
let now = +new Date()
// 将当前时间和上一次执行函数时间对比
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
setInterval(
throttle(() => {
console.log(1)
}, 500),
1
)
节流同样用到了闭包,lastTime一直保存着,不然无法达到效果
闭包
闭包就是能够读取其他函数内部变量的函数参考链接
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域
闭包的特性:
- 函数内再嵌套函数
- 内部函数可以引用外层的参数和变量
- 参数和变量不会被垃圾回收机制回收
-说说你对闭包的理解
-
使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念
-
闭包 的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中
-
闭包的另一个用处,是封装对象的私有属性和私有方法
-
好处:能够实现封装和缓存等;
-
坏处:就是消耗内存、不正当使用会造成内存溢出的问题
使用闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露
解决方法是,在退出函数之前,将不使用的局部变量全部删除
注意
使用闭包的时候要注意,虽然闭包内的函数,可以访问到数据,但是如果把该数据当做参数传递,那么就不会改变该数据,而是对他进行复制,相当于穿了个值进去而已
比如
function test() {
let a = 1
function change(x) {
x = x + 1
console.log(x);
}
change(a)
console.log(a);
}
test()
此处的change函数,把a传进去了,但是此处只是传递了a的值,后续不会对test里的a进行更改,当初做错了,不知道这个点,导致在做二叉树的递归的时候,还有动态规划的递归的时候,一直得不到想要的值,
如果想要change函数能改变a的值,就不要把a当做参数传进去,应该做如下修改
function test() {
let a = 1
function change() {
a = a + 1
console.log(a);
}
change(a)
console.log(a);
}
test()
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/14505.html