2013-07-03 2 views
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 

Что там происходит? Есть ли способ обойти это?

+0

Почему у этого должно быть одноименное имя? Можете ли вы получить доступ к окну.p или независимо от эквивалента узла, если вы используете узел? Во всяком случае, переменный подъем. –

ответ

5

Хотя JavaScript выглядит как C, Java и другие, у них есть область уровня блока. JavaScript имеет функциональный уровень scope.

Итак, в JavaScript инструкция if не создает новый блок. Выполняются только функции. Кроме того, var iable и function декларации «подняты» (невидимо перемещены) в верхнюю часть блока. В этом причина вашей проблемы.

При определении переменной:

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

фактически интерпретируется как:

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

Итак, если вы читаете varible x в bar(), даже если она определена в более высоком объеме (за пределами foo()), в нем указывается foo()x, то есть 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); 
})(); 
2

Нет, нет никакого способа сделать это в стандарте ECMAScript.

A var Декларация в любом месте блока является директивой для парсера JS, что все использование именованной переменной в этом блоке относится к локальной переменной. В JS блоком для этой цели является окружающая функция, или глобальная, если не в определении функции (*).

Следовательно, в последнем примере вы назначаете parent_p со значением локальной переменной p, которая, как вы еще не присвоили ей, составляет undefined.

Вы должны использовать промежуточную область, чтобы сохранить ссылку, если вы абсолютно необходимо повторно использовать имя переменной:

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

Будущий пересмотр ECMAScript может подобрать расширение let Mozilla JS, который вводит меньше блоки для определений, чем функции.Это позволит вам по буквам это более просто:

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

Другим распространенным подходом является передать переменную в качестве аргумента:

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

(*: Вы можете прочитать переменные из глобального масштаба с использованием глобальной . объект - т.е. window.p - но вы не можете получить локальные переменные из вмещающих неглобальными областей)

+0

Это то, что Mozilla реализовала 'let' до сих пор? Я думал, что его следует использовать как '{let p = {}; console.log (parent_p); } '(это работает в узле/v8 с --harmony - use-strict, в вашей версии нет). – bfavaretto

+0

Оба синтаксиса (my - это «let statement», ваш - «let definition») должен работать (и делать для меня в Firefox 20). – bobince

+0

«Операторы» и «выражения» работают в Firefox, но мне интересно, не зависит ли это от Mozilla. Они не работают на узле, и я не мог найти их в текущем проекте ES6 (где «определения», по-видимому, называются «объявлениями»). Мне все еще есть много, чтобы читать и учиться на черновиках. – bfavaretto

1

Как альтернативный взгляд на acdcjunior это правильный ответ:

Из-за область видимости функции (без блок-Scope) и грузоподъемных, это:

(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"}) 
})(); 
Смежные вопросы