JS高级之------闭包
1.闭包的理解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01_理解闭包</title>
</head>
<body>
<!--
1. 如何产生闭包?
* 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
2. 闭包到底是什么?
* 使用chrome调试查看
* 理解一: 闭包是嵌套的内部函数(绝大部分人)
* 理解二: 包含被引用变量(函数)的对象(极少数人)
* 注意: 闭包存在于嵌套的内部函数中
3. 产生闭包的条件?
* 函数嵌套
* 内部函数引用了外部函数的数据(变量/函数)
* 执行外部函数
-->
<script type="text/javascript">
function fn1 () {
var a = 3
function fn2 () { // 执行函数定义就会产生闭包(不用执行内部函数,但是如果是函数表达式就不能)
console.log(a)
}
fn2()
}
fn1()
</script>
</body>
</html>
1.闭包产生分析
在chrome中var a=3处打上断点,调用fn1()函数,当代码执行到断点处,闭包就已经产生了
当代码进入fn2函数中时在本地中就可以看到闭包显示,如下图所示
2.疑问:目前是否执行定义就可产生闭包
3.内部函数没有引用的不会出现在闭包中
2.常见的闭包
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>02_常见的闭包</title>
</head>
<body>
<!--
1. 将函数作为另一个函数的返回值
2. 将函数作为实参传递给另一个函数调用
-->
<script type="text/javascript">
// 1. 将函数作为另一个函数的返回值
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1() // 产生几个闭包只跟外部函数有关,外部函数执行一次就产生一个闭包
f() // 3
f() // 4 // 这两行代码只是在反复执行同一个闭包
// 2. 将函数作为实参传递给另一个函数调用
/*
对于下面代码来说,能够产生闭包,外部函数是showMsgDelay函数,
内部函数是setTimeOut中的函数,引用的变量为msg
*/
function showMsgDelay(msg, time) {
setTimeout(function () {
console.log(msg)
}, time)
}
showMsgDelay('hello', 1000)
</script>
</body>
</html>
注意:产生几个闭包只跟外部函数有关,外部函数执行一次就产生一个闭包
3.闭包的作用
闭包存在的根本原因分析:首先函数嵌套,然后内部函数引用外部函数的变量,最后外部函数执行,导致闭包产生,但是闭包能够产生并且不消失的原因是由于外部变量对闭包的引用,导致垃圾回收器扫描的时候发现内部函数以及变量都存在引用关系,因此不会进行回收操作,导致外部能够持续使用闭包对变量进行操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>03_闭包的作用</title>
</head>
<body>
<!--
1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
1. 函数执行完后, 函数内部声明的局部变量是否还存在?
2. 在函数外部能直接访问函数内部的局部变量吗?
-->
<script type="text/javascript">
function fun1() {
var a = 3;
function fun2() {
a++; //引用外部函数的变量--->产生闭包
console.log(a);
}
return fun2;
}
var f = fun1(); //由于f引用着内部的函数-->内部函数以及闭包都没有成为垃圾对象
f(); //间接操作了函数内部的局部变量
f();
</script>
</body>
</html>
4.闭包的生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>04_闭包的生命周期</title>
</head>
<body>
<!--
1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
2. 死亡: 在嵌套的内部函数成为垃圾对象时
-->
<script type="text/javascript">
function fun1() {
//此处闭包已经产生
var a = 3;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();
f();
f = null //此时闭包对象死亡
</script>
</body>
</html>
5.闭包的应用
<!--
闭包的应用2 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
5.1 应用一
myModule.js
function myModule(){
var msg = "some message"
function doSomething(){
console.log("doSomething "+msg.toUpperCase())
}
function doOther(){
console.log("doOther "+msg.toUpperCase())
}
return {
doSomething,
doOther
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包的应用_自定义js模块</title>
</head>
<body>
<script src="./myModule.js"></script>
<script>
var m = myModule()
m.doSomething() // doSomething SOME MESSAGE
m.doOther() // doOther SOME MESSAGE
</script>
</body>
</html>
5.2 应用二
myModule2.js
(
function () {
var msg = "some message"
function doSomething() {
console.log("doSomething " + msg.toUpperCase())
}
function doOther() {
console.log("doOther " + msg.toUpperCase())
}
window.myModule2 = {
doSomething,
doOther
}
}
)()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包的应用_自定义js模块2</title>
</head>
<body>
<script src="./myModule2.js"></script>
<script>
myModule2.doSomething() // doSomething SOME MESSAGE
myModule2.doOther() // doOther SOME MESSAGE
</script>
</body>
</html>
6.闭包的缺点及解决
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>06_闭包的缺点及解决</title>
</head>
<body>
<!--
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>
</body>
</html>