哈哈,终于又能让大家看到我的文章了,开心😊。感觉又有好长时间没写了,各位看官,请跟随我一起畅游知识的海洋吧。
上次跟大家分享了 JavaScript作用域 的相关知识点,也不知道大家看下去多少,那可是基础中的基础,学习就是这样,只有基础牢固了,接收其他知识的时候才能如鱼得水,如胶似漆,如梦如幻,如……………………
(请原谅作者语文知识的欠缺)
今天主要是跟大家分享一下javascript
中闭包的问题,如你所料,又是一篇基础分享,赶紧止住这些呼之欲出的废话,开始我们的表演 ↓↓↓↓
1.什么是闭包
在我们看到的各种技术书籍中,都会把闭包描述的很抽象,这里我们先给闭包一个定义 闭包就是可以在函数外部访问函数作用域中的的内部变量,同时它也可以将变量长久的保存在内存中 。在javascript
中,闭包是一个重难点,难是因为它确实很难理解,重要是因为在javascript
中很多高级写法都需要闭包来实现。
先来个开胃小栗子吧。
1.1 调用函数,实现每次+1的输出操作。
function a () {
let number = 10;
number++;
console.log(number);
}
a() // 11
a() // 11
a() // 11
上述代码中,并不能实现我们所需要的效果。这是为什么呢?
因为函数a
在执行完成后,就会将内部变量number
释放掉。每次执行a
函数都会重新创建执行 变量创建 -- 使用 -- 销毁
的整个过程,所以函数调用会一直输出同一个结果。那么如何才能达到题目的要求?
改进版
function a () {
let number = 10;
return function () {
number++;
console.log(number)
}
}
let result = a()
result(); // 11
result(); // 12
result(); // 13
通过改进,已经实现了题目所要求的效果。
这里是因为执行a
之后,还存在number
变量的引用,会将变量长久的保存在内存中,不会释放掉。所以可以实现依次累加的效果。
1.2 实现下列代码从 0-9 打印输出
问题
for(var i = 0;i < 10 ; i ++) {
setTimeout(function () {
console.log(i)
}, 0);
}
// 输出10个10
问题剖析:
-
setTimeout
是异步执行的,(这里与JavaScript事件队列
相关,后续会出相关文章。)。等到循环执行完成之后才会执行打印函数。 -
var
定义的是全局变量,i++
修改的也是全局变量 -
在打印函数执行的时候,
i
的值已经为10
。所以打印函数打印的都是10
改进
for(var i = 0;i < 10 ; i ++) {
function a(i){
return function (){
console.log(i)
}
}
setTimeout(a(i), 0);
}
解决方法剖析:
- 由于
setTimtout
执行的是闭包函数,每次传递到a
函数中的i
值都会被保存,所以打印函数打印的是自己保存的i
值
相信你看到这里已经累了。先休息下吧,眺望一下远方,舒缓一下眼睛。如果你是在车上,请闭上双眼休息一下。
2.闭包的用途
2.1 使用闭包实现 setTimeout 传参
形如这种方式使用,我们可以用闭包实现 setTimeout
传参的效果
for(var i = 0;i < 10 ; i ++) {
function a(i){
return function (){
console.log(i)
}
}
setTimeout(a(i), 0);
}
2.2 函数防抖和节流
这里只通过实例代码让大家看下,后期会出文章说明。
函数防抖实现
function debounce(fn) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, 500);
};
}
函数节流实现
function throttle(fn) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, 500);
};
}
3.使用闭包的注意点
3.1 内存泄漏问题
因为闭包会将变量长久的保存在内存中,所以会造成内存泄漏,所以需要在使用完成之后销毁变量,释放内存。
3.2 this指向问题
此问题我们在讨论this指针的时候讨论过,闭包的this
是指向window
的。
var obj = {
getName: functioin() {
return function () {
console.log(this)
}
}
}
obj.getName()() // window
4.实现篇
4.1 使用闭包实现一个三击响应事件
第一步,实现点击输出123
let box = document.getElementById('box')
box.onclick = (function () {
let count = 1;
return function () {
console.log(count)
count ++
}
})()
点击 box
的元素,可以看到控制台输出123
第二步,添加时间控制,1s内连续点击三次后执行事件
let box = document.getElementById('box')
box.onclick = (function () {
let count = 0;
let start = Date.now() // 添加开始时间
return function () {
count ++
if (count === 3) {
let end = Date.now() // 结束时间
if (end - start <= 1000) { // 如果三次点击事件在1s内,输出成功
console.log('三击事件响应成功')
}
count = 0; // 三击之后将条件重置
start = Date.now() // 开始时间为当前时间
}
}
})()
第三步,友情提示
将下方代码修改之后,可由三击事件变为任意多击事件。
if (count === n) // n代表点击次数
好了亲爱的看官朋友们,本次分享的内容就到这里了。希望你们能在自己喜欢的道路上越走越远。🤗🤗🤗
网友评论