'js难点之closure'

Closure

对于闭包(closure),我理解的就是函数内部使用函数,内部的函数就称为闭包。例如:

1
2
3
4
5
funOut() {
funIn(){
//执行命令
}
}

其中funIn()就称为funOut()函数的闭包,类似java中的内部类什么的。

Closure作用

每一种设计都有它的作用,如果闭包没有作用,恐怕也不会有人去研究它了。

作用1:进行回调

首先还是看一段nodejs代码:

1
2
3
4
5
6
7
8
9
10
11
12
var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});

console.log("程序执行结束!");

//以上代码执行结果如下:
//程序执行结束!
//菜鸟教程官网地址:www.runoob.com

这是nodejs中的一个特性,叫做异步回调函数。本质上来说,所有的异步回调函数都是闭包。可以看以下的代码对比。

1
2
3
4
5
6
7
8
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});

fs.funOut(arr0,funIn) {
funIn();
}
作用2:对于闭包(closure),当外部函数返回之后,内部函数依然可以访问外部函数的变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
function f1(){    
var N = 0; // N是f1函数的局部变量
function f2() {// f2是f1函数的内部函数,是闭包
N += 1; // 内部函数f2中使用了外部函数f1中的变量N
console.log(N);
}
return f2;
}
var result = f1();

result(); // 输出1
result(); // 输出2
result(); // 输出3

代码中,外部函数f1只执行了一次,变量N设为0,并将内部函数f2赋值给了变量result。由于外部函数f1已经执行完毕,其内部变量N应该在内存中被清除,然而事实并不是这样:我们每次调用result的时候,发现变量N一直在内存中,并且在累加。为什么呢?这就是闭包的神奇之处了!

作用3 使用闭包定义私有变量

通常,JavaScript开发者使用下划线作为私有变量的前缀。但是实际上,这些变量依然可以被访问和修改,并非真正的私有变量。这时,使用闭包可以定义真正的私有变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Product() {   
var name;
this.setName = function(value) {
name = value;
};
this.getName = function() {
return name;
};
}
var p = new Product();
p.setName("Fundebug");
console.log(p.name); // 输出undefined
console.log(p.getName()); // 输出Fundebug

代码中,对象p的的name属性为私有属性,使用p.name不能直接访问。

注意点

  • 1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  • 2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

    参考资料

    学习Javascript闭包(Closure)