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

时间:2012-01-31 19:43:15

标签: javascript scope

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

示例:

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

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

输出为

undefined
20

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

这是JavaScript引擎的陷阱吗?

6 个答案:

答案 0 :(得分:161)

这种现象被称为: 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

这是another example

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);

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

正如您所看到的,无论您将变量声明放在何处,它总是悬挂到本地闭包的顶部。


旁注:

这也适用于功能。

考虑this piece of code

test("Won't work!");

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

会给你一个参考错误:

  

未捕获的ReferenceError:未定义测试

这引发了很多开发人员,因为this piece of code工作正常:

test("Works!");

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

如上所述,原因是分配部分悬挂。因此,在第一个示例中,运行test("Won't work!")时,test变量已经声明,但尚未分配给它。

在第二个例子中,我们没有使用变量赋值。相反,我们正在使用正确的函数声明语法, 将函数完全挂起。


Ben Cherry撰写了一篇很好的文章,标题为JavaScript Scoping and Hoisting 阅读。它将全面展示你的全貌。

答案 1 :(得分:48)

我对此处的问题有所解释感到有点失望,但没有人提出解决方案。如果要在函数范围内访问全局变量而不使函数首先生成未定义的局部变量,请将var引用为window.varName

答案 2 :(得分:10)

JavaScript中的变量始终具有功能范围。即使它们是在函数的中间定义的,它们之前也是可见的。功能提升可能会出现类似的现象。

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

另见

答案 3 :(得分: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

答案 4 :(得分:0)

  1. 保持对外部变量(不仅仅是全局范围)的访问的最简单方法当然是尽量不要在函数中使用相同的名称重新声明它们。只是不要在那里使用 var 。建议使用proper descriptive naming rules。有了这些变量,将很难得到像 value 这样的变量(此方面不一定与问题中的示例相关,因为该变量名称可能是为简单起见而给出的。)

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

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

答案 5 :(得分: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>