首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > JavaScript >

Javascript 的词法作用域、调用对象跟闭包

2012-10-30 
Javascript 的词法作用域、调用对象和闭包function f(x) {var g function () { return x }return g}var

Javascript 的词法作用域、调用对象和闭包
function f(x) { var g = function () { return x; } return g;}var g1 = f(1);alert(g1()); //输出 1

假设我们把全局看成类似以下这样的一个大匿名函数:

(function() {    //这里是全局范围})();


那么例子就可以看成是:
(function() {    function f(x) {        var g = function () { return x; }        return g;    }    var g1 = f(1);    alert(g1());  //输出 1})();


   0. 全局的大匿名函数被定义的时候,它没有外层,所以它的作用域链是空的。
   1. 全局的大匿名函数直接被执行,全局的作用域链里只有一个 '全局调用对象'。
   2. 函数 f 被定义,此时函数 f 的作用域链是它外层的作用域链,即 '全局调用对象'。
   3. 函数 f(1) 被执行,它的作用域链是新的 f(1) 调用对象加上函数 f 被定义的时候的作用域链,即 'f(1) 调用对象->全局调用对象'。
   4. 函数 g (它要被返回给 g1,就命名为 g1吧)在 f(1) 中被定义,它的作用域链是它外层的函数 f(1) 的作用域链,即 'f(1) 调用对象->全局调用对象'。
   5. 函数 f(1) 返回函数 g 的定义给 g1。
   6. 函数 g1 被执行,它的作用域链是新的 g(1) 调用对象加上外层 f(1) 的作用域链,即 'g1 调用对象->f(1)调用对象->全局调用对象'。

这样看就很清楚了吧。
闭包 Closuer

闭包的一个简单的说法是,当嵌套函数在被嵌套函数之外调用的时候,就形成了闭包。

之前的那个例子其实就是一个闭包。g1 是在 f(1) 内部定义的,却在 f(1) 返回后才被执行。可以看出,闭包的一个效果就是被嵌套函数 f 返回后,它内部的资源不会被释放。在外部调用 g 函数时,g 可以访问 f 的内部变量。根据这个特性,可以写出很多优雅的代码。

例如要在一个页面上作一个统一的计数器,如果用闭包的写法,可以这么写:

var counter  = (function() {    var i = 0;    var fns = {"get": function() {return i;},               "inc": function() {return ++i;}};    return fns;})();//do somethingcounter.inc();//do something elsecounter.inc();var c_value = counter.get();  //now c_value is 2


这样,在内存中就维持了一个变量 i,整个程序中的其它地方都无法直接操作 i 的值,只能通过 counter 的两个操作。

在 setTimeout(fn, delay) 的时候,我们不能给 fn 这个函数句柄传参数,但可以通过闭包的方法把需要的参数绑定到 fn 内部。

for(var i=0,delay=1000; i< 5; i++, delay +=1000) {    setTimeout(function() {        console.log('i:' + i + " delay:" + delay);    }, delay);}


这样,打印出来的值都是
delay:6000i:5 delay:6000i:5 delay:6000i:5 delay:6000i:5 delay:6000


改用闭包的方式可以很容易绑定要传进去的参数:
for(var i=0, delay=1000; i < 5; i++, delay += 1000) {    (function(a, _delay) {         setTimeout(function() {             console.log('i:'+a+" delay:"+_delay);        }, _delay);    })(i, delay);}


输出:
i:0 delay:1000i:1 delay:2000i:2 delay:3000i:3 delay:4000i:4 delay:5000


闭包还有一个很常用的地方,就是在绑定事件的回调函数的时候。也是同样的道理,绑定的函数句柄不能做参数,但可以通过闭包的形式把参数绑定进去。
总结

   1. 函数的词法作用域和作用域链是不同的东西,词法作用域是抽象概念,作用域链是实例化的调用对象链。
   2. 函数在被定义的时候,同时也是它外层的函数在被执行的时候。
   3. 函数在被定义的时候它的词法作用域就已经确定了,但它仍然是抽象的概念,没有也不能被实例化。
   4. 函数在被定义的时候还确定了一个东西,就是它外层函数的作用域链,这个是实例化的东西。
   5. 函数在被多次调用的时候,它的作用域链都是不同的。
   6. 闭包很强大。犀牛书说得对,理解了这些东西,你就可以自称是高级 Javascript 程序员了。因为利用好这些概念,可以玩转 Javascript 的很多设计模式。

-EOF-

热点排行