惊讶全局变量在JavaScript中具有未定义的值


87

今天,当我看到全局变量undefined在某些情况下具有价值时,我感到非常惊讶。

例:

var value = 10;
function test() {
    //A
    console.log(value);
    var value = 20;

    //B
    console.log(value);
}
test();

输出为

undefined
20

在这里,为什么JavaScript引擎将全局值视为undefined。我知道JavaScript是一种解释语言。如何考虑函数中的变量?

这是JavaScript引擎的陷阱吗?

Answers:


175

这种现象称为:JavaScript变量提升

您绝对不会在函数中访问全局变量。您只访问局部value变量。

您的代码等效于以下内容:

var value = 10;

function test() {
    var value;
    console.log(value);

    value = 20;
    console.log(value);
}

test();

还感到惊讶undefined吗?


说明:

每个JavaScript程序员迟早都会碰到这一点。简单地说,任何变量声明总是悬挂到本地盖顶部。因此,即使您在第一次console.log调用后声明了变量,也仍然会认为它是在此之前声明的。
但是,只有声明部分被吊起;另一方面,分配不是。

因此,当您第一次调用时console.log(value),您引用的是本地声明的变量,该变量尚未分配任何内容。因此undefined

这是另一个例子

var test = 'start';

function end() {
    test = 'end';
    var test = 'local';
}

end();
alert(test);

您认为这会引起什么警报?不,不要只是继续阅读,请考虑一下。有什么价值test

如果您说的不是start,则您错了。上面的代码等效于此:

var test = 'start';

function end() {
    var test;
    test = 'end';
    test = 'local';
}

end();
alert(test);

这样全局变量就不会受到影响。

正如你所看到的,无论在哪里你把你的变量声明中,它始终是悬挂到本地盖顶部。


边注:

这也适用于功能。

考虑这段代码

test("Won't work!");

test = function(text) { alert(text); }

这将为您提供参考错误:

未捕获的ReferenceError:未定义测试

因为这段代码可以正常工作,所以使很多开发人员不满意:

test("Works!");

function test(text) { alert(text); }

如上所述,这样做的原因是悬挂分配部件。因此,在第一个示例中,当test("Won't work!")运行时,test变量已被声明,但尚未分配功能。

在第二个示例中,我们不使用变量赋值。相反,我们使用了正确的函数声明语法,这确实使函数完全提升了。


Ben Cherry在这方面写了一篇很棒的文章,标题为JavaScript Scoping and Hoisting
阅读。它会为您提供整个细节的详细信息。


27
这是一个很好的解释。但是,我错过了一个可以访问函数内部全局变量的解决方案。
2014年

1
@ DuKes0mE-您始终可以在函数内部访问全局变量。
约瑟夫·席尔伯

3
是的,为什么开篇文章中的案例A无法正常工作并说其未定义?我知道,它将被解释为局部变量,而不是全局变量,但是它将如何变为全局变量呢?
2014年

4
访问全球可变用途window.value
Venkat Reddy,

1
这种可变的提升方式是什么时候实施的?这一直是javascript的标准吗?
Dieskim

54

我对这里的问题得到解释感到失望,但没有人提出解决方案。如果要在函数作用域中访问全局变量而又不首先使该函数创建未定义的局部变量,请将该变量引用为window.varName


8
是的,很遗憾其他人没有提出解决方案,因为这是Google结果的第一个结果。他们做了ALMOST回答了实际问题,这是js引擎中的陷阱....叹气->感谢您的回答
user1567453 2015年

2
多数民众赞成在理论知识和完成之间的区别。感谢您的回答!
hansTheFranz '17

对我来说,在任何函数中的任何地方都使用一个全局名称是一个错误。至少造成混乱。最糟糕的是需要Google搜索。谢谢
dcromley '18年

Javascript每天都会让我感到惊讶。谢谢男人,答案很有帮助。
拉夫

感谢您提供的解决方案,我在未定义全局var之后但仅在Safari中发现了这一点。出现了其他“包含”文件和全局变量,例如“谷歌”,所以我复制了谷歌使用的方法:window.globalVarFromJS = window.globalVarFromJS || {}; 然后,我找到了您的解决方案,以为我会加入其中。
拉尔夫·欣克利

10

JavaScript中的变量始终具有函数范围的作用域。即使它们是在函数中间定义的,它们也是以前可见的。通过功能提升可以观察到类似现象。

话虽如此,第一个console.log(value)看到value变量(内部变量遮蔽外部变量value),但尚未初始化。您可以将其视为所有变量声明都隐式移至函数的开头(而不是最里面的代码块),而定义保留在同一位置。

也可以看看


我一直喜欢简单的词+1 :)
Jashwant 2012年

3

有一个全局变量value,但是当控件进入该test函数时,value将声明另一个变量,该变量遮盖了全局变量。由于JavaScript中的变量声明(而不是赋值)被提升到声明它们的作用域的顶部:

//value == undefined (global)
var value = 10;
//value == 10 (global)

function test() {
    //value == undefined (local)
    var value = 20;
    //value == 20 (local)
}
//value == 10 (global)

请注意,函数声明也是如此,这意味着您可以在函数似乎在代码中定义之前调用它:

test(); //Call the function before it appears in the source
function test() {
    //Do stuff
}

还值得注意的是,当将两者组合成函数表达式时,变量将undefined一直存在,直到赋值发生为止,因此,在这种情况发生之前,您不能调用函数:

var test = function() {
    //Do stuff
};
test(); //Have to call the function after the assignment

0
  1. 保持对外部变量(不仅是全局范围)的访问的最简单方法是,当然,不要在函数中使用相同的名称重新声明它们。只是不要在那里使用var。建议使用适当的描述性命名规则。有了这些变量,将很难以诸如value之类的变量结尾(此方面不一定与问题中的示例相关,因为可能为简单起见而给出了此变量名)。

  2. 如果该函数可以在其他地方重用,因此不能保证在该新上下文中实际定义了外部变量,则可以使用Eval函数。此操作速度很慢,因此不建议用于性能要求较高的功能:

    if (typeof variable === "undefined")
    {
        eval("var variable = 'Some value';");
    }
    
  3. 如果要访问的外部作用域变量是在命名函数中定义的,则可能首先将其附加到函数本身,然后从代码中的任何位置进行访问-从深度嵌套的函数或外部的事件处理程序访问其他一切。请注意,访问属性的速度较慢,并且需要您更改编程方式,因此,除非确实必要,否则不建议您这样做:变量作为函数的属性(JSFiddle)

    // (the wrapper-binder is only necessary for using variables-properties
    // via "this"instead of the function's name)
    var functionAsImplicitObjectBody = function()
    {
        function someNestedFunction()
        {
            var redefinableVariable = "redefinableVariable's value from someNestedFunction";
            console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
            console.log('--> redefinableVariable: ', redefinableVariable);
        }
        var redefinableVariable = "redefinableVariable's value from someFunctionBody";
        console.log('this.variableAsProperty: ', this.variableAsProperty);
        console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
        console.log('redefinableVariable: ', redefinableVariable);
        someNestedFunction();
    },
    functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
    functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
    functionAsImplicitObject();
    
    // (spread-like operator "..." provides passing of any number of arguments to
    // the target internal "func" function in as many steps as necessary)
    var functionAsExplicitObject = function(...arguments)
    {
        var functionAsExplicitObjectBody = {
            variableAsProperty: "variableAsProperty's value",
            func: function(argument1, argument2)
            {
                function someNestedFunction()
                {
                    console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
                        functionAsExplicitObjectBody.variableAsProperty);
                }
                console.log("argument1: ", argument1);
                console.log("argument2: ", argument2);
                console.log("this.variableAsProperty: ", this.variableAsProperty);
                someNestedFunction();
            }    
        };
        return functionAsExplicitObjectBody.func(...arguments);
    };
    functionAsExplicitObject("argument1's value", "argument2's value");
    

0

即使使用全局变量,我也遇到了同样的问题。我发现我的问题是全局变量在html文件之间不存在。

<script>
    window.myVar = 'foo';
    window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>

我试图在已加载的HTML文件中引用myVar和myVarTwo,但收到未定义的错误。长话短说,我发现我可以使用以下方式引用变量:

<!DOCTYPE html>
<html lang="en">
    <!! other stuff here !!>
    <script>

        var myHTMLVar = this.parent.myVar

        /* other stuff here */
    </script>
</html>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.