闭包是什么
闭包=内层函数+引用的外层函数的变量(定义在一个函数内部的函数)
闭包是指有权访问另一个函数作用域中的变量的函数。这些变量始终保存在内存之中,不会随着函数的结束而自动销毁。将变量进行私有化。
特征:函数的嵌套、函数内部可以引入外部函数中的这个参数跟变量、参数和变量不会被js的垃圾回收机制回收
闭包的本质将函数内部和函数外部连接起来的桥梁。
闭包的例子
在上面我们理解了闭包的定义,在通常情况下我们会使用一个函数包裹住闭包结构,以起到对变量的保护应用。
function outer(){
const a = 1
function f(){
console.log(a) //1
}
f()
}
outer()
这段代码定义了一个outer的外层函数,其中里面包含了一个内层函数f。其中内层函数打印调用了外层函数的变量值a。
闭包的特点
具备了全局变量和局部变量的特点
-
闭包拥有全局变量的不会被释放的特点
-
闭包拥有局部变量的无法被外部访问的特点
闭包的好处
-
可以让一个变量长期在内存中不被释放
-
避免全局变量的污染和全局变量不同的是闭包中的变量无法被外部使用
-
私有成员的存在,无法被外部调用,只能直接内部调用
闭包的歧义
闭包一定会有return吗?
不一定。
我们在什么情况下会用到return?
外部如果想要使用闭包的变量,那么此时需要用到return。
function outer(){
let a = 1
return function(){
console.log(a) //1
}
f()
}
const fn = outer()
fn()
举例:统计函数调用的次数
1.普通函数
这时候我们会发现一个问题,此时的i是一个全局变量,当我们在终端输入i=1000时,这个时候如果再次调用fn(),就会出现i被调用了1001次的结果
2.闭包形式
闭包一定会有内存的泄露吗?
不一定。只有函数才会产生作用域的概念,所以说内存泄漏是非常重要的。
内存泄漏指的是在程序运行过程中,由于错误的内存管理或者资源管理,导致已经不再需要的内存或者资源没有被释放或者回收,从而造成系统内存或者资源的浪费。这种情况会导致系统的内存或者资源使用量持续增加,最终可能导致程序运行变慢、系统崩溃或者其他不良后果。
谁会存在内存泄漏?
count变量
// 内存泄漏
function fn(){
let count = 1
function fun(){
count++
console.log(`函数被调用${i}次`);
}
return fun
}
const result = fn()
result() //2
result() //2
借助于垃圾回收机制的标记清除法可以看出,如何引起的内存泄漏
-
result是一个全局变量,代码执行完毕不会立即销毁
-
result使用fn函数
-
fn用到fun函数
-
fun函数里面用到count
-
count被引用就不会被回收,所以一直存在
注意:
-
不是所有的内存泄露都要手动回收的
-
比如react里面很多闭包就是不能回收的
闭包的应用
1.防抖
避免重复执行,只执行一次
//防抖 避免函数的重复调用 只会调用一次
function Antishake(fn,wait){ //第一个参数是函数 第二个参数是毫秒值
let timer = null //声明一个变量来接收延时器 初始值为null
return function(){
clearTimeout(timer)
timer = setTimeout(() => {
fn() //调用这函数
}, wait);
}
}
let an = Antishake(function(){ //用一个变量接收
console.log('555');
},2000)
document.querySelector('div').onmouseenter = ()=>{
an() //调用一次
}
2.节流
减少执行的次数,执行多次
function throttle(fn,wait){
let timer = null //节点闸
return function(){
if(timer) return //null false 不是null结果减少true 如果上传没有我就直接跳过 没有人我就上去
timer = setTimeout(() => { //上车了
fn()
timer = null //做完之后重新关闭节点闸
}, wait);
}
}
let throttle1 = throttle(()=>{
console.log('我上车了');
},2000)
document.querySelector('div').onclick = ()=>{
throttle1()
}