2012-07-09 92 views
2

我有一段JavaScript代码,我在“JavaScript忍者的秘密”(John Resig书)中找到。我有一个问题来了解变量的行为。 以下是代码(相对于简化为原来的):变量范围澄清

(function() { 
    var results; 
    this.assert = function assert() { 
     var li = document.createElement("li"); 
     results.appendChild(li); 
     return li; 
    }; 
    this.test = function test(name, fn) { 
     results = document.getElementById("results"); 
     results = assert().appendChild(document.createElement("ul")); 
     fn(); 
    }; 
})(); 
window.onload = function() { 
    test("A test.", function() { 
     assert(); 
     assert(); 
    }); 
}; 

我的问题是结果变量。当您输入“test”函数时,由于appendChild函数的作用,结果变量将首先取值“ul#results”,然后值“ul”。但是当您输入“fn()”函数时,“results”的值仍然是“ul#results”。为什么?我有些难以理解这个变量的范围。

有人可以帮我理解这个话题吗?

非常感谢。

回答

2

该变量是在匿名函数的范围内创建的。 asserttest访问相同results变量。

+0

是的,但是为什么结果变量在输入fn()函数时有不同的值? – user1512966 2012-07-09 20:11:02

+0

为什么你认为在这一点上会有另一个价值? – ThiefMaster 2012-07-09 20:12:08

+0

哎呀!我发现了这个问题。问题是在fn()函数中,变量结果不可见。它只在“test”和“assert”函数中可见,但是我在HTML中有“结果”为“id”的ul元素,然后在萤火虫中,当我悬停结果时看到这个“id”值用鼠标变量。 – user1512966 2012-07-09 20:32:42

0

这可能比任何东西都更令人困惑,但基本上,代码是递归地向DOM中的元素添加一些子元素。这是因为封闭中定义的变量“结果”而成为可能。该变量在该范围内仍然存在。

顺便说一句,这里是一个文章,显示了一些在javascript中解释变量作用域的测试。请记住,它不会谈论关闭,就像你在这里。但它可能有助于解释你会遇到的其他一些事情。

http://www.computerhowtoguy.com/an-introduction-to-javascript-variable-scope/

这是过程步骤说明步骤:

  1. 可以在window.onload函数的代码将具有 “窗口” 的水平 范围。如果你输出“this”,你会得到“窗口” 对象。
  2. onload函数调用“test”。 “测试”,这也可能是 写this.test或window.test,是在关闭。其原因是 闭包中的“测试”函数是由于以下行: this.test = ...由于闭包是在窗口级执行的,所以“this”引用“窗口”对象。
  3. 在“test”函数中调用“this”是指“窗口”对象。 由于上面的断言函数被定义为:this.assert = ... (window.assert)它位于窗口对象级别。如果你确实声明了 == window.assert,你会得到“true”,因为它们只是同一个(函数)对象的两个名字。
  4. 运行此行:assert()。appendChild(document.createElement(“ul”)) assert函数在执行之后的任何事情之前执行。所以让我们 进入断言代码...
  5. 在断言“这”仍然是“窗口”对象。创建局部变量“li” ,引用尚未将 附加到文档元素的新DOM元素。然后,我们将这个新的li元素添加到 “results”DOM元素中。现在它是文档DOM 对象的一部分。返回li元素的javascript引用。
  6. 现在回到:assert()。appendChild(document.createElement(“ul”)) appendChild函数被调用,将新的'ul'元素附加到 新创建的'li'元素。接下来,我们的javascript“结果”对象 再次被重新分配,这次是新创建的ul元素。 最后,调用'fn'(我们的匿名函数)。
  7. 进入“fn”...“这个”仍然指窗口。断言被调用, 断言仍然是对window.assert函数的引用创建一个新的 li元素并将其附加到“results”变量。记住,最后一件“结果”被分配到的是ul 元素,所以我们正在为它添加一个新的li。

的DOM结构将在这一点上是这个样子:

<div id="results"> 
    <li> 
     <ul> 
      <li></li> 
     </ul> 
    </li> 
</div> 

而且使代码与同样的事情上移动...

这是代码修改后,现在有评论:

// the following is a closure. a sort of isolated container with its own scope. 
(function() { 
    // results is not globally scoped, only scoped at the closure level, 
    // since its defined with "var". 
    var results; 

    // "this" is the calling object. (ie: window object) 
    this.assert = function assert() { 

     // since "var" is used "li" is part of this function. 
     var li = document.createElement("li"); 

     // results (at the closure level) appends a child at this function's level. 
     results.appendChild(li); 

     // return a javascript reference to the new DOM element. 
     return li; 
    }; 

    // again "this" is the calling object. when called in onload below, "this" is the window object. 
    this.test = function test(name, fn) { 

     // results refers to the closure level results variable, since var is ommitted. 
     // this is a reference to an element in the DOM. 
     results = document.getElementById("results"); 

     // changing the variable now. the DOM object "results" is NOT altered by this assignment, since 
     // javascript is separate from the DOM. 
     // NOTE: the assert function was previously assigned to the window object previously. so stuff in that 
     // function will be window scoped. 
     results = assert().appendChild(document.createElement("ul")); 

     // call fn 
     fn(); 
    }; 
})(); 

window.onload = function() { 

    // at this point, "this" is the "window" object. 
    // "test" is part of the closure above. in the closure the test function is assigned 
    // to "this". since we are calling the function here, "this" will be the window object in the 
    // closure for this call. 
    test("A test.", 
     // an anonymous function. this is really just an object passed into the "test" function 
     function() { 
      assert(); 
      assert(); 
     } 
    ); 
};