《JavaScript设计模式与开发》笔记 4.闭包

  • 1.变量的作用域
  • 2.变量的生存周期
  • 3.闭包更多作用
    • 1.封装变量
    • 2.延续局部变量寿命
  • 4.闭包和面向对象设计
  • 5.闭包和内存管理

1.变量的作用域

var func = function(){
var a = 1;
}
func();
console.log(a); //输出undefined
var a = 1;
fun1 = function(){
var b = 2;
fun2 = function(){
var c = 3;
console.log(b); //输出 2
console.log(a); //输出 1
}
fun2();
console.log(c); //输出undefined
}
fun1();

2.变量的生存周期

var func = function(){
var a = 1;
return function(){
a++;
console.log(a);
}
}
var f = func();
f(); // 输出1
f(); // 输出2
f(); // 输出3
f(); // 输出4

当退出函数后,局部变量a并没有消失,而是似乎一致在某个地方存活着。这是因为当执行var f = func();时,f返回了一个匿名函数的引用,它可以访问到func()被调用时产生的环境,而局部变量a一致处于这个环境里。既然局部变量所在的环境还能被外界,这个局部变量就有了不被销毁的理由。这里产生了一个闭包结构,局部变量的声明看起来被延续了。

既然f返回了一个匿名的函数引用,那么下面的也符合闭包

var func = (function(){
var a = 1;
return function(){
a++;
console.log(a);
}
})();
f(); // 输出1
f(); // 输出2
f(); // 输出3
f(); // 输出4

再次变种为引用对象的方式

var func = {
a :0,
call:function(){
this.a++;
console.log(this.a);
}
}
func.call(); //输出1
func.call(); //输出2
func.call(); //输出3

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
var nodes = document.getElementsByTagName('div');
for(var i =0;i<nodes.length;i++){
nodes[i].onclick=function(){
alert(i);
}
}
</script>
</body>
</html>

测试这段代码就会发现,无论点击哪个div,最后弹出的结构都是5,。这是因为div节点onclick事件是被异步出发的,当事件出发的时候,for循环早已结束,此时变量i的值已经是5,所以在div的onclick事件函数中顺着作用域链从内到外查找变量i时,查找到的值总是5。

解决方法是在闭包作用的帮助下,把每次循环的i值都封装起来。(就是把每个点击事件都独立放在内存里面。)

var nodes = document.getElementsByTagName('div');
for(var i =0;i<nodes.length;i++){
(function(i){
nodes[i].onclick=function(){
alert(i);
}
})(i);
}

3.闭包更多作用

1.封装变量

闭包可以帮组我们封装私有变量”

var mult = function(){
var a = 1;
for(var i =0;i <arguments.length;i++){
a = a*arguments[i];
}
return a;
}
console.log(mult(1,2,5));

mult函数接受一些number类型的函数,并返回这些参数的乘积。现在我们觉得对于这些那些参数来说,每次都进行计算是一种浪费,我们可以加入缓存机制来提高这个函数的性能。

var cache={};
var mult = function(){
var args = Array.prototype.join.call(arguments,','); //将参数对象变成一个字符串 args 1,2,3
if(cache[args]){
console.log('执行缓存');
return cache[args];
}
var a = 1;
for(var i=0;i<arguments.length;i++){
a = a*arguments[i];
}
return cache[args] = a;
}
console.log(mult(1,2,3)); //进行计算
console.log(mult(1,2,3)); //执行缓存,不用计算

继续封装减少页面全局变量

var mult = (function(){
var cache={}; //私有变量
return function(){
var args = Array.prototype.join.call(arguments,',');
if(cache[args]){
return cache[args];
}
var a = 1;
for(var i=0;i<arguments.length;i++){
a = a*arguments[i];
}
return cache[args] = a;
}
})()
console.log(mult(1,2,3)); //进行计算
console.log(mult(1,2,3)); //执行缓存,不用计算

提炼代码,独立模块

var mult = (function(){
var cache={}; //私有变量
var cala = function(){
var a = 1;
for(var i=0;i<arguments.length;i++){
a = a*arguments[i];
}
return a;
}
return function(){
var args = Array.prototype.join.call(arguments,',');
if(cache[args]){
return cache[args];
}
return cache[args] = cala.apply(null,arguments);
}
})();
console.log(mult(1,2,3)); //进行计算
console.log(mult(1,2,3)); //执行缓存,不用计算
2.延续局部变量的寿命
var report = function(src){
var img = new Image();
img.src = src;
}

利用闭包来延续img的使用

var report = (function(){
var imgs = []; //img就留在了内存里面
return function(src){
var img = new Image();
imgs.push(img);
img.src = src;
}
})();

4.闭包和面向对象

闭包:

var extent = function(){
var value =0;
return {
call:function(){
value++;
console.log(value);
}
}
}
var extent = extent();
extent.call(); //输出1
extent.call(); //输出2
extent.call(); //输出3

如果换成面向对象的写法就是

var extent = {
value :0,
call:function(){
this.value++;
console.log(this.value);
}
}
extent.call(); //输出1
extent.call(); //输出2
extent.call(); //输出3

5.闭包和内存管理

闭包是一个非常强大的特性,但人民对其他诸多误解,一种耸人听闻的说法就是闭包会造成内存泄漏。如果在将来需要回收这些变量,我们可以手动吧这些变量设置为null。

上一篇:基于 twemproxy 搭建 redis 集群


下一篇:vertical-align 笔记