深入理解JavaScript闭包

深入理解,Javascript,js,闭包,深入理解Javascript,js闭包

        简言之,理解JavaScript闭包(Closure)很多问题的关键是JavaScript是解释型的语言,闭包只有在调用的时候才进行解析。


        维基百科上对闭包的定义是: Closure (also lexical closure or function closure) is a function together with a referencing environment for the non-local variables of that function.用我拙劣的语言翻译过来就是闭包(又称“词法闭包”或“函数闭包”)是一个包含了非本地变量引用环境的函数。

        闭包其实就是一个函数;如果一个函数访问了它的外部变量,那么它就是一个闭包。一个典型的例子就是全局变量的使用。所以从技术上来讲,在Javascript中,每个function都是闭包,因为它总是能访问在它外部定义的变量。


示例:

        首先来看一个简单的例子:

function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
var sayAlert = say667();
sayAlert()

        执行结果应该弹出667而不是666,这个应该很好理解。再来看一个容易迷惑的经典例子:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result.push( function() {alert(item + ' ' + list[i])} );
    }
    return result;
}
function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

        testList的执行结果是弹出item3 undefined窗口三次。因为这三个闭包是在同一个外部函数中定义的,item的值为最后计算的结果,但是当i跳出循环时i值为3,所以list[3]的结果为undefined.


将引用变为拷贝

        理解问题的关键是,Javascript是一门解释型的语言,一个函数内部定义的另一个函数(即闭包)只有在调用的时候才进行解析闭包中引用的变量的值是此时内存中的值buildList函数中定义闭包时,使用了参数"list"以及内部变量"i"引用,而不是拷贝。因此只有当闭包执行时,也就是在testList函数中调用时,才会开始引用list和i的值并输出;而此时i的值为4,结果可想而知了!

        为了达到预期的效果,我们来改造一下buildList函数,而改造的关键是在每次循环中创建变量i的拷贝,也就是将引用变为拷贝​一种简单的方法就是使用自执行的“匿名函数”来对闭包进行包裹:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        (function(r){
            var item = 'item' + list[r];
            result.push( function() {alert(item + ' ' + list[r])} );
        })(i);
    }
    return result;
}

        这样,在函数buildList执行的时候,匿名函数会立即执行,并把i作为参数;此时匿名函数内部的变量r相当于有了i的一个拷贝,而r的值是不会被外部的循环改变的。因此函数testList的执行结果是分别弹出“item1 1”、“item2 2”、“item3 3”。​


你理解了吗?

        要小心的是,在Javascript函数参数传递的时候,只有基本类型的参数会被拷贝,对象类型的参数传递的是引用。因此,如果给匿名函数传递对象类型的参数时(没有人会这么做吧!),要小心出现意外的情况;举个变态的例子:

function buildList(list) {
    var result = [];
    var obj = {};
    for (obj.i = 0; obj.i < list.length; obj.i++) {
        (function(r){
            var item = 'item' + list[r.i];
            result.push( function() {alert(item + ' ' + list[r.i])} );
        })(obj);
    } 
    return result;
}

        函数testList的执行结果是什么呢?是分别弹出“item1 undefined”、“item2 undefined”、“item3 undefined”​​窗口,跟前面两种写法的结果都不一样。原因是匿名函数立即执行后,其内部变量item被正确赋值,等到testList函数运行时,闭包中引用的r.i其实就是obj对象的i变量,它的值当然是3,结果就可想而知了。

        Javascript闭包,你理解了吗?​


  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值