首页 > 代码库 > 《JavaScript设计模式与开发》笔记 4.闭包

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

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

1.变量的作用域

var func = function(){    var a = 1;}func();console.log(a); //输出undefinedvar 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();    // 输出1f();    // 输出2f();    // 输出3f();    // 输出4

 

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

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

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

 

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

var func = {    a :0,    call:function(){        this.a++;        console.log(this.a);    }}func.call();   //输出1func.call();   //输出2func.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 =http://www.mamicode.com/0;    return {        call:function(){            value++;            console.log(value);        }    }}var extent = extent();extent.call();   //输出1extent.call();   //输出2extent.call();   //输出3

 

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

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

 

5.闭包和内存管理

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

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