闭包
# 回顾知识
根据作用域的不同,分为全局变量和局部变量,函数外部的叫全局变量,函数内部叫局部变量,分为以下三点
- 函数内部是可以使用函数外部的全局变量的
- 函数外部是不能使用函数内部的局部变量的
- 当函数执行完以后,函数内部局部变量就会销毁
话不多说上代码:
function fun() {
var a = 10
}
console.log(a) // 报错为a为定义:Uncaught ReferenceError: a is not defined
2
3
4
但这时候我们又想让a变量在函数外部能够被访问,我们又该咋办?就引入了闭包这个概念
# 闭包
# 闭包概念
javascipt高级设计:指有权访问另一个函数作用域中的变量函数
个人定义:函数外部有权访问函数内部的变量,意味着函数内部就为局部变量
例如:上代码:
function fun() {
let a = 1
return function() {
console.log(a)
}
}
// fun() 调用之后得到一个函数
// 相当于 function() {console.log(a)} 匿名函数
let rersult = fun() // 把匿名函数赋值给result
rersult() // 调用函数
2
3
4
5
6
7
8
9
10
打印结果:
1
说明:
fun为闭包函数,return函数只起到让闭包函数外部能够访问闭包函数内部的变量作用
# 在谷歌浏览器调试闭包
说明:scope为作用域,local为局部,closure为闭包,a为闭包函数的局部变量,gloabal全局作用
# 闭包的作用
- 延伸了变量的作用范围(延长局部变量的生命周期)
- 函数外部可以操作函数内部的数据
上代码:
function fun1() {
let b = 5
return function () {
b ++
console.log(b)
}
}
const result = fun1()
result() // 6
result() // 7
2
3
4
5
6
7
8
9
10
分析:
- fun1函数执行完成以后,由于return了一个函数变量b不会立马销毁,只有在闭包函数外部使用完以后才会销毁
- 闭包函数外部是看不到b变量的,但是可以在外部操作它
# 内存泄漏和内存溢出
# 内存泄漏
内存泄漏:占用的内存没有及时清理
影响:内存泄漏次数积累多了,就会导致内存溢出
# 常见的内存泄漏
- 意外的全局变量
- 没有及时清理定时器或者回掉函数
- 闭包
例如内存泄漏1:
function fun() {
let a = new Array(1)
console.log(a)
}
fun() // a为全局变量[]数组
2
3
4
5
6
例如内存泄漏2:
let timer = ''
timer = setInterval(() => {
console.log('清理计时器')
},1000)
// clearInterval(timer) 清除计时器
2
3
4
5
6
例如内存泄漏3:
function fun1() {
let b = 5
return function () {
b ++
console.log(b)
}
}
let result = fun1()
// result = null 回收闭包
2
3
4
5
6
7
8
9
10
# 内存溢出
程序运行时候出现错误。程序运行所需内存超过剩余内存,就会出现内存溢出的错误
例如:
let obj = {}
for (let i = 1 ; i < 1000; i++) {
obj[i] = new Array(20)
console.log('@@@')
//把所有的数组都放到obj里面保存,导致obj占用很大的内存
}
2
3
4
5
6
# 闭包是否会产生内存泄漏
一般情况下是不会的,这种情况是预期的,除非代码质量不高,滥用代码。因为内存泄漏是非预期的,本来想回收掉但没回收
# 闭包的应用场景
# 高阶函数
常见的高阶函数:Promise、setTimeout、arr.map()
# 接收函数作为参数
上代码:
function fun(callback) {
callback && callback()
}
fun(function () { console.log (666)})
2
3
4
# 函数作为返回值
上代码:
function fun(fn) {
return function() {
console.log('函数作为返回值')
}
}
fun()
2
3
4
5
6
# 函数的坷里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式 上代码:
function fun(a,b,c) {
return b => {
return c => {
return a+b+c
}
}
}
let resolut = fun(1)(2)(3)
console.log(resolut) // 6
2
3
4
5
6
7
8
9
# 应用场景:迭代商品送福利的计算
# 防抖和节流
# 防抖
官方:触发高频事件后一段时间(wait)只会执行一次函数,如果指定时间(wait)内高频事件再次被触发,则重新计算时间。
个人理解:重复执行的函数,在规定时间内,只执行最后一次。频率:一次
上代码封装:
function debounce(fn,delay) {
let t = null
return function() {
if (t) clearTimeout(t)
t = setTimeout(() => {
fn.apply(this)
},delay)
}
}
2
3
4
5
6
7
8
9
应用场景:
input输入框的模糊查询、提交按钮、
# Demo演示
可用F12开发者工具查看元素及样式,可打开codepen在线编辑代码。
# 节流
官方:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
个人理解:一个函数执行一次后,大于设定周期才会执行第二次。频率:多次。
上代码封装:
function throttle(fn,delay = 200) {
let flag = true
return function () {
if(!flag) {
return
}
flag = false
setTimeout(() => {
fn.apply(fn)
flag = true
},delay)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
应用场景:
window对象的resize、scroll事件、鼠标的移动事件,比较适合动画相关的场景
# Demo演示
可用F12开发者工具查看元素及样式,可打开codepen在线编辑代码。
# 总结
防抖和节流貌似很相似,但是节流你可以想象一下水坝,你在河道中建了水坝,不能让水不流动,只能让它流慢点;换言之,不能让用户的方法都不执行,如果这样干就是防抖。
再简单的说:防抖指的是只要你动作不停,就不会执行;而节流指的是规定时间内必须执行一次;一个上限,一个为下限。