javascript - 为无父本地变量 : impossible? 定义 Setter/Getter

标签 javascript scope closures local setter

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"

即使在使用自定义扩展的测试中,我也编译成修改后的 Chromium,该 Chromium 在上下文是实际全局上下文的任何窗口创建之前运行,甚至尝试调用 this直接在全局上下文中使我的程序崩溃,我可以毫不费力地完成它:
Object.defineProperty(Object.prototype, "define", {
    value: function(name, descriptor){
        Object.defineProperty(this, name, descriptor);
    }
};
define("REALLYglobal", {get: function(){ return "above window context"; }});

然后它在以后创建的所有帧中都可用,作为通过指定 getter/setter 路由的全局路由。老__defineGet/Setter__也可以在没有指定调用它的情况下在该上下文中工作(虽然在 Firefox 中不起作用,但上面的方法可以)。

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

我希望能够这样做的原因是,它是允许重载大多数数学运算符以用于复杂对象(如数组和哈希)并派生复杂结果值的唯一障碍。在我为重载设置的对象类型上设置值的情况下,我需要能够连接到 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]; }
    };
};

(这类似于 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

我不在乎这个解决方案有多么老套,在这一点上,我对它是否可以完成只是一个强烈的好奇心,即使它需要打破所有存在的最佳实践。到目前为止,我已经通过重新定义/拦截/修改 Object.prototype.valueOf/toString 之类的操作使 Firefox 和 Chrome 崩溃了几百次。 , Function.prototype Function.prototype.constructor , Function.prototype.call/apply , arguments.callee.caller等,具有无限的递归错误和诸如此类的尝试追溯性地操纵上下文。我唯一能做的就是用 eval 和动态构建代码块来包装整个事情,这对我来说太远了,以至于我无法实际使用。唯一其他远程成功的路线是使用with。结合预定义容器上的所有局部变量,但这显然对使用 with 的问题非常有影响。 .

最佳答案

这目前在具有代理的环境中是可能的。那将是 node > 0.6 运行为 node --harmony_proxies或 >0.7 与 node --harmony . Chromium Canary(不确定它是否还没有)在底部的 about:flags 中,实验性 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?'] }

命中或错过,您需要注意不要通过简单地查看调试器中的代理来炸毁浏览器。我不得不将那个东西包装在一个闭包中,以防止代理最终进入全局范围,否则它每次都会使框架崩溃。关键是它在某种程度上起作用,而其他任何东西都不起作用。

关于javascript - 为无父本地变量 : impossible? 定义 Setter/Getter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7400250/

相关文章:

java - 每秒更新 TextView 中的数字

c# - 闭包与类?

php - Google 货币转换器 API 和 JavaScript 下拉列表出现 File_get_contents 错误

javascript - 错误 : A navigator can only contain 'Screen' components as its direct children

javascript - 如何在内部从事件监听器调用类函数?

Javascript - 返回返回函数的值

JavaScript 变量在传递给函数时不包含预期值

javascript - 如何重用此 JavaScript 超时关闭?

javascript - 递归解析对象数组并根据 id 过滤对象

javascript - 收集多个元素并在输入时触发对所选元素的单击