为一个未显示的局部变量定义Setter/Getter:不可能?

38 javascript setter closures scope local

之前有一些关于StackOverflow的问题,质疑如何通过作用域链访问局部变量,比如你想用括号表示法和字符串引用局部变量,你需要类似的东西__local__["varName"].到目前为止,我还没有找到实现这一目标的最骇客的方法,并且在数小时利用我知道的每一招之后都没有提出一种方法.

它的目的是在任意非实现变量上实现getter/setter.Object.defineProperties或__defineGet/Setter__要求调用上下文.对于全局或窗口上下文中的属性,您可以实现具有用于直接引用对象的setter/getter的目标.

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

即使在我与编译成一个改良的铬自定义扩展我之前的任何窗口创建,其内容具有实际全球范围内,甚至试图调用运行测试,this直接在全球范围内崩溃我的程序,我能拉这一关没有一个障碍:

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

然后,它可以在以后创建的所有帧中作为通过指定的getter/setter路由的全局帧.旧的__defineGet/Setter__也适用于该上下文而没有指定要调用它的内容(虽然在Firefox中不起作用,但上面的方法确实如此).

因此,基本上可以为对象上的任何变量定义get/set guard,包括直接调用对象的窗口/全局上下文(您不需要window.propname,只是propname).这是无法引用未填充的范围变量的问题,是唯一可以在可访问范围但没有可寻址容器的类型.当然,它们也是最常用的,所以它不是一个优势.这个问题也超越了ES6/Harmony中Proxies的当前实现,因为它是一个问题,特别是无法使用语言的语法来处理本地对象的容器.

我希望能够做到这一点的原因是,它是允许重载大多数数学运算符以用于复杂对象(如数组和散列)并导出复杂结果值的唯一障碍.如果在我为重载设置的对象类型上设置了值,我需要能够挂钩到setter.没有问题,如果对象可以是全局的,或者可以包含在父对象中,这可能就是我要用的东西.它仍然有用a.myObject,但目标是尽可能透明地使用它.

不仅如此,能够完成这样的事情真的很有用:

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]; }
    };
};
Run Code Online (Sandbox Code Playgroud)

(这与ES6的解构类似,但具有更多通用应用程序,用于实现获取/设置所附带的功能,而不仅仅是传输复杂的值).即使这个基本代码也会完全失败:

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
Run Code Online (Sandbox Code Playgroud)

我不在乎解决方案是多么苛刻,在这一点上,对于我是否能够完成它是一种强烈的好奇心,即使它需要打破所有存在的最佳实践.我已经崩溃Firefox和Chrome几百倍地追求这个迄今为止做这样的事情重新定义/拦截/修改Object.prototype.valueOf/toString,Function.prototype Function.prototype.constructor,Function.prototype.call/apply,arguments.callee.caller,等有无限递归误差和诸如此类的东西在尝试陪审团钻机上下文追溯.我能够完成的唯一工作就是用eval基本上包装整个事物并动态构建代码块,这对我来说实际上是一个太过于实际使用的桥梁.唯一的另一个远程成功的路线是使用with与预先定义容器上的所有局部变量相结合,但这显然是非常具有干扰性的使用问题with.

kzh*_*kzh 6

看起来答案是否定的.我一直在寻找这样的行为.我无法想出任何可行的解决方案.这个问题似乎很相似.Python有很好的locals关键字.


小智 6

目前,在具有Proxies的环境中可以实现这一点.这将是节点> 0.6运行node --harmony_proxies或> 0.7 node --harmony.Chromium Canary(不确定它是否已经过了)关于:底部的标志,实验性的javascript.Firefox已经有一段时间没有标志了.

因此,当ES6变得更加官方时,这可能不会起作用,但现在它在一定程度上起作用.

  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?'] }
Run Code Online (Sandbox Code Playgroud)

命中或错过,您只需在调试器中查看代理即可注意不要炸毁浏览器.我不得不将这个东西包装在一个闭包中,以防止代理在全局范围内结束,或者每次都崩溃了.重点是它在某种程度上起作用,没有别的.