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
.
小智 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)
命中或错过,您只需在调试器中查看代理即可注意不要炸毁浏览器.我不得不将这个东西包装在一个闭包中,以防止代理在全局范围内结束,或者每次都崩溃了.重点是它在某种程度上起作用,没有别的.
归档时间: |
|
查看次数: |
3820 次 |
最近记录: |