2

我在函数中有一个局部表变量,还有一个子函数应该有一个同名的变量,同时仍然能够访问父变量。这应该是显而易见和直接的,但不幸的是,如果我在任何地方定义一个具有相同名称的局部变量,JavaScript 不允许我访问父变量:

var p = {alpha : 'beta'};

console.debug (p);
// [08:16:21.896] ({alpha:"beta"})
// Okay.

(function() {
    console.debug (p);
    // [08:16:21.896] ({alpha:"beta"})
    // Right! JavaScript, you're so awesome!
})();

// One moment though, I think I still need the parent's table...
(function() {
    var p = { 'p': p };
    console.debug (p);
    // [08:16:21.896] ({p:(void 0)})
    // Wait, what?
})();

// Okay, maybe giving it the same name in the same statement confuses you?
(function() {
    var parent_p = p;
    var p = {};
    console.debug (parent_p);
    // [08:16:21.897] undefined
})();
// Give me back my variable! http://v.gd/jsWhyDoYouDoThis

那里发生了什么事?有没有办法解决这个问题?

4

3 回答 3

5

Although JavaScript looks like C, Java and others, they have block-level scope. JavaScript has function-level scope.

So in JavaScript an if statement does not create a new block. Only functions do. On top of that, variable and function declarations are "hoisted" (invisibly moved) to the top of the block. That's the reason of your problem.

When you define a variable:

function foo() {
    bar();
    var x = 1;
}

is actually interpreted like:

function foo() {
    var x;
    bar();
    x = 1;
}

So if you read a varible x in bar(), even if it is defined in a higher scope (outside foo()), it reads foo()'s x, which is undefined when bar() is executed.

Taking a closer look at your code, trying to understand what happens in practice:

var p = {alpha : 'beta'};
...    
(function() {
    // var p = { 'p': p }; actually is:
    var p;
    p = { 'p': p }; // thus p inside is `undefined`
    console.debug (p);
})();

(function() {
    // var parent_p = p; var p = {};  actually is:
    var parent_p, p;
    parent_p = p; // that's why parent_p becomes undefined here
    p = {};
    console.debug (parent_p);
})();
于 2013-07-03T00:46:48.367 回答
2

不,在标准的 ECMAScript 中没有办法做到这一点。

块内任何地方的var声明都是对 JS 解析器的指令,即该块中命名变量的所有使用都指的是局部变量。在 JS 中,用于此目的的块是周围的函数,如果不在函数定义 (*) 中,则为全局。

因此,在您的最后一个示例中,您parent_p使用局部变量的值进行分配p,因为您尚未为其分配任何东西undefined

如果绝对必须重用变量名,则必须使用中间范围来保存引用:

(function() {
    var parent_p = p;
    (function() {
        var p = {};
        console.debug (parent_p);
    })();
})();

ECMAScript 的未来版本可能会采用 Mozilla JS 扩展let,它引入了比函数更小的定义块。这将使您更简单地拼写:

(function() {
    var parent_p = p;
    let (p = {}) {
        console.debug (parent_p);
    }
})();

另一种常见的方法是将变量作为参数传递:

(function(parent_p) {
    var p = {};
    console.debug (parent_p);
})(p);

(*:您可以使用全局对象从全局范围读取变量 - 即window.p- 但您不能从封闭非全局范围中获取局部变量。)

于 2013-07-03T00:56:57.820 回答
1

作为acdcjunior正确答案的替代观点:

由于功能范围(无块范围)和提升,这:

(function() {
    var parent_p = p;
    var p = {};
    console.debug (parent_p);
    // [08:16:21.897] undefined
})();

变成:

(function() {
    var parent_p, p;  //both undefined
    parent_p = p;     //undefined
    p = {};
    console.debug (parent_p);
    // [08:16:21.897] undefined
})();

因此,要解决这个问题,您可以将变量作为包含它的对象的属性来访问;在这种情况下,是作为其直接属性之一的window“主机”/“根”对象。p

(function() {
    var parent_p = window.p,
               p = {};
    console.debug (parent_p);
    // [08:16:21.897] ({alpha:"beta"})
})();
于 2013-07-03T00:58:04.293 回答