如何在创建具有相同名称的局部变量之前保留父函数的变量值?

时间:2013-07-03 00:40:01

标签: javascript

我在函数中有一个本地表变量,并且一个子函数应该具有一个具有相同名称的变量,同时仍然能够访问父变量。这应该是显而易见的,但不幸的是,如果我在任何地方定义一个具有相同名称的局部变量,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

那里发生了什么?有什么方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:5)

尽管JavaScript看起来像C,Java等,但它们具有块级范围。 JavaScript具有功能级范围

因此在JavaScript中,if语句不会创建新块。只有功能。最重要的是,var iable和function声明被“提升”(无形地移动)到块的顶部。这就是你问题的原因。

定义变量时:

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

实际上被解释为:

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

因此,如果您在x中阅读了变量bar(),即使它在更高的范围内定义(foo()之外),也会显示foo()的{​​{1}} x 1}},执行undefined时为bar()

仔细研究一下你的代码,试着去理解实践中发生的事情:

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

答案 1 :(得分: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 - 但您无法通过封闭非全局范围来获取局部变量。)

答案 2 :(得分: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是'host'/'root'对象,它将p作为其中一个直接属性。

(function() {
    var parent_p = window.p,
               p = {};
    console.debug (parent_p);
    // [08:16:21.897] ({alpha:"beta"})
})();