2013-07-03 3 views
2

J'ai une variable de table locale dans la fonction, et une fonction enfant supposée avoir une variable avec le même nom, tout en étant capable d'accéder à la variable du parent. Cela devrait être évident et simple, mais, malheureusement, JavaScript ne me laisse pas la variable d'accès parent si je définir une variable locale avec le même nom partout:Comment conserver la valeur de variable de la fonction parent avant de créer une variable locale avec le même nom?

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 

Qu'est-ce qui se passe là-bas? Y a-t-il un moyen de contourner cela?

+0

Pourquoi doit-il avoir le même nom? Pouvez-vous accéder à window.p ou quel que soit l'équivalent de nœud si vous exécutez un nœud? Quoi qu'il en soit, le levage variable. –

Répondre

5

Bien que JavaScript ressemble à C, Java et autres, ils ont portée au niveau des blocs. JavaScript a portée au niveau de la fonction.

Donc, en JavaScript, une instruction if ne crée pas de nouveau bloc. Seules les fonctions font. En plus de cela, les déclarations var iable et function sont "hissées" (déplacées de manière invisible) vers le haut du bloc. C'est la raison de votre problème.

Lorsque vous définissez une variable:

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

est en fait interprété comme:

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

Donc, si vous lisez un varible x dans bar(), même si elle est définie dans un cadre supérieur (extérieur foo()), il lit foo() , qui est undefined lorsque bar() est exécuté.

regarder de plus près votre code, en essayant de comprendre ce qui se passe dans la pratique:

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

Non, il n'y a aucun moyen de le faire dans ECMAScript standard.

Une déclaration var n'importe où dans un bloc est une directive à l'analyseur JS que toute utilisation de la variable nommée dans ce bloc fait référence à une variable locale. Dans JS, un bloc dans ce but est la fonction environnante, ou global sinon dans une définition de fonction (*).

Par conséquent, dans votre dernier exemple, vous affectez parent_p avec la valeur de la variable locale p, qui, comme vous ne l'avez pas encore définie, est undefined.

Vous devez utiliser un champ intermédiaire pour enregistrer la référence si vous devez absolument réutiliser le nom de la variable:

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

Une future révision de ECMAScript est susceptible de capter l'extension JS Mozilla let qui introduit plus petit blocs pour les définitions que les fonctions.Cela vous laisse épelez ce plus simplement:

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

Une autre approche commune est de passer la variable comme argument:

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

(*: Vous pouvez lire des variables de la portée globale à l'aide du monde . objet - à savoir window.p - mais vous ne pouvez pas obtenir des variables locales d'enfermer les étendues non globales)

+0

Est-ce que Mozilla a implémenté 'let' jusqu'ici? Je pensais qu'il devrait être utilisé comme '{let p = {}; console.log (parent_p); } '(cela fonctionne dans le noeud/v8 avec --harmony --use-strict, votre version ne fonctionne pas). – bfavaretto

+0

Les deux syntaxes (la mienne est une 'let statement', la vôtre est une 'let definition') devrait fonctionner (et faire pour moi dans Firefox 20). – bobince

+0

Les "instructions" et les "expressions" fonctionnent dans Firefox, mais je me demande si c'est spécifique à Mozilla. Ils ne fonctionnent pas sur le noeud, et je ne les ai pas trouvés dans le brouillon ES6 actuel (où les "définitions" semblent s'appeler "déclarations"). J'ai encore beaucoup à lire et à apprendre des projets. – bfavaretto

1

Comme autre vue sur acdcjunior est bonne réponse:

En raison de fonctionner portée (pas de bloc-champ) et le levage, ceci:

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

devient:

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

Donc, pour résoudre ce problème, vous devez accéder à la variable comme propriété de l'objet qui contient il; dans ce cas window est l'objet 'hôte'/'racine' qui contient p comme l'une de ses propriétés directes.

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