为无父级局部变量定义 Setter/Getter:不可能
Defining Setter/Getter for an unparented local variable: impossible?
之前在StackOverflow上有一些问题,质疑如何通过作用域链访问局部变量,例如如果您想使用括号符号和字符串引用局部变量,则需要类似__local__["varName"]
。到目前为止,我还没有找到最黑客的方法来实现这一点,并且在利用我知道的每一个技巧数小时后还没有想出一种方法。
其目的是在任意无父变量上实现 getter/setter。Object.defineProperties 或 __defineGet/Setter__
需要调用上下文。对于全局或窗口上下文中的属性,您可以实现使用资源库/获取器直接引用对象的目标。
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"; }});
然后,它可用于以后创建的所有帧,作为通过指定吸气器/设置器的全局路由。旧__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
、Function.prototype
Function.prototype.constructor
、Function.prototype.call/apply
、arguments.callee.caller
等方式使 Firefox 和 Chrome 崩溃了几百次,并试图追溯地陪审钻机上下文。我唯一能够做的工作就是用评估和动态构建的代码块基本上包装整个东西,这是一个对我来说太远而无法实际使用的桥梁。唯一另一个远程成功的途径是将with
与容器上预定义的所有局部变量结合使用,但这显然在使用with
的问题之外非常具有侵入性。
这目前在具有代理的环境中是可能的。这将是节点> 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?'] }
命中或未命中,您需要注意不要通过简单地查看调试器中的代理来炸毁浏览器。我不得不将它包装在闭包中,以防止代理最终进入全局范围,否则它每次都会崩溃帧。关键是它在某种程度上有效,而其他任何东西都没有。
看起来答案是否定的。我一直在寻找这样的行为已经有一段时间了。我无法想出任何过得去的解决方案。这个问题似乎很相似。Python有一个很好的locals
关键字。
既然你声明你想要与window/global
类似的行为,我假设你想要在给定的上下文中这样做,而不是window/global
。执行此操作的一种简单方法是将 with
语句与 local
对象和define
函数结合使用,这些函数以 local
为目标实现Object.defineProperty
。您不仅仅是将自己的代码放在with
块中。
重要说明:with
重载本机局部变量 ( var, let, const
(。因此,保持清晰的代码非常重要,并防止在范围和父/子上下文中出现重复的名称。
让我们从上下文开始,在这种情况下,我使用闭包,但这也可以是函数、构造函数或任何其他上下文。
// This closure represents any function, class or other scoped block.
(function (){
}());
接下来,我们添加存储容器和define
函数。如果要从代码中的任何位置(在此范围内(访问本地属性,这基本上是应始终开始的。
// This is where we store the local property. (except: var, let, const)
const local = {};
// The define function is used to declare and define the local properties.
function define(name, descriptor){ Object.defineProperty(local, name, descriptor); }
现在,您可以将任何代码放在 with
语句之前,但对于此示例,我们只会添加以某种方式需要local
的代码,因此下一步是创建 with
语句。
// This with statement extends the current scope with local.
with(local){
// This is where your code goes.
}
现在 with
语句的外部结构已经准备就绪,我们可以开始在 with 语句中添加代码了。
放置在 with
语句块中的所有代码都可以访问local
的属性,就好像它们使用 例如 var
定义一样,包括在 with
语句中定义的属性。
有几种方法可以使用local
的属性。定义属性的最简单方法是直接在"本地"中设置它。这只需要执行一次,之后只需通过其名称即可访问该属性。
local.setDirectly = "directly set value";
console.log(setDirectly); // logs "directly set value"
定义属性的另一种方法是使用 define
函数,而不是支持 get/setters
以及枚举和写入访问选项。预期与 Object.defineProperty
相同的行为。
例如,您可以添加一个返回当前时间的 time
属性。
define("time", {
get: function(){
var date = new Date();
return date.getHours() + ":" + ("0" + date.getMinutes()).substr(-2);
}
})
console.log(time);
或者,您可以创建一个计数器属性,该属性在每次访问时递增,放置在嵌套闭包中,以保护计数器自己的变量免受不需要的更改。
(function (){
var counterValue = 0;
define("count", {get: function(){ return counterValue++ }});
}());
console.log(count); // logs 0
console.log(count); // logs 1
当您将所有这些组合在一起时,您将获得类似于以下代码的内容
// This closure represeents any function, class or other scoped block.
(function(){
// This is where we store the local property. (except: var, let, const)
const local = {};
// The define function is used to declare and define the local properties.
function define(name, descriptor){ Object.defineProperty(local, name, descriptor); }
// This with statement extends the current scope with local.
with(local){
// This is where your code goes.
// Defining a variable directly into local.
local.setDirectly = "directly set value";
console.log(setDirectly); // logs "directly set value"
// Defining local properties with the define function
// For instance a time variable that return the current time (Hours:Minutes)
define("time", {
get: function(){
var date = new Date();
return date.getHours() + ":" + ("0" + date.getMinutes()).substr(-2);
}
})
console.log(time); // logs HH:MM
// Or a counter property that increments each time it's been accessed.
(function (){
var counterValue = 0;
define("count", {get: function(){ return counterValue++ }});
}());
console.log(count); // logs 0
console.log(count); // logs 1
console.log(count); // logs 2
console.log(count); // logs 3
}
}());
正如我之前提到的,了解使用 with
语句的含义非常重要。有关with
的更多信息可以在MDN - with中找到。正如问题所述,这是对如何可能的搜索,而不是你应该如何。使用 MDN 上的信息,看看它是否适合您的情况。
这是否回答了你的问题,但这有效:
Object.defineProperty(window, 'prop', {
get: function () {
alert('you just got me')
},
set: function (val) {
alert('you just set me')
},
configurable: true});
对于仅包含基本对象的解决方案:
function ref (o)
{
return new Proxy({}, new Proxy({}, {
get (_, prop) { return (_, ...args) => Reflect[prop](o(), ...args) }
}));
}
要同时使用 DOM 和基元对象,请执行以下操作:
function ref (o)
{
return new Proxy({}, new Proxy({}, {
get (_, prop) { return {
get (_, prop) {
let p = o(), r = p[prop];
if (r instanceof Function) r = r.bind(p)
return r
},
set (_, prop, v) { o()[prop] = v },
has (_, prop) { return prop in o() },
keys(_, prop) { return Object.keys(o()) },
apply (_, _this, args) { return Object.apply(o(), _this, args) },
hasOwn (_, prop) { return Object.hasOwnProperty.call(o(), prop) },
ownKeys() {
var p = o();
return Object.getOwnPropertyNames(p).concat(Object.getOwnPropertySymbols(p))
},
deleteProperty (_, prop) { return delete o()[prop] },
defineProperty (_, prop, desc) { return Object.defineProperty(o(), prop, desc) },
getOwnPropertyDescriptor (_, prop) { return Object.getOwnPropertyDescriptor(o(), prop) }
}[prop] ?? ((_, ...args) => Reflect[prop](o(), ...args));
}}));
}
function refs (o)
{
if (!(o instanceof Function)) o = (o => () => o)(o);
return new Proxy({}, {
get (_, prop) { return ref(() => o()[prop]) }
})
}
用法
let vec = {x: 0, y: 1, z: 2};
let {x, y, z} = refs(() => vec);
outp(`X: ${x}. Y: ${y}. Z: ${z}`); // X: 0. Y: 1. Z: 2
vec.x = 3;
outp(`X: ${x}. Y: ${y}. Z: ${z}`); // X: 3. Y: 1. Z: 2
x = 1;
outp(vec.x); // 3
vec = {y: 1, z: 1};
outp(y === 1); // false
outp(y == 1); // true
outp(z == 1); // true
outp(y == z); // false
// You cannot directly compare these Proxy objects.
outp(y.valueOf() === z.valueOf()); // true
outp(y.valueOf() === 1); // true
outp(z.valueOf() === 1); // true
function ref (o)
{
return new Proxy({}, new Proxy({}, {
get (_, prop) { return {
get (_, prop) {
let p = o(), r = p[prop];
if (r instanceof Function) r = r.bind(p)
return r
},
set (_, prop, v) { o()[prop] = v },
has (_, prop) { return prop in o() },
keys(_, prop) { return Object.keys(o()) },
apply (_, _this, args) { return Object.apply(o(), _this, args) },
hasOwn (_, prop) { return Object.hasOwnProperty.call(o(), prop) },
ownKeys() {
var p = o();
return Object.getOwnPropertyNames(p).concat(Object.getOwnPropertySymbols(p))
},
deleteProperty (_, prop) { return delete o()[prop] },
defineProperty (_, prop, desc) { return Object.defineProperty(o(), prop, desc) },
getOwnPropertyDescriptor (_, prop) { return Object.getOwnPropertyDescriptor(o(), prop) }
}[prop] ?? ((_, ...args) => Reflect[prop](o(), ...args));
}}));
}
function refs (o)
{
if (!(o instanceof Function)) o = (o => () => o)(o);
return new Proxy({}, {
get (_, prop) { return ref(() => o()[prop]) }
})
}
let text = '';
function emitText()
{
document.body.appendChild(
Object.assign(document.createElement('pre'), {innerText: text})
);
}
function outp (t)
{
text += " // " + t;
}
function header (t)
{
emitText();
document.body.appendChild(
Object.assign(document.createElement('h1'), {innerText: t})
);
text = '';
}
function log (t)
{
text += ''n' + t;
}
header("Usage");
let vec = {x: 0, y: 1, z: 2}; log('let vec = {x: 0, y: 1, z: 2};');
let {x, y, z} = refs(() => vec); log('let {x, y, z} = refs(() => vec);');
log('outp(`X: ${x}. Y: ${y}. Z: ${z}`);'); outp(`X: ${x}. Y: ${y}. Z: ${z}`);
vec.x = 3; log('vec.x = 3;');
log('outp(`X: ${x}. Y: ${y}. Z: ${z}`);'); outp(`X: ${x}. Y: ${y}. Z: ${z}`);
x = 1; log('x = 1;');
log('outp(vec.x);'); outp(vec.x);
log('');
vec = {y: 1, z: 1}; log('vec = {y: 1, z: 1};');
log('outp(y === 1);'); outp(y === 1);
log('outp(y == 1);'); outp(y == 1);
log('outp(z == 1);'); outp(z == 1);
log('outp(y == z);'); outp(y == z);
log('// You cannot directly compare these Proxy objects.');
log('outp(y.valueOf() === z.valueOf());'); outp(y.valueOf() === z.valueOf());
log('outp(y.valueOf() === 1);'); outp(y.valueOf() === 1);
log('outp(z.valueOf() === 1);'); outp(z.valueOf() === 1);
header('');
- 节点.js在不可能的地方打印“未定义”
- 谷歌地图Api在CRM Dynamics 2011 iframe"不可能使用显示属性“;消息
- 从数组中随机选择,但在for循环中选择是不可能的;没有按预期工作
- 向具有不同域和基本身份验证的服务器发出 ajax POST 请求是不可能的
- 使用 javascript/jquery/any 动态创建 html 页面客户端.如果不可能,如何使用 php/ajax
- 在现代浏览器中隐藏地址栏是不可能的吗?
- 双击选择框在现代浏览器上是不可能的
- 不可能将html2canvas与ie8一起使用
- 编写一个程序在别人的网站上做某事,是不可能的
- 不可能在一个页面中使用两次JavaScript
- 异步回调不可能嵌套
- 为什么不可能将对象保存在二维数组中
- 不可能的井字游戏Javascript
- JavaScript:是否不可能将单个“三元”操作扩展到多个层
- 是否不可能在JavaScript中使用多行regex模式来指示输入字符串的开始
- 将弹出窗口的大小调整为最小高度和宽度,然后拖动到以下大小应该是不可能的
- 任务不可能:jQuery属性选择器
- swipebox.js-不可能以相反的顺序滑动或导航幻灯片
- 这是“;DI”;结构不可能,或者我应该找一个bug
- 为无父级局部变量定义 Setter/Getter:不可能