Demo 1:闭包初识
先要认识几点现实:
- 函数外部无法访问函数内部的局部变量。
- 函数内的嵌套函数可以访问函数的局部变量
- 将嵌套函数作为闭包返回到外部空间,外部空间通过执行返回的嵌套函数可以实现外部空间对函数内部局部变量的间接访问。
关于闭包的定义及特殊性
函数在创建它的环境之外执行,那么它就是闭包。
如上例子中,函数displayName 如果在创建它的环境中:makeFunc函数内执行,则不叫闭包了。
闭包会记住创建它的环境。创建它的环境包含所在环境的局部变量,这也就是说为什么闭包能够使得外部空间访问到函数的局部变量的原因,同时也是发生内存泄漏的导火索。
Demo 2 : 返回闭包——函数工厂
上面的例子我们认识到:
* function可以不用写方法名。
* 两次对makeAdder的调用,虽然是同一makeAdder函数,但是对于返回的闭包来说,创建闭包的环境是二者是独立的,没有瓜葛。
Demo 3:闭包使用场景之:模拟私有方法
var counter=(function() {
var privateCounter=0;
function changeBy(val) {
privateCounter += val;
}
return {
increment:function() {
changeBy(1);
},
decrement:function() {
changeBy(-1);
},
value:function() {
return privateCounter;
}
}
})();
咋一看,看不懂,那么像剥洋葱一样一层层的解析:
第一层: (function() {….})()
我可以如此改写一下:
var makeCounter = function() {
var privateCounter=0;
function changeBy(val) {
privateCounter += val;
}
return {
increment:function() {
changeBy(1);
},
decrement:function() {
changeBy(-1);
},
value:function() {
return privateCounter;
}
}
}
var counter = makeCounter();
为什么难看懂呢?就是因为将上面两步合成了一步。使用句式是:
(function() {....})()
定义闭包和执行闭包一块做了,确实奇特!
其他类似的例子
(function(global) {
var env = global;
(function(destroyInstance) {
destroyInstance( % s)
})(env.destroyInstance)
})(this)
第二层 深入闭包内部
关注一下闭包的返回:
return {
increment:function() {
changeBy(1);
},
decrement:function() {
changeBy(-1);
},
value:function() {
return privateCounter;
}
}
这返回的其实是一个对象。而在js中对象的定义是什么样的呢?
js对象的定义
var lennon={name:"John",year:1940}
只不过上面的demo中,每个value对应的值都是一个闭包而已。
这样写的好处是什么呢?
counter拿到了对象,那么外部空间只能调用到对象的increment,decrement,value属性,但是却无法直接调用到changeBy,privateCounter,他们被隐藏起来了,类似于java类中的private.
使用闭包时的常见问题
循环中创建闭包
问题的关键在于:
- 闭包保留并可访问创建它的环境数据,在这里就是函数setupHelp内的数据。
- 循环结束后,item指向了列表的最后一个。
- 红框中的闭包在实际执行的时候,使用了函数setupHelp当前的item,也就是列表中的最后一个。
- 当三个输入框点击的时候,显示的内容都是最后一个item.hel的内容。
如何解决呢?
- 增加上下文环境来保留与之相对应的item数据,在这里增加了makeHelpCallback函数来封装了一层。
性能问题
除非必须,勿用闭包,对性能影响大。
参考资料
http://edu.youkuaiyun.com/course/detail/800/10909?auto_start=1
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures