js之认识闭包

本文采用5W1H分析法来看一下闭包。

一、why-----从为什么要引入闭包先来了解一下闭包。

讨论为什么要引入闭包就要先讨论一下js中的作用域链及垃圾回收机制。

熟悉js语言的人都知道js中有作用域的概念和垃圾回收机制。那么我们首先来看一下js中的作用域链

  1. 作用域链:

js中的变量执行环境分为全局执行环境和局部执行环境。当代码在执行时会创建变量对象的一个作用域链。而作用域链简单来说,就是函数在定义的时候创建的上下文执行环境,用于在标识符解析中变量查找。作用域链的前端始终都是当前执行代码所在环境的变量对象即函数自身的本地变量,作用域链的下一个变量对象来自包含环境即父级函数,而在下一个变量对象则来自下一个包含环境,然后一直延续到全局执行环境;全局执行环境的变量对象始终是作用域链的最后一个对象。js中标识符解析就是沿着作用域链一级一级地搜索标识符的过程;搜索过程从作用域链的前端开始逐级向上搜索,直到找到标识符为止。

作用域链只能从下往上查找而不能从上往下查找,举例来说函数内部可以访问到全局变量,而在函数外部不能访问函数内部变量。如下代码:

var n=999;
function f1(){
  alert(n);
}
f1(); // 999

代码中n为全局变量,而在函数内部可以直接访问到。再看以下代码:

function f1(){
   var n=999;
}
alert(n); // 会报错  n is not defined

当在函数外部访问内部变量时会报错。了解了作用域链再来看一下js垃圾回收机制。

2.js中垃圾回收机制:

javascript时一门具有自动垃圾回收机制的语言。一般来说,一个函数在执行开始的时候,会给其中定义的局部变量划分内存空间以便存储他们的值,然后在函数中使用变量,等到函数执行完毕返回了,局部变量就没有存在的必要了,这些变量就被认为是无用的了;因此释放他们的内存供将来使用,下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用。由于作用域链和垃圾回收机制的限制,若需要在函数外部访问函数内部的变量则访问不到。若要想在函数外部访问函数内部的变量怎么访问呢?此时闭包就华丽登场了(*^_^*)。

二、what-----什么是闭包呢从概念来理解

  《javascript高级程序设计第三版》中是这样定义闭包的“闭包是指有权访问另一个函数作用域中的变量的函数”。所有的函数都可以说是闭包。在我理解,简单来说就是在一个函数内部嵌套一个函数,内部函数可以引用外部函数的参数和变量,参数和变量不会被回收,使局部变量能够在全局中访问到,暴露局部变量能够让他的上一层环境访问到。闭包由函数和创建该函数的执行环境构成。当在函数内部嵌套另外一个函数时,并且外部函数将嵌套的函数对象作为返回值返回就是闭包的一种。如下代码实例:

function aaa() {
    var a=1;
    function bbb(){
        a++;
        alert(a);
     }
     return bbb;
 }

 var c=aaa(); c();//2

内部函数bbb可以访问到外部函数中的a变量。当外部函数执行完后,由于内部函数引用了外部函数的变量,因此内部函数的作用域包含了外部函数的活动对象,当外部函数被调用返回后其执行环境被销毁,但内部函数的作用域链依然存在,因其内部函数还未被销毁所以其外部函数的活动对象仍然在内存中,如在bbb中访问a变量依然可以访问到。等其内部函数执行完后作用域链被销毁,释放变量内存。了解了闭包的概念,再来看一下闭包的应用场景。

三、when------什么时候使用闭包?

当希望一个变量长期驻扎在内存中时,此时使用闭包可以使函数执行完后其活动对象仍然在内存中。

四、where----闭包应用在哪?

闭包好处:1、避免全局变量的污染
2、可以模仿块级作用域
3、私有成员的存在

1、闭包可以用来模仿块级作用域。

js中本身没有块级作用域的概念,而使用闭包可以用来模仿块级作用域。

function outputNumbers(count){
        (function(){
            for(var i=0;i<count;i++){
                alert(i);
            }
        })();
        alert(i);//报错  i is not defined
    }

在for循环外部插入一个私有作用域,变量i只能在循环中使用,使用后即被销毁。所以在私有作用域外部访问不到。

2、在循环中直接找到对应的索引。

若页面中有多个li标签,而需要给每个标签添加一个事件,则可以这样做:

for(var i=0;i<aLi.length;i++){
        (function(i){
            aLi[i].onclick=function(){
                alert(i);
            }
        })(i);
    }

如果不使用闭包的话,那么弹出的i为aLi.length的值。

for(var i=0;i<6;i++){
      aLi[i].onclick=function(){
            alert(i);//6
        }
    }

3、用闭包模拟私有方法:

js中本身没有私有成员的概念,所有对象属性都是公有的。闭包可以用来创建私有变量。如下代码:

var aaa = (function(){
        var a = 1;
        function bbb(){
                a++;
                alert(a);
        }
        function ccc(){
                a++;
                alert(a);
        }
        return {
                b:bbb,             //json结构
                c:ccc
        }
})();
aaa.b();     //2
aaa.c()   

4、模块化代码,避免全局变量的污染:

var abc = (function(){      //abc为外部匿名函数的返回值
        var a = 1;
        return function(){
                a++;
                alert(a);
        }
})();
abc();    //2   调用一次abc函数,其实是调用里面内部函数的返回值
abc();    //3

接下来再来看一下闭包的缺点:闭包会使变量始终保存在内存中,如果不当使用会增大内存消耗。还有由于IEjs对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁驻留在内存中的元素。只有关闭浏览器时才会释放内存。如下代码:

function fn(){
    var oDiv = document.getElementById(‘div1‘);//oDiv用完之后一直驻留在内存中
    oDiv.onclick = function () {
        alert(oDiv.id);//这里用oDiv导致内存泄露
    };
}
fn();
//最后应将oDiv解除引用来避免内存泄露
function fn(){
    var oDiv = document.getElementById(‘div1‘);
    oDiv.onclick = function () {
        alert(oDiv.id);
    };
    oDiv = null;
}

由于oDiv.onclick中引用了oDiv.id,互相引用时存在内存泄漏,因此需手动解除引用来避免内存泄漏。

时间: 2024-11-03 03:28:14

js之认识闭包的相关文章

js中的闭包之我理解

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

(转)js中的闭包问题

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

js中的闭包理解

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

js匿名函数闭包

函数声明: function functionName(arg0,arg1){ //函数体 } 函数表达式: var functionName = function(arg0,arg1){ //函数体 } 函数声明和函数表达式之间的主要区别是前者会在代码执行前被加载到作用域中,而后者是在代码执行到那一行的时候才会有定义.另一个区别是函数声明会给函数指定一个名字,而函数表达式则创建一个匿名函数,然后将这个函数赋给一个变量 1 递归 递归函数是在一个函数通过名字调用自身的情况下构成的,例如: fun

js中的闭包理解一

闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实在是比较晦涩,对我来说也是一样. 但是他也是js能力提升中无法绕过的一环,几乎每次面试必问的问题,因为在回答的时候.你的答案的深度,对术语的理解以及js内部解释器的运作方式的描述,都是可以看出你js实际水平的.即使你没答对,也能让考官对你的水平有个评估.那么我先来说说我对js中的闭包的理解. 闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等. 在理解闭包以前.最好能

转载 - 读博客 - 笔记 - JS中的闭包(closure)

原博客标题:JS中的闭包(closure)原博客地址:http://www.cnblogs.com/jingwhale/p/4574792.html 笔记如下: 1. 函数内部声明变量的时候,一定要使用var命令.如果不用的话,实际上是声明了一个全局变量. function outer(){ localVal = 30; return localVal; } outer(); alert(localVal);//30 2. Javascript语言特有的“链式作用域”结构(chain scope

js中的闭包和c#中的闭包

我们 关于闭包,一个老僧长谈的话题:js的闭包俺将的比较多了,而且很详细,俺就不说了,可以看看之前的文章: 我们来对比一下c#中的闭包和js中的闭包: 先看我们的c#代码: static List<Action> fn0() { int result = 0; List<Action> list = new List<Action>(); for (int i = 0; i < 10; i++) { result = i + 1; //这样result相当于一个全

浅谈JS中的闭包

今天 大年初一,祝各位小伙伴们狗年旺旺啊,闲来也没事,只能钻研一下自己的分内之事,也就是作为一个前端码农的身份,得时刻保持学习的态度,温故而知新,每天都给自己一个小目标去完成,日积月累,所想达到的状态,都会有所见长. JS中的闭包,想必,做web开发的程序猿们都有一定的认识吧,不仅仅是js里有这种特性,而且弱语言类型的譬如python里也是有闭包这么一个强大的特性的,对于老司机们来说,闭包可真的是一个很好的东西,但是对于新入门的菜鸟们来说,闭包却是一个难以搞得清楚的特性,很多情况下,会用,但是却

通俗易懂地解释JS中的闭包

1. "闭包就是跨作用域访问变量." [示例一] ? 1 2 3 4 5 6 7 8 9 var name = 'wangxi' function user () {  // var name = 'wangxi'  function getName () {  console.log(name)  }  getName() } user() // wangxi 在 getName 函数中获取 name,首先在 getName 函数的作用域中查找 name,未找到,进而在 user 函

详解js中的闭包

前言 在js中,闭包是一个很重要又相当不容易完全理解的要点,网上关于讲解闭包的文章非常多,但是并不是非常容易读懂,在这里以<javascript高级程序设计>里面的理论为基础.用拆分的方式,深入讲解一下对于闭包的理解,如果有不对请指正. 写在闭包之前 闭包的内部细节,依赖于函数被调用过程所发生的一系列事件为基础,所以有必要先弄清楚以下几个概念: 1. 执行环境和活动对象 ** - 执行环境(execution context)定义了变量或者函数有权访问的其他数据,每个执行环境都有一个与之关联的