JavaScript 里的闭包是什么? 有何应用?

看一个例子:


var foo = ( function ( ) {
    var secret = 'secret';
    // “闭包”内的函数可以访问 secret 变量,而 secret 变量对于外部却是隐藏的
    return {
        get_secret: function ( ) {
            // 通过定义的接口来访问 secret
            return secret;
        },
        new_secret: function ( new_secret ) {
            // 通过定义的接口来修改 secret
            secret = new_secret;
        }
    };
} ( ) );


foo.get_secret ( ); // 得到 'secret'
foo.secret; // Type error,访问不能
foo.new_secret ('a new secret'); // 通过函数接口,我们访问并修改了 secret 变量
foo.get_secret ( ); // 得到 'a new secret'


引用 Douglas Crockford [1] :


之所以可能通过这种方式在 JavaScript 种实现公有,私有,特权变量正是因为闭包,闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。




需要注意的一点时,内部函数访问的是被创建的内部变量本身,而不是它的拷贝。所以在闭包函数内加入 loop 时要格外注意。另外当然的是,闭包特性也可以用于创建私有函数或方法。


--------
关于为什么在 JavaScript 中闭包的应用都有关键词“return”,引用 JavaScript 秘密花园中的一段话:


闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。


JavaScript 中的闭包与其 Scope Chain 特性真是密不可分的.
JavaScript 中的闭包:
例子:
def foo() {
    var a = 1;
    def bar() {
        a = a + 1;
        alert(a);
    }
    return bar;
}


var closure = foo();  // 这个时候返回的是 bar() 这个函数外加其包上的变量 a;
var closure2 = foo(); // 这里同样生成了另外一个闭包(实例)
closure(); // 2
closure2(); // 2 , 绑定了另外一份变量 a
closure(); // 3


对于常规的 foo() 方法来说, 在其内部的变量 a 的存在应该在 foo() 方法执行完毕以后就消失了, 但是 foo() 方法返回了一个新的方法 bar(), 而这个方法却访问到了 foo() 方法的变量 a (JavaScript 通过 Scope Chain 访问到父级属性), 而方法 bar() 的存在延长了变量 a 的存在时间, 类似与将变量 a 关闭在了自己的作用域范围内一样, 只要方法 bar() 没有失效, 那么变量 a 则会一直伴随着方法 bar() 存在, 而变量 a 与方法 bar() 的这样存在形式被称为闭包; 


闭包的应用:  
在我看来, 虽然时常听到闭包这个概念, 但真正的应用还真不是很多, 也没看到或者想到比较经典的应用.  在 JavaScript 中, 使用得最多的, 恐怕还是将 function 作为 first class 的情况使用, 就好比你可以 var a = new Number(0); 可以将 a 当作函数的参数不断的进行传递, 你也可以 var f = new Function(arg1, arg2...., functionBody) [new Function("x", "y", "return (x + y)/2")] 来定义函数, 然后将 f 作为参数在函数中不断的传递; 但我一般不会让这个 function 产生闭包来进行传递, 而是会传递一个独立的 function 避免写多了自己都忘记是哪个了.


JavaScript 闭包的实现:
如开篇所说, JavaScript 中的闭包实现与 JavaScript 的 Scope Chain 是密不可分的. 首先在 JavaScript 的执行中会一直存在一个 Execute Context Stack (想想 JavaScript 解释器在看到一个 alert(x) 的时候, 如果没有上下文他怎么知道这个 x 是什么?), Execute Context Stack 中最下面一个一定是 GlobalContext, 而在每一个函数的执行开始就会向这个 stack 中压入一个此 Function 的 Execution Context; 而一个 Execution Context 的组成分为三部分: 
1. Variable Object:  存储方法内的变量 vars, 方法传入的参数, 函数内定义的函数等等(函数表达式不保存), Variable Object 在任何时候是不可以被直接访问到的, 当然不同的 JS 引擎提供了访问接口就说不定了;
2. Scope Chain: 这个函数执行的时候用以寻找值的 Scope Chain, 这个 Scope Chain 由 Variable Object + All Parent Scopes 组成, Variable Object 会放在这个 Scope Chain 的最前面, 这也是为什么函数内的变量会被最先找到;
3. thisValue, 函数被调用的时候的 this 对象, 存储的就是函数的调用者(caller)的引用; 


对于 Variable Object 在不同的情况下会有不同的定义, 例如在全局的时候被称为 Global Object, 而在函数中则被称为 Activation Object 激活对象; 


正是由于有了 Execution Context 中的 Scope Chain, JavaScript 才能够使得在方法 bar() 
的内部访问到方法 foo() 中的变量 a, 才能够使方法 bar() 将变量 a 关闭在自己的作用范围内不让他随 foo() 方法的执行完毕而销毁;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值