Javascript全局范围分配

时间:2013-03-13 14:52:22

标签: javascript global-variables

我理解全局范围和javascript变量及其一般不受欢迎的问题;并且你到处找到它们。以下(在浏览器中)是等效的:

var foo = 3; // foo === 3, window.foo === 3
bazz = 10; // bazz === 10, window.bazz === 10

在全局范围内使用var关键字声明变量与在代码中的任何位置声明它没有var相同:您的变量被分配给根(窗口)对象。

我经常看到的一种技术(例如设置谷歌分析)是这样的:

var _gaq = _gaq || [];

...我遵循的理由是,如果_gaq已被声明使用,如果不是将其创建为数组。它允许粗心编码不覆盖已分配给全局变量_gaq的任何值。

我不明白为什么会抛出错误:

_gaq = _gaq || [];

它们看起来与我相同:_gaq应该取_gaq的值或者初始化为数组。但它引发了一个引用错误 - 我的问题是:它们为什么不同?

4 个答案:

答案 0 :(得分:5)

您永远不能读取尚未声明的变量,这就是您在最后一种情况下使用表达式_gaq || []尝试的内容。

在这种情况下

 _gaq = _gaq || [];

_qaq之前未被声明,当评估右侧(_gaq || [])时,它会抛出错误。

以下是对这种情况下正在发生的事情的逐步说明:

赋值运算符在规范的section 11.13.1中描述:

  

生产AssignmentExpression : LeftHandSideExpression = AssignmentExpression的评估如下:

     

1。让lref成为评估LeftHandSideExpression的结果     2.让rref成为评估AssignmentExpression的结果     ......

LeftHandSideExpression_gaqAssignmentExpression_gqa || []

因此,首先评估_qaq,这会产生unresolvable reference,因为未声明变量_gaq。此评估不会抛出错误。

然后评估_gqa || []。这是LogicalORExpression,在section 11.11中描述为LogicalORExpression || LogicalANDExpression。在这种情况下,LogicalORExpression(左侧)为_gaqLogicalANDExpression(右侧)为[]。 表达式评估如下:

  

1。让lref成为评估LogicalORExpression的结果   2.让lval成为GetValue(lref)   ...

我们已经知道lref将是一个无法解决的参考,因为未声明_gaq。因此,让我们看看GetValue正在做什么(在section 8.7.1中定义,V是传递给GetValue的值):

  

1。如果Type(V)不是Reference,请返回V   2.让base成为调用GetBase(V)的结果   3.如果IsUnresolvableReference(V),则抛出ReferenceError例外   ...

正如您所看到的,在此过程的第三步中会引发ReferenceError错误,而该错误又通过评估赋值的右侧来执行,这就是抛出错误的位置。< / p>

那么,为什么var _gaq = _gaq || [];不会发生这种情况?

这一行:

var _gaq = _gaq || [];

实际上是

var _gaq;
_gaq = _gaq || [];

因为名为hoisting [MDN]的东西。这意味着在评估_gaq时,会导致无法解析的参考,而是带有值undefined的参考。

(如果变量_gaq已经声明(并且可能有值),则var _gaq将不会产生任何影响。)


如果您想在函数内部全局创建_gaq,请参阅window

,明确
window._gaq = window._gaq || [];

答案 1 :(得分:4)

如果_gaq右侧的=之前未使用var声明,则会引发参考错误。您正在尝试引用不存在的变量。 “魔术”只适用于分配给不存在的变量。

就像说x = y + 1;问题不是不存在的x,而是不存在的y

答案 2 :(得分:1)

这将引发错误,因为在当前执行上下文的上下文链中找不到该变量。访问无法解析的变量将导致错误。

_gaq = _gaq || [];
另一方面,这将尝试解决_gac试图将其作为窗口对象的成员进行查找,结果证明它是全局上下文“持有者”对象。这种情况的不同之处在于它不会抛出错误,但window._gaq将返回undefined,因为在window对象中找不到该属性。

_gaq = window._gaq || [];

因此,由于全局上下文对象是窗口(当谈到浏览器时),如果定义了_gaq,这两个语句将具有相同的效果。当没有定义_gaq时会注意到差异,使用window对象访问它可以获得不会出错的优点。

答案 3 :(得分:1)

这里的基本概念是hoisting,在实践中通常很棘手。变量在函数范围的顶部定义,而赋值仍然发生在定义的位置。

使用此var _gaq = _gaq变量实际上是在之前定义,执行赋值的实际代码行。这意味着当赋值发生时,变量已经在窗口范围内。如果没有_gaq前面的var,则不会发生提升,因此当赋值运行导致引用错误时,_gaq尚不存在。

如果要查看此操作,可以使用以下内容检查_gaq变量何时添加到窗口对象:

function printIsPropOnWindow(propToCheck)
{
    for (prop in window)
    {
        if (prop == propToCheck)
        {
            console.warn('YES, prop ' + prop + ' was on the window object');
            return;
        }
    }
    console.warn('NO, prop ' + propToCheck + ' was NOT on the window object');
}


try {
    var _gaq = function() {
        printIsPropOnWindow("_gaq");
        return a;
    }();
} catch (ex) {
    printIsPropOnWindow("_gaq");
}
_gaq = "1";
printIsPropOnWindow("_gaq");

如果您按原样尝试一次,并且在删除_gaq之前使用var一次,您会看到非常不同的结果,因为其中一个有_gaq悬挂而另一个没有。