目录
闭包的理解
如何产生闭包?
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
闭包是什么?
- 理解一:闭包是嵌套的内部函数,例如:下面例子中内部函数
fun2()
就是一个闭包
function fun1(){
var a = '我是父元素定义的变量a';
var b = '变量b'
function fun2 (){//fun2()是内部函数,一个闭包
console.log(a);//使用了父函数中声明的变量a
}
fun2();
}
fun1()//调用函数fun1()
-
理解二:通过debug调试可以查看到,闭包是包含被内部函数引用外部函数的变量(函数)的对象。
-
通过调式可以看到一个
Closure (闭包)
里面只显示变量a
,没有变量b
,故此也可以理解为闭包是被内部函数引用外部函数的变量(函数)的对象。 -
执行内部函数定义就会产生闭包(不用调用函数)
产生闭包的条件
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
常见的闭包
将函数作为另一个函数的返回值
function fun1() {
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();//1
f();//2
f();//3
注意:
- 该过程只产生了一个闭包
- 每次调用
f()
相当于执行了一次fun2()
将函数作为一个实参传递给另一个函数调用
function show(msg, time) {
setTimeout(function () {
console.log(msg);
}, time)
}
show("新奇时间", 1000);
</script>
闭包的作用
- 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长局部变量的生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
例如上面的例子中
function fun1() {
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();//1
f();//2
f();//3
- 在这个例子中,函数执行完后
变量a
仍然存活在内存中 - 可以通过闭包去操作(读写)
fun1()中变量a
。
闭包生命周期
- 闭包产生:在嵌套内部函数定义执行完成时就产生闭包(不是调用)
- 闭包死亡:在嵌套的内部函数成为垃圾对象时
function fun1() {
//此时闭包就已经产生(函数提升,内部函数对象已经建立)
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();//1
f();//2
f();//3
f = null;//闭包死亡(包含闭包的函数对象成为垃圾对象)
闭包的应用:定义JS模块
- 具有特定功能的js文件
- 将所有的数据和功能都封装在一个函数内部(私有的)
- 只向外暴露一个包含n个方法的对象和函数
- 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
自定义模块
- 这里先创建一个
js文件
,命名为myModule.js
代码如下
function myModule() {
//私有数据
var str = "Hello World!!!";
//操作数据的函数
function showBig() {
console.log("showBig():" + str.toUpperCase());
}
function showSmall() {
console.log("showSmall():" + str.toLowerCase());
}
//向外暴露的对象(给外部使用的方法)
return {
showBig: showBig,
showSmall: showSmall
}
}
- 然后将
myModule.js文件
通过<script>
标签在html文件
中进行引用
<script src="./myModule.js"></script>
<script>
var fn = myModule();
fn.showBig();
fn.showSmall();
</script>
效果:
另一种写法:
利用立即执行函数往window里面直接添加方法
(function() {
//私有数据
var str = "Hello World!!!";
//操作数据的函数
function showBig() {
console.log("showBig():" + str.toUpperCase());
}
function showSmall() {
console.log("showSmall():" + str.toLowerCase());
}
//向外暴露的对象(给外部使用的方法)
window.myModule = {
showBig: showBig,
showSmall: showSmall
}
})()
- 然后将
myModule.js文件
通过<script>
标签在html文件
中进行引用
<script src="./myModule.js"></script>
<script>
myModule.showBig();
myModule.showSmall();
</script>
闭包的缺点及解决方法
缺点
- 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
- 容易造成内存泄漏
内存溢出和泄漏
内存溢出
- 一种程序运行出现的错误
- 当程序运行时需要的内存超过了剩余的内存时,就会抛出内存溢出的错误
内存泄漏
- 占用的内存没有及时释放
- 内存泄漏积累多了就容易导致内存溢出
常见内存泄漏
- 意外的全局变量,没用使用
var、let、const
申明,直接使用的变量
function fn(){
a = new.Array(10000);
}
fn();
- 没有及时清理的计时器或回调函数
var time = setInterval(function(){//循环定时器后不清理
console.log("还有谁?");
},1000);
//clearInterval(time);
- 闭包
function fun1() {
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();//1
f();//2
f();//3//闭包会产生内存泄漏
f = null;//回收闭包-->让内部函数成为垃圾对象
解决方法
- 尽量少使用闭包
- 及时释放
面试题
面试题一
//代码片段一
var name = "The Window";
var Object = {
name: "The Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
console.log(Object.getNameFunc()());// ? "The Window"
//代码片段二
var name2 = "The Window";
var Object = {
name2: "The Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name2;
};
}
};
console.log(Object.getNameFunc()());// ? "The Object"
解析:
- 代码片段一中没有闭包,相当于函数直接执行,故此
this指向window
,打印"The Window"
- 代码片段二中,外部函数先申明一个变量保存了
此时的this
此时this指向Object
,然后内部函数使用了外部函数变量that
,产生了闭包,that
中值保存的是this指向的Object
,故此打印"The Object"
面试题二
function fun(n, o){
console.log(o);
return{
fun: function(m){
return fun(m, n);
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined, ?, ?, ?
console.log("--------------------");
var b = fun(0).fun(1).fun(2).fun(3);//undefined, ?, ?, ?
console.log("--------------------");
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined, ?, ?, ?
思路
- 先找到产生闭包的变量,也就是
n
,每次打印的是上一次产生的闭包n
,没有产生闭包打印就是undefined
- 如下:
var a = fun(0); //undefined 这里会产生闭包n = 0;故此下面都会打印0
a.fun(1); a.fun(2); a.fun(3);//0, 0, 0
console.log("--------------------");
var b = fun(0).fun(1).fun(2).fun(3);//undefined, 0, 1, 2
console.log("--------------------");
var c = fun(0).fun(1);//undefined, 0 这里会产生闭包n = 1;
c.fun(2);//1 这里会产生闭包n = 2,但是会被垃圾回收
c.fun(3);//1 这里会产生闭包n = 3 但是会被垃圾回收