Définir Setter / Getter pour une variable locale non partagée: impossible?

Il y a quelques questions précédentes sur StackOverflow questionnant comment on peut accéder à des variables locales via la chaîne de portée, comme si vous vouliez faire référence à des variables locales en utilisant une notation de bracket et une chaîne, vous auriez besoin de quelque chose comme __local__["varName"] . Jusqu'à présent, je n'ai pas encore trouvé la méthode la plus difficile à accomplir, et je n'ai pas trouvé de méthode après des heures d'exploitation de toutes les astuces que je connais.

L'objectif est de mettre en œuvre des getters / setters sur des variables arbitraires non comparables. Object.defineProperties ou __defineGet/Setter__ nécessitent un contexte à appeler. Pour les propriétés dans les contextes global ou de fenêtre, 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 en essayant d'appeler this directement dans le contexte global, me heurte. sans accroc:

 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 toutes les images créées plus tard en tant que routage global via le getter / setter spécifié. L'ancien __defineGet/Setter__ fonctionne également dans ce contexte sans spécifier quoi l'appeler (ne fonctionne pas dans Firefox cependant, la méthode ci-dessus).

Donc, fondamentalement, il est possible de définir get / set guardes pour n'importe quelle variable sur un objet, y compris la fenêtre / contexte global avec un appel direct vers l'objet (vous n'avez pas besoin de window.propname , juste propname ). Il s'agit du problème consistant à ne pas pouvoir référencer les variables scopées non partagées, étant le seul type pouvant être accessible mais sans conteneur adressable. Bien sûr, ils sont aussi les plus couramment utilisés, donc ce n'est pas un cas de bord. Ce problème transcende également l'implémentation actuelle de Proxies dans ES6 / Harmony car il s'agit précisément d'être incapable de traiter le conteneur d'un objet local avec la syntaxe de la langue.

La raison pour laquelle je veux pouvoir faire cela, c'est que c'est le seul obstacle à permettre la surcharge de la plupart des opérateurs de mathématiques pour l'utilisation dans des objets complexes, comme des tableaux et des hachages, et une valeur résultante résultante. Je dois 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. Pas de problème si l'objet peut être global ou peut être contenu dans un objet parent, ce qui est probablement ce que je vais aller avec. Il est toujours utile avec a.myObject , mais l'objectif est de le rendre aussi transparent que possible.

Non seulement cela, mais il serait vraiment utile de pouvoir 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]; } }; }; 

(Ceci est similaire à la déstructuration d'ES6 mais a des applications plus générales pour la mise en œuvre de fonctionnalités associées à la mise en place et non seulement au 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 contrainte de la solution, à ce stade, c'est une curiosité intense pour moi de savoir si cela peut être accompli, même si cela nécessite de rompre toutes les meilleures pratiques qui existent. J'ai rompu Firefox et Chrome quelques centaines de fois à la poursuite de cela jusqu'à maintenant 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écurrence infinies et autres dans les tentatives de contextes de jury de rétroactivité. La seule chose que j'ai pu faire du travail est en train d'envelopper en gros l'ensemble avec eval et dynamiquement la construction de blocs de code, qui est un pont trop loin pour que je n'exerce jamais. Le seul autre chemin à succès à distance était utilisé with combiné à la pré-définition de toutes les variables locales sur un conteneur, mais il est évidemment très intrusif sur les problèmes avec l'utilisation.

Ceci est actuellement possible dans les environnements avec Proxies. Ce serait un nœud> 0.6 exécuté comme node --harmony_proxies ou> 0.7 avec node --harmony . Chromium Canary (pas sûr si c'est à ce sujet) dans environ: drapeaux en bas, javascript expérimental. Firefox l'a eu pendant un certain temps sans drapeaux.

Donc, cela ne fonctionnera probablement pas lorsque ES6 devient 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 miss et vous devez prendre soin de ne pas faire exploser votre navigateur en regardant simplement un proxy dans le débogueur. J'ai dû envelopper cette chose dans une fermeture pour empêcher le proxy de se retrouver dans la portée globale ou il a écrasé le cadre toutes les fois. Le point est que cela fonctionne dans une certaine mesure, où rien d'autre ne le fait.

Il semble que la réponse soit Non . J'ai cherché un comportement comme celui-ci pendant un bon moment. Je n'ai pas pu trouver une solution passable. Cette question SO semble semblable. Python a le bon mot-clé locals .