闭包,最熟悉的陌生人,用久了框架层级的东西,对最该知道的内容产生了陌生感,不过没关系,再次认识你一下。话不多说,我要开始认识你了。
但是,再学习闭包之前,要先简单回顾一下IIFE,立即执行函数,全名:immediately invoked function expression。
IIFE
- 会创建新的作用域,作用域的变量不会与外界变量相互污染。
- IIFE也可以有自己的返回值。
闭包
大家很容易搜到的定义大概是这样的 “一个函数访问了它的外部变量,那么它就是一个闭包”。如果这样理解的话。那么在javascript中,任何一个function都是闭包了。其实说的也没错,但是就是初学者可能无法真正的理解闭包。
代码一:
function foo(x) {
var tmp = 3;
function bar(y) {
alert(x + y + (++tmp));
}
bar(10);
}
foo(2)
上面就是一个函数访问了它的外部变量,那么它就是一个闭包,写出来的例子,显然和真正意义上的闭包不太像那么一回事。那么我们看下面的例子。
代码二:
function foo(x){
var tmp = 3;
return function (y) {
alert(x + y + (++tmp));
}
}
var bar = foo(2);
bar(10);
代码一和代码二的结果都为16。但是代码二看起来更像是闭包,对比一下,不难知道,两者的区别就是在于是否包含 “return”。下面根据自己的理解大概总结一下。
需要注意的是,上面的“return”并不是必须的,这是这里用作对比更为明显。
理解
一个函数访问了它的外部变量,那么它就是一个闭包
一个函数访问了它的外部变量,但是当它的外部函数退出之后,外部函数的变量却没有退出,仍维持在内部函数内,那么在内部函数就维持了外部函数的局部数据,这样的现象为理解上的闭包现象。
代码二举例说明:
外部函数: foo
外部变量:x 和 tmp
内部函数:return 出去的匿名函数
var bar = foo(2); 执行外部函数 foo,
bar(); 此时执行bar,而外部函数已经完成并退出,但是在 bar函数内,仍然保存外部变量 x 和 tmp。
看到这里,相信大家已经对闭包有了大概的印象,那么闭包到底在程序中什么地方会用到呢?
实例
在构建对象模式时,通常会有私有变量,私有方法。但在javascript中并没有这一类的名词概念,但是闭包就可以模拟实现构造函数内部的私有变量跟私有方法。具体代码如下:
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
上面就是闭包的比较实用的例子,但是这样会有一个问题,就是很容易还导致内存泄漏,因为这样确实是将变量私有化了,但是却一直没有释放。(关于这部分内容,作为小白的我还需要深入学习,是从别的网站看到会有这样的问题,就拿出来作为标记)
如果在项目里面不能充分的利用闭包的好处,可以考虑更换原型的方式修改自己的代码
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype.getName = function() {
return this.name;
};
MyObject.prototype.getMessage = function() {
return this.message;
};
关于闭包的实用性,目前也就是学到这些,接下来,就是处理无意之中形成闭包带来的“惊喜”
var arr = [];
for (var i=0;i<3;i++){
arr[i] = function () {
return i;
};
}
for(var j =0 ;j < 3; j++){
console.log(arr[j]());
}
上面的结果是 3,3,3
和预期的 1,2,3 好像不太一样
原因:在arr里面的每一项的function中的 i 值,都是3.这是因为,在javascript中,没有块级作用域,所以即使 i 是在for循环里面被定义的,但是它仍然是全局变量,每次的加加操作,都会改变它的值,然而,在第二个for循环进入的时候,第一个for循环已经完成,此时 i==3;
解决上面的问题,就需要用到我们开头写到的IIFE,因为 IIFE会创建新的作用域,作用域的变量不会与外界变量相互污染。 所以,在第一个for中的每一个i 都是新的作用域内部的i,不会相互污染。具体代码如下:
var arr = [];
for (var i=0;i<3;i++){
(function(n){
arr[n] = function () {
return n;
}
})(i)
}
for(var j =0 ;j < 3; j++){
console.log(arr[j]());
}
此次认识的闭包就这么多了,程序届的小白一枚,初出茅庐,鼓励之上。以上全是个人学习的心得。如有错误,请多多指正。
参考网址如下:
MDN.
JS中的IIFE和闭包.
Javascript闭包——懂不懂由你,反正我是懂了.