一、闭包定义
1、定义:
当函数能够记住(外部作用域可能不存在)并且访问自己的作用域链时就会产生闭包。
2、闭包本质上是一个c++对象,管理内部函数依赖于外部环境的数据。
<script type="text/javascript">
/*闭包定义
* 闭包:当函数发生嵌套时,如果内部函数引用了外部函数的变量时,就会产生闭包
闭包是一个js引擎创建的c++对象,用来管理一些数据,内部函数引用了外部函数的数据
闭包(标准定义):当函数能够记住并且访问自己的作用域链时就会产生闭包 。
如何解释闭包的定义?伪闭包和闭包的两种情况。
常见的闭包
a. 将函数作为另一个函数的返回值
b. 将函数作为实参传递给另一个函数调用
*/
function wrap() {
var a = 1;
var b = 2;
function inner() {
console.log(a);
console.log(b);
}
//常见闭包:1、将函数作为另一个函数的返回值
return inner;
}
var c = wrap();
c();
// 常见闭包: 2. 将函数作为实参传递给另一个函数调用
function showMsgDelay(msg, time) {
setTimeout(function() {
console.log(msg)
}, time)
}
showMsgDelay('hello', 1000)
</script>
二、产生闭包的条件
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包。
- a. 函数嵌套
- b. 内部函数引用了外部函数的数据(变量/函数)
- c. 调用外部函数
三、常见的闭包
a. 将函数作为另一个函数的返回值
b. 将函数作为实参传递给另一个函数调用
四、闭包作用
闭包作用: 延长包裹函数的生命周期;让包裹函数的外部可以读到包裹函数内部的数据。
<!--闭包作用
1. 延长了包裹函数局部变量的生命周期
2. 让包裹函数外部读写到包裹函数内部的数据 -->
<script type="text/javascript">
function wrap(){
var a=1;
var b=2;
function inner(){
console.log(a);
console.log(b);
}
}
var inner=wrap();
inner();
</script>
五、闭包生命周期
闭包创建: 闭包 是包裹函数的执行上下文被创建时,即包裹函数被调用时创建。
闭包销毁:手动将内部函数置为null.
<!--
闭包生命周期:
创建: 闭包 是包裹函数的执行上下文被创建时,即包裹函数被调用时创建。
销毁:手动将内部函数置为null.
闭包什么时候被收回?
当闭包的引用计数为0的时候,自动被收回
-->
<script type="text/javascript">
function wrap() {
var a = 1;
var b = 2;
// debugger
function inner() {
console.log(a);
console.log(b);
}
return inner;
}
//闭包是由wrap函数创建,由wrap包裹函数将闭包交给inner函数的作用域
var c = wrap(); //创建闭包
c();
c=null;//销毁闭包
</script>
六、闭包缺点:内存泄漏
<!--内存泄漏&内存溢出
内存溢出:指的是程序向系统申请一定大小内存,而系统不能满足程序的要求就是内存的溢出。
内存泄漏:指申请的内存一直得不到释放,GC回收不了。一般在项目中就是,你声明的变量一直保存在内存中,它有值但你把它的引用地址搞丢了一直没法用它,而GC又没法回收这块内存给别的程序使用就叫内存泄漏。
-->
<script type="text/javascript">
function wrap(){
var a=1;
var b=2;
function inner(){
console.log(a);
console.log(b);
}
}
var inner=wrap();
inner();
// 虽然使用inner=null可以销毁闭包,但是在开发中,代码量大,不容易确定在何处销毁,一般不做销毁处理,因此导致了内存溢出.
inner=null;//销毁闭包
</script>
七、 伪闭包(鸡肋闭包)
不是必须创建闭包,通过变量查询也可以实现变量值获取,却创建了闭包。
<!--伪闭包(鸡肋闭包):不是必须创建闭包,通过变量查询也可以实现变量值获取,却创建了闭包-->
<script type="text/javascript">
function wrap(){
var a=1;
var b=2;
function inner(){
console.log(a);
console.log(b);
}
inner();
}
wrap();
</script>
八、闭包应用——模块化
定义JS模块:
1. 具有特定功能的js文件
2. 将所有的数据和功能都封装在一个函数内部(私有的)
3. 只向外暴露一个包信n个方法的对象或函数
4. 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
<body>
<!--
闭包的应用2 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="05_coolModule.js"></script>
<script type="text/javascript">
var module = coolModule()
module.doSomething()
module.doOtherthing()
var module2 = coolModule()
module2.doSomething()
module2.doOtherthing()
</script>
九、闭包缺点
1、缺点
- 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
- 容易造成内存泄露
2、 解决
- 能不用闭包就不用
- 及时释放
十、使用细节和注意事项:
1、闭包创建个数与包裹函数的调用次数有关。
<script type="text/javascript">
function wrap() {
var a = 1;
var b = 2;
function inner() {
console.log(a);
console.log(b);
}
return inner;
}
//闭包创建个数与包裹函数的调用次数有关
var c = wrap();
var cc =wrap();
var ccc =wrap();
ccc();
cc();
c();
</script>
2、闭包创建后存放:在内部函数的作用域中。
3、闭包使用:内部函数调用时使用。
4、闭包缺点:内存泄漏。
5、包裹函数创建的闭包是和内部函数一一绑定关系。