什么是闭包?
以下代码就是一个闭包,实现函数调用一次,a减一的功能
f1函数被存进了变量fm,他是全局变量,不会被垃圾回收机制回收,
下次再调用fm()的时候a的值在内存中保存,所以每次都是在原有的基础上减一
闭包需要满足四个条件:
1.有函数嵌套 (函数f2嵌套在函数f1中)
2.内部函数引用外部作用域的变量参数(函数f2引用了外部作用域的变量a)
3.返回值是函数 (函数f1返回的是函数f2)
4.创建一个对象函数,让其长期驻留 (fm就是创建的一个对象函数)
闭包总结:闭包就是直接调用函数里面的函数,这样变量声明可以在外函数内部,第一次向上找到变量,第二次变量已经缓存在内函数,不用继续找。
如何实现呢?内函数作为外函数的返回值,再将外函数赋值给一个变量,再使用这个变量调用,这样就是直接调用的内函数而没有经过外函数。
闭包的使用场景
闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。例如 setTimeout 传参、回调、IIFE(立即调用函数表达式)、函数防抖、节流、柯里化、模块化等等
setTimeout 传参
//原生的setTimeout传递的第一个函数不能带参数
setTimeout(function(param){
alert(param)
},1000)
//通过闭包可以实现传参效果
function myfunc(param){
return function(){
alert(param)
}
}
var f1 = myfunc(1);
setTimeout(f1,1000);
回调
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>test</title>
<link rel="stylesheet" href="">
</head>
<style>
body{
font-size: 24px;
}
h1{
font-size: 1.5rem;
}
h2{
font-size: 1.2rem;
}
</style>
<body>
<p>测试</p>
<a href="#" id="color-blue">蓝色</a>
<a href="#" id="color-red">红色</a>
<a href="#" id="color-green">绿色</a>
<script>
function changeSize(color){
return function(){
document.body.style.color = color;
};
}
var blue = changeSize("blue");
var red = changeSize("red");
var green = changeSize("green");
document.getElementById('color-blue').onclick = blue;
document.getElementById('color-red').onclick = red;
document.getElementById('color-green').onclick = green;
</script>
</body>
</html>
闭包的优缺点
优点:
1.变量被保存起来没有被销毁,随时可以被调用
2.只有函数内部的子函数才能读取局部变量,可以避免全局污染
缺点:
因为闭包不会销毁变量 所以会造成内存泄漏 也就是说当闭包的变量过多时会导致内存占用过大从而导致运行速度变慢(可以在闭包使用结束后手动清除或者置空变量来解决内存泄漏的问题)
为什么闭包可以保存状态
函数执行时会形成一个私有作用域,通常情况下当函数执行完成,栈内存会自动释放,但是如果函数执行完成,当前私有作用域(栈内存)中的某一部分内容被内存以外的其它东西(变量/元素的事件)占用了,那么当前的栈内存就不会释放掉,也就形成了不销毁的私有作用域(里面的私有变量也不会销毁)
通过闭包,变量a的状态被保留了,闭包(上例的fm)用到了外层变量(a),导致外层函数(f1)不能从内存释放。
只要闭包fm没有被垃圾回收机制清除,外层函数提供的运行环境也不会被清除,它的内部变量就始终保存着当前值,供闭包读取,所以闭包fm使得函数f1的内部环境,一直存在。