2010-11-04 123 views
4

對不起,如果這已被回答,但我在這裏找不到合適的答案。模塊化Javascript中的變量範圍

我已經開始用模塊化風格編寫我的JavaScript代碼,並且我有一個關於模塊變量作用域的問題。

下面的代碼給了我一個相互衝突的答案。

我有一個名爲Base的模塊,聲明瞭兩個字符串和一個數組。它還有一個名爲fetchData的函數,它使用jQuery getJSON快捷方式將這些變量設置爲服務器數據。不幸的是,當我詢問Base的string1或string2時,我得到了未定義的。我知道這可能是由於我已經設置了深層的兩個函數(在AJAX回調和fetchData內)的值,並且該作用域限制它看不到Base.string1和Base.string2。

但是,當我從模塊外部查看Base.array1時,它將設置爲從服務器提取的適當數據,即使它的設置與字符串的範圍相同。

下面的代碼:

namespace.Base = (function(){ 
    var string1, string2, array1 = []; 
    function fetchData(){ 
     $.getJSON('backendScript.php', function(data){ 
      string1 = data.string1; 
      string2 = data.string2; 
       arrayCount = data.arr.length; 
       for(var i = 0; i<arrayCount; i++){ 
        array1[i] = data.arr[i]; 
       } 
     }) 
    } 
    return{ 
     fetchData: fetchData, 
     string1: string1, 
     string2: string2, 
     array1: array1 
    } 
})(); 

如果我改變

string1 = data.string1; 

namespace.Base.string1 = data.string1; 

它的工作原理是我想要的。

所以我的問題是,爲什麼array1的設置與字符串的範圍相同?

此外,在模塊的函數中設置模塊級變量而不必提供全局路徑(例如namespace.Base.string1)有什麼補救措施?

+0

記住要給予好評*所有*答案這對你有幫助。檢查哪一個最能解答你的問題。如果沒有一個是「值得檢查」的,那麼只需對所有有幫助的答案進行upvotes。 – 2010-11-04 15:55:25

+0

我是新來的,所以我會盡快獲得15點聲望。 – Dan 2010-11-05 00:46:58

+0

嗯,好點。歡迎來到Stack Overflow。 – 2010-11-06 23:53:41

回答

4

的問題是,你實際上有兩個不同的引用,你調用創建namespace.Base匿名函數的閉包內的變量string1namespace.Base.string1,這是將對象從匿名函數返回。將變量string1分配給對象屬性string1是一次性設置,而不是實時參考。變量string1的進一步修改不會影響對象屬性。這裏有你想要什麼:

namespace.Base = (function() { 
    var my = { 
    string1: null, 
    string2: null, 
    array1: [], 
    fetchData: function() { 
     $.getJSON('backendScript.php', function(data){ 
     my.string1 = data.string1; 
     my.string2 = data.string2; 
     var arrayCount = data.arr.length; 
     for (var i = 0; i < arrayCount; i++){ 
      my.array1[i] = data.arr[i]; 
     } 
     }); 
    } 
    }; 
    return my; 
})(); 

現在的namespace.Base地方,但市民,大家都在對象my。您可以在匿名函數中使用var創建私有變量,或者通過將其添加到my來創建更多公共屬性。

+0

謝謝!我可以理解我的變量在被fetchData設置之前如何返回。我錯誤地認爲它們是實時引用或像對象的屬性。我開始認爲模塊模式不值得使用,而不是創建對象。我理解有私有變量和選擇哪些變量允許通過API訪問(返回)的概念,但似乎要創建動態變量,我必須使用對象模式。 – Dan 2010-11-04 08:41:45

+0

另外,在創建模塊時是否創建了一個名爲「my」的對象作爲標準練習?我似乎記得以前看過,並沒有完全理解它。 – Dan 2010-11-04 08:42:57

+0

我經常爲此使用'my'。有時候會使用'that'或其他詞語。我認爲'我的'更清楚一些。確實模塊模式不完善,因爲您仍然必須使用對象來保持對公共屬性的本地引用。無論您是否使用它,取決於您,但私人財產有時候很方便。 – bcherry 2010-11-04 18:29:56

0

您的「範圍」問題實際上並不是範圍問題。問題是數組是指向其數據的指針,而字符串則不是。

namespace.Base被設置爲匿名函數的結果(返回值)。 - 它被設置爲一個包含函數ref(fetchData)的對象,兩個空字符串和一個數組。

如果您稍後調用fetchData函數,那麼它將更改array1的內容。 但它也會創建兩個新的字符串(來自data.string1和data.string2)。string1和string2(它們是namespace.Base.string1和namespace.Base.string2)的舊值不會更改。所以他們被留作空字符串(不是你想要的)。

這是一個例子。嘗試在Firebug--

s1 = "Hi"; 
s2 = s1; // s2 => "Hi" 
s1 = "Bye" 
alert(s2); // *** s2 is still "Hi", it was not changed! 

// But arrays are different: 
a1 = ["Hi"]; 
a2 = a1; 
a1[0] = "Bye"; 
alert(a2[0]); // a2[0] is now "Bye" 

新增:非同步計時誤差

另外,請注意你的代碼是錯誤的,因爲寫的,因爲你沒有給主叫方沒有辦法知道Ajax調用時已完成:

namespace.Base.fetchData(); // starts the Ajax call via getJSON method 
var a = namespace.Base.array1; // ERROR!! The value of namespace.Base.array1 is 
           // indeterminate since you don't know if the 
           // the Ajax request has completed yet or not! 

你似乎是試圖將異步Ajax調用(它調用回調函數,一旦答案已經從遠程服務器接收)到一個同步調用,不會返回,直到將R轉換結果已收到。

這是一個非常糟糕的主意。 (如果您想了解更多,請在SO另一個問題。)

+0

謝謝!開始更好地理解閉包,但我也不知道數組以這種方式工作的事實......非常有見地的例子。 – Dan 2010-11-04 07:35:22