一、理解闭包
1. 如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
2. 闭包到底是什么? (可使用chrome调试查看,比较直接)
理解一: 闭包是嵌套的内部函数(绝大部分人)
理解二: 包含被引用变量(函数)的对象(极少数人)
即函数中有 Closure 对象(在chrome可以查看)
注意: 闭包存在于嵌套的内部函数中
3. 产生闭包的两个条件?
函数嵌套
内部函数引用了外部函数的数据(变量/函数) ===>> 内部函数执行函数定义就会产生闭包(不用调用内部函数)
总结:闭包实质上是函数作用域的副产物。 即内部函数访问外部函数的变量即产生
示例1:会产生闭包
<script type="text/javascript">
function fn1 () {
var a = 2
var b = 'abc'
function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)
console.log(a)
}
// fn2()
}
fn1()
</script>
浏览器中查看闭包
示例2: 不会产生闭包 ==>> fun2 是作为fun1中的变量,所以没有函数嵌套
<script type="text/javascript">
function fun1() {
var a = 3
var fun2 = function () {
console.log(a)
}
}
fun1()
</script>
二、常见的闭包
1. 将函数作为另一个函数的返回值
<script type="text/javascript">
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4 ==>>这里输出4是因为访问的闭包中的值,上一次闭包结束后a的值没有被释放,所以基础值变成了3
</script>
2. 将函数作为实参传递给另一个函数调用
<script type="text/javascript">
function showDelay(msg, time) {
setTimeout(function () {
alert(msg)
}, time)
}
showDelay('aaaaa', 2000)
</script>
//定时器中的回调函数和外部函数showDelay产生了函数嵌套,并且引用了外部函数的变量msg,从而产生了闭包
三、闭包的作用
1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
注意:
1. 函数执行完后, 函数内部声明的局部变量是否还存在? ==>> 一般是不存在, 存在于闭包中的变量才可能存在
2. 在函数外部能直接访问函数内部的局部变量吗? ==>> 不能, 但我们可以通过闭包让外部操作它
实例:闭包操作变量
<script type="text/javascript">
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
// return a
}
function fn3() {
a--
console.log(a)
}
return fn3
}
var f = fn1()
f() // 1
f() // 0
</script>
四、闭包的生命周期
1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
2. 死亡: 在嵌套的内部函数成为垃圾对象时
<script type="text/javascript">
function fn1() {
//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
var a = 2
function fn2 () {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>
五、闭包的应用_自定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
第一步:创建一个js文件,写一段js代码,并暴露出一下方法给外部使用
function myModule() {
//私有数据
var msg = 'ABcDefGhj'
//操作数据的函数
function doUpper() {
console.log('doUpper() '+msg.toUpperCase())
}
function doLower() {
console.log('doLower() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
return {
doUpper: doUpper,
doLower: doLower
}
}
第二步:html文件中引入自定义的模块,并使用其中的方法
<body>
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
var module = myModule() //解释定义的模块
//使用其中的方法
module.doUpper()
module.doLower()
</script>
</body>
注意:这种方法需要在html中保存一下自定义的模块,才能使用器暴露出来的方法。
使用 IIFE 的方法自定义JS模块:
(function () {
//私有数据
var msg = 'ABcDefGhj'
//操作数据的函数
function doUpper() {
console.log('doUpper() '+msg.toUpperCase())
}
function doLower () {
console.log('doLower() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法) ==> 直接挂载带window上
window.myModule2 = {
doUpper: doUpper,
doLower: doLower
}
})()
<body>
//直接引入模块并使用器方法
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
myModule2.doUpper()
myModule2.doLower()
</script>
</body>
六、闭包的优缺点
1. 缺点
函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
容易造成内存泄露
2. 解决
能不用闭包就不用
及时释放
<script type="text/javascript">
function fn1() {
var arr = new Array[100000] //会造成内存泄露
function fn2() {
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
f = null //让内部函数成为垃圾对象-->回收闭包
文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!