Definiendo Setter / Getter para una variable local sin argumentos: impossible?


Hay algunas preguntas anteriores sobre StackOverflow que cuestionan cómo se accede a las variables locales a través de la cadena de alcance, como si quisiera hacer referencia a variables locales usando notación de corchetes y una cadena, necesitaría algo como __local__["varName"]. Hasta ahora no he encontrado ni siquiera el método más hackeado para lograr esto, y no se me ha ocurrido un método después de horas de explotar todos los trucos que conozco.

El propósito de esto es implementar getters / setters en arbitrarios variables no analizadas. Objeto.defineProperties o __defineGet/Setter__ requieren que se invoque un contexto. Para las propiedades en los contextos global o window puede lograr el objetivo de tener un setter/getter para referencias directas al objeto.

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

Incluso en mis pruebas con una extensión personalizada compilé en un Chromium modificado que se ejecuta antes de cualquier creación de ventana donde el contexto es el contexto global real, e incluso tratando de llamar this directamente en el contexto global se bloquea mi programa, puedo lograr esto sin un problema:

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

Y luego está disponible en todos los marcos creados posteriormente como un global enrutado a través del getter/setter especificado. El antiguo __defineGet/Setter__ también funciona en ese contexto sin especificar en qué llamarlo(sin embargo, no funciona en Firefox, el método anterior sí).

Así que básicamente es posible definir get / set guards para cualquier variable en un objeto, incluido el contexto window / global con llamada directa al objeto (no necesita window.propname, solo propname). Este es el problema de no poder hacer referencia a las variables de ámbito sin precedentes, ya que es el único tipo que puede estar en un ámbito accesible pero no tiene un contenedor direccionable. Por supuesto, también son los más utilizados también por lo que no es un caso de borde. Este problema también trasciende la implementación actual de Proxies en ES6/Harmony, ya que es un problema específicamente con la imposibilidad de abordar el contenedor de un objeto local con la sintaxis del lenguaje.

La razón por la que quiero ser capaz para hacer esto es que es la única barrera que permite la sobrecarga de la mayoría de los operadores matemáticos para su uso en objetos complejos como matrices y hashes y derivar un valor resultante complejo. Tengo que ser capaz de enganchar en el setter en los casos en que un valor se está estableciendo en un tipo de objeto que he configurado para la sobrecarga. No hay problema si el objeto puede ser global o puede ser un contenido en un objeto padre, que es probablemente lo que voy a ir con. Sigue siendo útil con a.myObject, pero el objetivo es hacerlo tan transparente utilizable como sea posible.

No solo eso, sino que sería realmente útil poder lograr algo como esto:{[19]]}

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]; }
    };
};

(Que es similar a la desestructuración de ES6, pero tiene aplicaciones más generales para implementar la funcionalidad adjunta a la obtención/configuración y no solo el transporte de valores complejos). Incluso este código básico fallará completamente:

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

No me importa lo hackeada que sea la solución, en este punto es solo una curiosidad intensa para mí en cuanto a si puede lograr, incluso si requiere romper todas las mejores prácticas que existen. He bloqueado Firefox y Chrome unos cientos de veces en la búsqueda de esto hasta ahora haciendo cosas como redefinir / interceptar / modificar Object.prototype.valueOf/toString, Function.prototype Function.prototype.constructor, Function.prototype.call/apply, arguments.callee.caller, etc. con errores de recursión infinitos y todo lo demás en los intentos de arreglar los contextos del jurado retroactivamente. Lo único que he podido hacer funcionar es envolver básicamente todo el asunto con eval y construir dinámicamente fragmentos de código, que es un puente demasiado lejos para que yo lo use. La única otra ruta remotamente exitosa fue el uso de with combinado con la predefinición de todas las variables locales en un contenedor, pero eso es obviamente muy intrusivo además de los problemas con el uso de with.

Author: DJMcMayhem, 2011-09-13

2 answers

Parece Que la respuesta es No. He estado buscando un comportamiento como este durante bastante tiempo. No he podido encontrar ninguna solución aceptable. Esta pregunta parece similar. Python tiene la palabra clave nice locals.

 6
Author: kzh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:05

Esto es posible actualmente en entornos con Proxies. Que sería nodo > 0.6 ejecutar como node --harmony_proxies o >0,7 con node --harmony. Chromium Canary (no estoy seguro si está fuera de eso todavía) en about:flags en la parte inferior, javascript experimental. Firefox lo ha tenido por un tiempo sin banderas.

Así que esto probablemente no funcionará cuando ES6 se vuelva más oficial, pero funciona hasta cierto punto ahora.

  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 or miss y debe tener cuidado de no explotar su navegador simplemente mirando un Proxy en el depurador. Tuve que envolver esa cosa en un cierre para evitar que el proxy terminara en el alcance global o se estrelló el marco cada vez. El punto es que funciona hasta cierto punto, donde nada más lo hace.

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-03 08:47:45