请教你关于js对象的问题(闭包)

ricoyu 2010-03-30
JS闭包
1 Lexical Scope(词法范围)
In JavaScript, functions have lexical scope. This means that functions create their environment (scope) when they are defined, not when they are executed.
>>> function f1(){var a = 1; f2();}
>>> function f2(){return a;}
>>> f1();
a is not defined

Inside the function f1() we call the function f2(). Because the local variable a is also inside f1(), one might expect that f2() will have access to a, but that's not the case. At the time when f2() was defined (as opposed to executed), there was no a in sight. f2(), just like f1(), only has access to its own scope and the global scope. f1() and f2() don't share their local scopes.


2 Closures in a Loop
function f() {
   var a = [];
   var i;
   for(i = 0; i < 3; i++) {
     a[i] = function(){
        return i;
     }
   }
   return a;
}

>>> var a = f();
>>> a[0]()
3
>>> a[1]()
3
>>> a[2]()
3

What happened here? We created three closures that all point to the same local variable i. Closures don't remember the value, they only link (reference) the i variable and will return its current value. After the loop, i's value is 3. So all the three functions point to the same value.

function f() {
  var a = [];
  var i;
  for(i = 0; i < 3; i++) {
    a[i] = (function(x){
      return function(){
        return x;
      }
    })(i);
  }
  return a;
}

>>> var a = f();
>>> a[0]();
0
>>> a[1]();
1
>>> a[2]();
2

Here, instead of just creating a function that returns i, you pass i to another self-executing function. For this function, i becomes the local value x, and x has a different value every time.
这个例子很好地说明了functions create their environment (scope) when they are defined。

语法域: 是指定义某个程序段落时的区域
执行域: 是指实际调用某个程序段落时所影响的区域.
闭包:    是指语法域位于某个特定的区域, 具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落.
            这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值(深连结).

function test(){
   for(var i=0;i<5;i++){
       //如果没有这个闭包,不能得到0,1,2,3,4的结果
       // 以为setTimeout是在循环结束后才被"异步"调用的
       (function(j){
           setTimeout(function(){alert(j);}, 100);
       })(i);
   }
}

通过闭包环境绑定修正事件注册时的"this"指针
button1.onclick = (function(owner){
  return function(){
    click_handler.apply(owner,arguments); //此时该事件处理函数中的this不再指向window对象,而是 button1这个对象
  }
})(button1);
ricoyu 2011-05-21
beyond-sunbing 写道
这段代码的目的描述不是很清楚。
实际使用应该是:
//this == obj
var f = obj.show("id", 3);
f();
f();

代码的特点是需要在show函数调用完成后仍然可以访问count参数。
show函数调用时js引擎完成的几个主要的事情如下。
当调用show函数时,js引擎将创建一个活动对象,这个对象将包含函数实参、函数形参、内嵌函数以及内嵌变量等。当经过变量实例化过程后可以将活动对象想象成{arguments:{0:"id", 1: 3, length : 2, callee...}, id : "id", count : 3...}。并将这个对象加入到函数show作用域链的最前端。当定义内部函数时,内部函数的内部属性[[scope]]将指向show函数的作用域链。内部函数作为函数返回值并被外部引用,即构成:f引用show函数定义的内部函数,内部函数的[[scope]]引用show函数执行时创建的活动对象,此时当show函数返回后活动对象依旧被外部通过链的方式引用,js的垃圾回收器也就会回收。也就达到show函数返回后仍然可以访问count参数的目的。

这位仁兄的是正解, 不过理解起来确实比较难, 推荐看 JavaScript高级程序设计第二版, 上面对作用域解析与闭包的问题讲解的比较透彻。
ricoyu 2013-03-30
首先, javascript只有两个作用域, 全局作用域(window对象)和函数作用域, 你在调用show之后, 如果没有返回一个函数, show函数这个作用域马上就会被销毁,即count之类的变量都不存在了,所以你后续的要用到这些变量的代码就不工作了。

而你如果在一个函数(a)调用中再返回一个函数(b),并且b要用到它的外层作用域a里面的变量, 那么此时, 函数a这个作用域不会被销毁, 即count还在, 所以后续调用b时, 你的代码能正常工作。

这样解释会好理解一点?
operation110 2013-04-22
这个问题可以看下jquery的设计,如果你要增加一个方法虽然两个写法都可以,但是后者return这个函数后你可以在这个函数基础上进行连用
如:
第一种只能用$("#a").show();方法。
第二种可用$("#a").show().hide();方法注意到没有后面可以连用其余的方法。
interjc 2014-08-27
var Foo = function(){
  alert('foo');
  return function(){
    alert('bar');
  }
}; //alert 'foo'

var bar = new Foo();
bar(); //alert 'bar'
Global site tag (gtag.js) - Google Analytics