闭包的理解
如何产生闭包?
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
闭包是什么?
- 理解一:闭包是嵌套的内部函数,例如:下面例子中内部函数
fun2() 就是一个闭包
function fun1(){
var a = '我是父元素定义的变量a';
var b = '变量b'
function fun2 (){
console.log(a);
}
fun2();
}
fun1()
-
理解二:通过debug调试可以查看到,闭包是包含被内部函数引用外部函数的变量(函数)的对象。 -
通过调式可以看到一个Closure (闭包) 里面只显示变量a ,没有变量b ,故此也可以理解为闭包是被内部函数引用外部函数的变量(函数)的对象。 -
执行内部函数定义就会产生闭包(不用调用函数)
产生闭包的条件
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
常见的闭包
将函数作为另一个函数的返回值
function fun1() {
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();
f();
f();
注意:
- 该过程只产生了一个闭包
- 每次调用
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();
f();
f();
- 在这个例子中,函数执行完后
变量a 仍然存活在内存中 - 可以通过闭包去操作(读写)
fun1()中变量a 。
闭包生命周期
- 闭包产生:在嵌套内部函数定义执行完成时就产生闭包(不是调用)
- 闭包死亡:在嵌套的内部函数成为垃圾对象时
function fun1() {
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();
f();
f();
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);
function fun1() {
var a = 0;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
var f = fun1();
f();
f();
f();
f = null;
解决方法
面试题
面试题一
var name = "The Window";
var Object = {
name: "The Object",
getNameFunc: function () {
return function () {
return this.name;
};
}
};
console.log(Object.getNameFunc()());
var name2 = "The Window";
var Object = {
name2: "The Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name2;
};
}
};
console.log(Object.getNameFunc()());
解析:
- 代码片段一中没有闭包,相当于函数直接执行,故此
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);
console.log("--------------------");
var b = fun(0).fun(1).fun(2).fun(3);
console.log("--------------------");
var c = fun(0).fun(1); c.fun(2); c.fun(3);
思路
- 先找到产生闭包的变量,也就是
n ,每次打印的是上一次产生的闭包n ,没有产生闭包打印就是undefined - 如下:
var a = fun(0);
a.fun(1); a.fun(2); a.fun(3);
console.log("--------------------");
var b = fun(0).fun(1).fun(2).fun(3);
console.log("--------------------");
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
|