2014-03-04 27 views
0

我在繞過javascript函數式編程模式時遇到了一些麻煩,特別是與變量作用域有關。我在網上找到的大部分內容都很有幫助;但事情仍然沒有點擊。考慮下面的例子:javascript功能對象示波器

<!DOCTYPE html> 
<html> 
<head> 
    <script type="text/javascript" src="/d3.v3.min.js"></script> 
</head> 
<body> 
<script type="text/javascript"> 
    function SimpleWidget(spec) { 

     var instance = {}; 

     var headline, description; 

     instance.render = function() { 
      var div = d3.select('body').append("div"); 

      div.append("h3").text(headline); 

      div.attr("class", "box") 
        .attr("style", "color:" + spec.color) 
        .append("p") 
        .text(description); 

      return instance; 
     }; 

     instance.headline = function (h) { 
      if (!arguments.length) return headline; 
      headline = h; 
      return instance; 
     }; 

     instance.description = function (d) { 
      if (!arguments.length) return description; 
      description = d; 
      return instance; 
     }; 

     return instance; 
    } 

    var widget = SimpleWidget({color: "#6495ed"}) 
      .headline("Simple Widget") 
      .description("This is a simple widget demonstrating functional javascript."); 
    widget.render(); 
</script> 
</body> 
</html> 

這個模式很有用,因爲它封裝了一個可重用的代碼片段。我們可以重構SimpleWidget()函數以獲取元素id,並使用一些簡單的jQuery代碼更新渲染函數來更新dom,從而能夠使用它來更新任何給定的元素。很有用,但我對它的工作原理並不瞭解。

小部件對象如何訪問傳入變量'spec'({color:「#6495ed」})?看看方法鏈,我期望'widget'只有3個函數,但它也可以訪問simpleWidget()函數的內部範圍變量。

通過chrome調試程序運行此示例顯示,widget對象內部的渲染函數具有一個用於存放描述,實例和spec變量的「函數範圍」;但這似乎神奇地發生。我不清楚這些內部變量是如何在將它們的包含函數賦值給一個變量時傳遞的,隨後調用它的函數。

當我的頭腦和描述函數都返回實例對象時,render()函數是如何訪問spec變量的,這讓我非常驚訝。我希望有人能在這裏指出我正確的方向。

+0

這與函數式編程無關。相反,你想[瞭解什麼是閉包](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Bergi

回答

1

這正是關閉的功能。當你在JavaScript中定義一個函數時,它將永遠有權訪問其父作用域中的變量。因此,當您將widget定義爲返回的實例時,那些作爲widget屬性分配的功能會「知道」spec變量,因爲它在父範圍中可用。

換句話說,SimpleWidget被調用,spec變量綁定到您傳入的參數,然後定義一個新函數並將其分配給變量render。即使在SimpleWidget返回之後,該函數也會始終知道父範圍的變量規範。

0

實際上,您在全局範圍中創建的函數可以互相調用。一個函數有一個符號表,它告訴它在變量中查找內存的位置,並且函數中定義的每個函數都會得到一個鏈接,以便它們可以引用它們的對等體。

如果您以後在SimpleWidget()之外改變了render(),它將不會有這個鏈接,並且不知道如何訪問'spec'。由於您的示例中的render()是在SimpleWidget()內定義的,因此它有一個鏈接返回到創建它的SimpleWidget()的調用,並且可以訪問與該調用相關的內存位置。