Définir Setter / Getter pour une variable locale sans équivalent: impossible?

il y a quelques questions précédentes sur StackOverflow en se demandant comment on peut accéder à des variables locales via la chaîne scope, comme si vous vouliez référencer une variable locale en utilisant la notation des crochets et une chaîne, vous auriez besoin de quelque chose comme __local__["varName"] . Jusqu'à présent, je n'ai pas trouvé la méthode la plus pirate pour accomplir ceci, et je n'ai pas trouvé de Méthode Après des heures pour exploiter tous les trucs que je connais.

le but est de mettre en œuvre des getters/setters arbitraires, unparented variables. Objet.defineProperties ou __defineGet/Setter__ besoin d'un contexte pour être appelé. Pour les propriétés dans les contextes global ou window, vous pouvez atteindre l'objectif d'avoir un setter/getter pour les références directes à l'objet.

Object.defineProperty(this, "glob", {get: function(){return "direct access"})
console.log(glob); //"direct access"

même dans mes tests avec une extension personnalisée j'ai compilé dans un chrome modifié qui s'exécute avant toute création de fenêtre où le contexte est le contexte global réel, et même essayer d'appeler this directement dans le contexte global crashes mon programme, je peux le faire sans problème:

Object.defineProperty(Object.prototype, "define", {
    value: function(name, descriptor){
        Object.defineProperty(this, name, descriptor);
    }
};
define("REALLYglobal", {get: function(){ return "above window context"; }});

et il est alors disponible dans tous les cadres créés plus tard en tant que routé global à travers le getter/setter spécifié. L'ancienne __defineGet/Setter__ fonctionne également dans ce contexte sans spécifier sur quoi l'appeler (ne fonctionne pas dans Firefox cependant, la méthode ci-dessus le fait).

donc fondamentalement, il est possible de définir get/set gardes pour toute variable sur un objet, y compris la fenêtre/contexte mondial avec appel direct à l'objet (vous n'avez pas besoin de window.propname , juste propname ). C'est le problème d'être incapable de faire référence à des variables scopées sans précédent, étant le seul type qui peut être dans une portée accessible mais n'a pas de conteneur adressable. Bien sûr, ils sont aussi les plus couramment utilisées il n'est donc pas un cas limite. Ce problème transcende également la mise en œuvre actuelle des procurations dans ES6/Harmony depuis c'est un problème spécifique au fait d'être incapable d'adresser le conteneur d'un objet local avec la syntaxe de la langue.

la raison pour laquelle je veux pouvoir faire ceci est que c'est la seule barrière pour permettre la surcharge de la plupart des opérateurs mathématiques pour une utilisation dans des objets complexes comme les tableaux et les hachures et pour dériver une valeur résultante complexe. J'ai besoin de pouvoir me connecter au setter dans les cas où une valeur est définie sur un type d'objet que j'ai configuré pour la surcharge. Aucun problème si l'objet peut être global ou peut être contenue dans un objet parent, ce qui est probablement ce que je vais juste aller avec. C'est toujours utile avec a.myObject , mais le but est de le rendre aussi transparent que possible.

non seulement cela, mais il serait vraiment utile d'être en mesure d'accomplir quelque chose comme ceci:

var point3d = function(){
    var x, y, z;
    return {
        get: function(){ return [x, y, z]; },
        set: function(vals){ x=vals[0]; y=vals[1]; z=vals[2]; }
    };
};

(qui est similaire à la déstructuration de ES6 mais a des applications plus générales pour mettre en œuvre la fonctionnalité attachée à obtenir/configurer et non seulement le transport de valeurs complexes). Même ce code de base échouera complètement:

var x = {myname: "intercept valueOf and :set: to overload math ops!", index: 5};
x++; //x is now NaN if you don't implement a setter somehow

Je ne me soucie pas de la façon dont hacky la solution est, à ce point c'est juste une curiosité intense pour moi quant à savoir si elle peut être accomplie, même si elle exige de briser toutes les meilleures pratiques qui existent. J'ai écrasé Firefox et Chrome quelques centaines de fois dans la poursuite de ce jusqu'ici en faisant des choses comme redéfinir/intercepter / modifier Object.prototype.valueOf/toString , Function.prototype Function.prototype.constructor , Function.prototype.call/apply , arguments.callee.caller , etc. avec des erreurs de récursion infinies et autres dans les tentatives de jury rig contextes rétroactivement. La seule chose que j'ai pu faire c'est d'emballer tout ça avec des morceaux de code de construction eval et dynamique, ce qui est un pont trop loin pour que je puisse l'utiliser. La seule autre méthode qui a connu un certain succès à distance a été l'utilisation du with combiné à la pré-définition de toutes les variables locales sur un conteneur, mais c'est évidemment très intrusif en plus du problèmes avec l'utilisation de with .

36
demandé sur DJMcMayhem 2011-09-13 14:20:04

2 réponses

on dirait que la réponse est Non . Je cherche ce genre de comportement depuis longtemps. Je n'ai pas été en mesure de proposer une solution praticable. Cela DONC, la question semble similaire. Python a le joli mot-clé locals .

6
répondu kzh 2017-05-23 11:54:05

cela est actuellement possible dans les environnements avec des procurations. Ce serait noeud > 0.6 exécuté comme node --harmony_proxies ou >0.7 avec node --harmony . Chrome Canary (pas sûr si c'est de ça pour l'instant) dans about:flags au fond, expérimental javascript. Firefox l'a depuis un moment sans drapeau.

donc cela ne marchera probablement pas quand ES6 deviendra plus officiel, mais cela fonctionne dans une certaine mesure maintenant.

  var target = (function(){
    var handler = Proxy.create(Proxy.create({
      get: function(r, trap){
        return function(name,val,c,d){
          if (trap === 'get' || trap === 'set') {
            name = val;
            val = c;
          }
          console.log('"'+trap + '" invoked on property "'+name+'" ' + (val?' with value "'+val+'"':''));
          switch (trap) {
            case 'get': return target[name];
            case 'set': return target[name] = val;
            case 'has': return name in target;
            case 'delete': return delete target;
            case 'keys': return Object.keys(target);
            case 'hasOwn': return Object.hasOwnProperty.call(target, name);
            case 'getPropertyDescriptor':
            case 'getOwnPropertyDescriptor': return Object.getOwnPropertyDescriptor(target, name);
            case 'getPropertyNames':
            case 'getOwnPropertyNames': return Object.getOwnPropertyNames(target);
            case 'defineProperty': return Object.defineProperty(target, name, val);
          }
        }
      }
    }))

    var target = {
      x: 'stuff',
      f: { works: 'sure did' },
      z: ['overwritten?']
    };


    with (handler){
      var z = 'yes/no';
      if (x) {
        //x
      } else {
        x = true;
      }
      console.log(f.works);
      if (f.works) {
        f.works = true;
        delete f;
      }

    }
    return target
  })()
   // "getPropertyDescriptor" invoked on property "z" 
   // "getPropertyDescriptor" invoked on property "z" 
   // "getPropertyDescriptor" invoked on property "x" 
   // "get" invoked on property "x" 
   // "getPropertyDescriptor" invoked on property "console" 
   // "getPropertyDescriptor" invoked on property "f" 
   // "get" invoked on property "f" 
   // sure did
   // "getPropertyDescriptor" invoked on property "f" 
   // "get" invoked on property "f" 
   // "getPropertyDescriptor" invoked on property "f" 
   // "get" invoked on property "f" 
   // "getPropertyDescriptor" invoked on property "f" 

   target: { x: 'Stuff', f: { works: true },  z: ['overwritten?'] }

Hit ou manquer et vous devez prendre soin de ne pas souffler votre navigateur en regardant simplement un Proxy dans le débogueur. J'ai dû fermer cette chose pour empêcher le proxy de finir dans la portée globale ou il s'est écrasé sur le cadre à chaque fois. Le fait est que cela fonctionne dans une certaine mesure, là où rien d'autre ne fonctionne.

6
répondu 2012-02-03 08:47:45