对javascript闭包的理解

1,722 阅读4分钟

  在工作中在很多情况下都会用到闭包,但是当别人问起闭包到底是什么时,总是觉得说不出个所以然来,所以这次准备把闭包这个知识点好好的梳理一遍,以加深对闭包的理解。而且很多时候我们也容易将匿名函数跟闭包弄混,所以在此区分一下。写得不对的地方欢迎各位大神指出。

1、什么是闭包?

含义: 有权访问另一个函数作用域中的变量的函数——高级js。(闭包是函数和声明该函数的词法环境的组合。当内部函数以某一种方式在任何一个外部函数作用域中被访问时,一个闭包就产生了——MDN.当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这事就产生了闭包)。
创建方式: 在一个函数内创建另一个函数(只要使用了回调函数,实际上就是在使用闭包)。
目的: 主要是为了设计私有的方法和变量。
优点: 可以避免全局变量的污染。
缺点: 1、闭包会常驻内存(闭包会携带包含它的函数的作用域),会增大内存使用量,使用不当很容易造成内存泄露。2、闭包只能取得包含函数中任何变量的最后一个值。
特性:
 1.函数嵌套函数;
 2.函数内部可以引用外部的参数和变量;
 3.参数和变量不会被垃圾回收机制回收;
 4.在执行环境中,闭包的作用域包含着它自己的作用域、包含函数的作用域和全局作用域;
 5.当函数返回了一个闭包时(闭包保存的是整个变量对象),该函数的执行环境的作用域链会被销毁,但是这个函数中的活动对象将会一直保存到闭包不存在为止。
 6、闭包只能取得包含函数中任何变量的最后一个值。

闭包例子1:

    function test(){
        var x=10;
        function foo(){
            x*=2;
            return x;
        }
        return foo;
    }
    //或者
    function test(){
        var x=10;
        return  function(){
            x*=2;
            return x;
        };
    }
    var y=test();
    for(var i=0;i<4;i++){
    	console.log(y());//20、40、80、160
    }

  结果分析:上述代码是一个闭包。我们在test()函数后,返回了一个指向函数foo对象的指针——foo(函数是对象,函数名实际上是一个指向函数对象的指针)。所以此时变量y其实就是一个指向函数foo对象的指针。但我们调用y()也就是在调用foo(),在函数foo()内,变量x可以看成是相对foo()函数的全局变量。所以每次调用foo()函数后,函数内的X对象的内存不会被销毁,所以会出现每次调用y()的结果是前面的两倍。

闭包例子2:

    function createFunctions() {
    	var result = new Array();
    	for(var i = 0; i < 10; i++) {
    		result[i] = function() {
    			return i;
    		};
    	}
    	return result;
    }
    var result=createFunctions();
    for(var i=0;i<result.length;i++){
    	console.log(result[i]());/10个10
    }

  结果分析:result是一个保存10个函数的数组。根据闭包的特性可知,i可以看成是resulti函数的全局变量。所以循环10次后,i变成了10(最后一次i++等于10,循环条件不成立,但i作为一个相对全局变量,在result[i]中的值最后都是10)。

闭包例子3:

function handler(){
    var dom=document.getElementById("test");
    dom.onclick=functin(){
        process(dom.id);
    }
}
//推荐
function handler(){
    var dom=document.getElementById("test");
    var id=dom.id
    dom.onclick=functin(){
        process(id);
    }
    dom=null;
}

  总结:在定时器、事件监听器、Ajax 请求、跨窗口通信、 Web Workers 或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包。

2、什么是匿名函数?

  顾名思义,匿名函数就是没有名称的函数,由编译器指定名称并分配空间,通常直接作为参数传递。

匿名函数的形式如下:

//形式1 函数表达式
var test=function(){
    return 20;
}
//形式2 立即执行函数
(function(i){
    rentun i
})(5);
形式2可以变成如下:
var =testfunction(i){
    return i;
}
test(5);

  立即执行函数的模式是为了保护函数体中的数据不被外界访问,以免被外界环境污染,同时还不会在内存中留下对该函数的引用。

不同点: 匿名函数是指没有指定函数名称的函数,而闭包有权访问另一个函数作用域中的变量的函数。有时候在闭包中使用匿名函数。
相同点: 他们都可以设计私有的方法和变量(如立即执行函数)、都可以避免全局变量的污染。