Question

Étant donné l'extrait suivant de javascript dans une étendue:

var x = 10;

function sayx() {
  alert(x);
}

sayx();

Vous vous attendez bien sûr à une boîte de message imprimée "10". Vous pouvez effectuer plusieurs imbrications de fonctions pour interagir avec le mode de détermination de "x", car lors de la résolution de x, l'environnement parcourt la chaîne des portées.

Vous pouvez même effectuer un niveau de "recompilation" avec eval pour injecter de nouvelles étendues lors de l'exécution. Par exemple:

var x = 10;

function sayx() {
  alert(x);
}

function wrap(f) {
  return eval('(function() { var x = 20;(' + f + ')(); })');
}

wrap(sayx)();

Cela fonctionne parce que la fonction aura sa fonction toString appelée qui renverra la source "originale" .. nous créons donc essentiellement une version encapsulée de la fonction qui a une nouvelle portée qui remplace x .. le résultat sera une alerte case qui affiche '20' et non pas '10'.

Cependant, à première vue, cela pourrait sembler fonctionner, mais la chaîne de la portée est brisée, le prochain élément de la chaîne n’est plus le même car la fonction 'f' est maintenant définie à un autre emplacement. Pire encore, c’est que la chaîne d'étendue dont il a hérité peut contenir de nombreuses références auxquelles la fonction appelante ne devrait pas avoir accès.

Alors, existe-t-il un moyen plus efficace et plus pratique d’injecter un élément de portée? quelque chose comme:

function withScope(f, scope) { ??? }

---

var x = 10, y = 10;

function dothemath() {
  alert(x + y);
}

var haxthemath = withScope(dothemath, { x: 9000 });
haxthemath(); // 9010 not 20

Je suppose que la réponse est "non", certains pourraient soutenir qu'il y a des problèmes de "sécurité" avec une telle injection de la portée, mais étant donné que vous pouvez faire l'affaire, malgré tout (même si elle est gravement endommagée), je ne pense pas que ce soit le cas.

L'avantage de ceci serait que vous pouvez essentiellement simuler vos propres pseudo variables.

Merci d'avance.

Modifier, juste un peu la clarification, imaginez que je veuille "withScope" une fonction ayant la chaîne de champs suivante:

Window - window properties
Object - { var x = 10 }
Object - { var y = 5 + x }

Je voudrais pouvoir obtenir une fonction qui avait effectivement la même chaîne + une portée que je fournis .. c'est-à-dire:

withScope(somefunc, { foo: 'bar' })

me donnerait

Window - window properties
Object - { var x = 10 }
Object - { var y = 5 + x }
Ext scope - { foo = 'bar' }

Toutes les variables permanentes antérieures seraient trouvées car mon étendue étendue ne dit rien à leur sujet.

Était-ce utile?

La solution

Si vous vous référez par portée aux variables locales de la fonction et / ou de la fermeture, je pense que la réponse est non.

Vous pouvez modifier la portée du mot clé this à l'aide de functionName.call (scope, arg1, ...) ou de functionName.apply (scope, [arg1, ...]); Cela pourrait être utilisé avec des prototypes pour créer des chaînes similaires à celles décrites - si quelque chose ne se trouve pas dans les propriétés propres à l'objet, il est recherché dans son prototype. Si la propriété n’est pas présente, le prochain prototype de la chaîne est utilisé, etc.

.

Autres conseils

Je pense que la réponse que vous recherchez est la fonction intégrée "avec". déclaration.

Cependant, je ne recommanderais pas de l'utiliser, car il est obsolète et n'existera probablement pas dans ecmascript 6

Le seul autre moyen qui me permettrait de faire ce genre de chose est de l'intérieur de l'application hôte elle-même, en manipulant l'environnement javascript de l'extérieur. Ou bien, si vous utilisez rhino, vous pouvez le faire à partir de javascript, car le lien entre Java apis et Javascript est aussi transparent que rhino. Lorsque Steve Yegge a fait remarquer pour la première fois cela, cela m'a époustouflé. Manipulez javascript de l'extérieur du javascript, mais de l'intérieur du javascript! C'est du génie!

Si vous êtes coincé dans un environnement de navigateur, vous pouvez peut-être utiliser un interpréteur javascript écrit en javascript, tel que narcissus.

Peut-être utiliser la méthode toString intégrée de javascript pour les fonctions?

function withScope(f, scope) {
    var toExec = "";
    for (var prop in scope) {
        toExec += "var " + prop + " = scope['" + prop + "'];"
    }
    toExec += "f = (" + f.toString() + ")";
    eval(toExec);
    return f;
}
var x = 10;
var f = function() {
    console.log(x);
};
withScope(f, {x: 20})();

Cela semble être une mauvaise idée cependant ...

Si vous voulez vous amuser avec scope, vous apprécierez peut-être l'instruction let fournie dans Javascript 1.7. Les versions récentes de Firefox prennent en charge laissez .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top