2010-11-04 88 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