JavaScript闭包

啥是闭包?

一个闭包就是一个可以访问外部函数作用域链的一个内部函数。闭包有三个作用域链:它可以访问自己的作用域(即当前的大括号内),它也可以访问外部函数的变量,它还可以访问全局变量。

闭包不仅可以访问外部函数的变量,还可以访问外部函数的参数。注意:尽管内部函数可以直接访问外部函数的参数,但并不能调用外部函数的arguments 对象。

我们可以通过在一个function里增加一个function来创建一个闭包。

一个简单的闭包示例 in JavaScript:

function showName (firstName, lastName) {

​var nameIntro = "Your name is ";
    // this inner function has access to the outer function's variables, including the parameter​
    ​function makeFullName () {

​       return nameIntro + firstName + " " + lastName;

    }
​
​return makeFullName ();

}

​
showName ("Michael", "Jackson"); // Your name is Michael Jackson

当function里嵌套function时,内部的function可以访问外部function里的变量。

function foo(x) {
    var tmp = 3;
    function bar(y) {
        alert(x + y + (++tmp));
    }
    bar(10);
}
foo(2)

   不管执行多少次,都会alert 16,因为bar能访问foo的参数x,也能访问foo的变量tmp。

但,这还不是闭包。当你return的是内部function时,就是一个闭包。内部function会close-over外部function的变量直到内部function结束。

function foo(x) {
    var tmp = 3;
    return function (y) {
        alert(x + y + (++tmp));
    }
}
var bar = foo(2); // bar 现在是一个闭包
bar(10);

      上面的脚本最终也会alert 16,因为虽然bar不直接处于foo的内部作用域,但bar还是能访问x和tmp。

  但是,由于tmp仍存在于bar闭包的内部,所以它还是会自加1,而且你每次调用bar时它都会自加1.

  (我们其实可以建立不止一个闭包方法,比如return它们的数组,也可以把它们设置为全局变量。它们全都指向相同的x和相同的tmp,而不是各自有一份副本。)

  上面的x是一个字面值(值传递),和JS里其他的字面值一样,当调用foo时,实参x的值被复制了一份,复制的那一份作为了foo的参数x。

  那么问题来了,JS里处理object时是用到引用传递的,那么,你调用foo时传递一个object,foo函数return的闭包也会引用最初那个object!

function foo(x) {
var tmp = 3;
return function (y) {
    alert(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    alert(x.memb);
    }
}
var age = new Number(2);
var bar = foo(age); // bar 现在是一个引用了age的闭包
bar(10);

 

jQuery中一段经典的闭包代码:

$(function() {
    ​
    ​var selections = []; 
    $(".niners").click(function() { // this closure has access to the selections variable​
        selections.push (this.prop("name")); // update the selections variable in the outer function's scope​
    });
​
});

 

闭包的特性及副作用

1. 外部函数返回后,闭包依然可以访问外部函数中的变量

这是闭包中重要的特性之一。是的,你没看错。在js函数执行过程中,内部函数与外部函数使用相同的作用域链。这意味着即使外部函数返回之后,内部函数依然可以访问其中的变量。所以我们可以稍后在程序中调用内部函数。

function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
    // this inner function has access to the outer function's variables, including the parameter​
   function lastName (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return lastName;
}
​
​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​
​
​// The closure (lastName) is called here after the outer function has returned above​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson
2. 闭包保存了对外部函数中的变量的引用

并不是保存实际的变量值。 当闭包被调用之前,如果外部函数的变量发生变化时,闭包会很有意思。闭包的这种特性有很多用途。例如下面这个私有变量的例子(first demonstrated by Douglas Crockford):


function celebrityID () {
    var celebrityID = 999;
    // We are returning an object with some inner functions​
    // All the inner functions have access to the outer function's variables​
    return {
        getID: function ()  {
            // This inner function will return the UPDATED celebrityID variable​
            // It will return the current value of celebrityID, even after the changeTheID function changes it​
          return celebrityID;
        },
        setID: function (theNewID)  {
            // This inner function will change the outer function's variable anytime​
            celebrityID = theNewID;
        }
    }
​
}
​
​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable
3. 闭包很乱

因为闭包可以访问外部函数的变量,所以在for循环中当外部函数的变量发生变化时容易出bug。

// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
      theCelebrities[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }

    return theCelebrities;
}
​
​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​
​var stalloneID = createIdForActionCelebs [0];

console.log(stalloneID.id()); // 103

 

转载于:https://www.cnblogs.com/ckwblogs/p/6051088.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值