2017-07-15 2 views
2

Considérez ce codeComment créer une nouvelle variable dans une fermeture

'use strict'; 

var factory =() => script => eval(script); 
var closure = factory(); 

closure('var v = 0'); 
var val = closure('typeof v'); 

console.log(val); 

Ceci est ma tentative pour y parvenir. Je veux créer une fermeture et ensuite permettre aux utilisateurs de créer une nouvelle variable locale dans cette fermeture. Est-ce que c'est possible?

J'ai lu quelque part que "la fonction native 'eval' peut même créer une nouvelle variable dans le contexte d'exécution locale.". Alors, pourquoi ça ne marche pas dans mon exemple? Ma conjecture est que c'est parce que la fonction a fini d'exécuter et le nombre de variables après la fin de la fonction ne peut pas être changé, mais je ne suis pas sûr.

Mon exemple de script crée une fermeture et essaie de déclarer et d'initialiser la nouvelle variable v dans cette fermeture et lui affecter le numéro 0. Je m'attendais à ce que le résultat de typeof v devrait être number, mais il est en réalité undefined.

Donc, j'ai deux questions:

  1. Pourquoi ne pas crée la v variable comme prévu
  2. Comment atteindre réellement que (ce serait l'exemple de travail)?
+0

Que faites-vous exactement essaies de construire ici? Que diriez-vous de créer un objet pour stocker votre contexte et ajouter des propriétés à cet objet au lieu d'essayer de pirater des fermetures. – Thomas

+0

Pourquoi avez-vous besoin de cela? J'ai une réponse à 75%, mais j'ai réalisé à quel point cela semblait étrange et inutile. Pourquoi avez-vous besoin de créer de nouvelles variables dans une portée comme celle-ci? Et notez que chaque fois que vous appelez 'closure', vous créez une nouvelle portée. Tous les appels à 'closure' ne sont pas" connectés ". – Carcigenicate

+0

@Thomas. Je sais que je peux utiliser un objet à la place, mais je ne fais que demander cette méthode afin de mieux comprendre le fonctionnement de javascript. –

Répondre

2

Eh bien, il est scope localement, mais un peu trop locale

var factory =() =>{ 
    return script =>{ 
    //this is the scope. you cant access any variables outside of this. 
    eval(script); 
    }; 
    //you want to eval here, which is impossible 
}; 

Vous pouvez faire des choses de portée très hacky travailler autour de ce (magasin toutes les variables contexte):

var factory = (context={}) => script =>{ with(context){ return eval(script); }}; 

Vous devez initialiser toutes les variables locales sur la création:

var exec=factory({v:0, b:undefined});// note that you need to set a value explicitly {v,b} wont worm 

et il fonctionne comme prévu:

console.log(
exec("v"),//0 
exec("v=2"),//2 
exec("v"),//2 
typeof v //undefined 
); 

http://jsbin.com/xibozurizi/edit?console


Si vous ne voulez pas aller qu'au fond, la seule chose que vous pourriez faire serait concaténer les chaînes:

var factory = code => concat => (eval(code),res=eval(concat),code+=concat,res); 
// or shorter/more buggy 
var factory = code => concat => eval(code+=";"+concat); 

var exec=factory("var a=1;"); 
console.log(
exec("a;"),//1 
exec("var b=a+1"), 
exec("b"),//2 
tyepof a, typeof b //undefined 
); 

http://jsbin.com/midawahobi/edit?console


Le code supérieur se déroulera les chaînes à plusieurs reprises, ce qui est peut ne pas voulu. Une autre approche:

var factory=code=>({ 
    code, 
    run(c){ 
    return eval(this.code+";"+c); 
    }, 
    add(c){ this.code+=";"+c} 
}); 

Vous pouvez donc faire

var exec=factory("var a='hello'"); 
exec.run("alert(a)")//alerts hello 
exec.add("var b=a+' world'"); 
console.log(exec.code,exec.run("b"));//hello world, the upper alert isnt run again 

http://jsbin.com/lihezuwaxo/edit?console

noter que evaling est toujours une mauvaise idée ...

+0

Ok, je pense que je l'ai eu. Bon expliqué. Cependant, je n'ai pas assez de rep pour donner +1, désolé. –

+0

@FigliDelleTenebre vous êtes les bienvenus;) et je pense que vous pourrez bientôt ... –

0

Votre appel à eval()ne créer une nouvelle variable, et il le fait dans le cadre de l'exécution locale de l'appel. Ce contexte est à l'intérieur cette fonction, de sorte qu'il n'affecte pas le contexte en dehors de, où vous appelez closure(). En général, eval() doit être strictement évité. Cela rend l'optimisation difficile (ou impossible), donc les fonctions qui l'utilisent seront ignorées par l'optimiseur. Il y a de rares circonstances dans lesquelles cela est utile, mais déclarer une nouvelle variable locale ne semble pas être l'une d'entre elles.

+0

Je sais que 'eval' n'est pas pour un usage commercial ou sérieux, mais j'essaie juste pour comprendre les concepts de JavaScript et comment cela fonctionne, rien de plus. Je sais que le code d'évaluation devrait être évité. –

0

Vous pouvez faire comme ça

var factory =() => script => eval(script); 
var closure = factory(); 
var v; 
closure('v=20') 
+0

Mais il crée une variable globale (ou au moins variable dans la portée parent). Si vous essayez de créer une nouvelle fermeture, elle utilisera la même variable 'v'. –