- xml - AJAX/Jquery XML 解析
- 具有多重继承的 XML 模式
- .net - 枚举序列化 Json 与 XML
- XML 简单类型、简单内容、复杂类型、复杂内容
假设我有一个对象,obj
,有一些方法和一些 getters:
var obj = {
method1: function(a) { /*...*/ },
method2: function(a, b) { /*...*/ },
}
Object.defineProperty(obj, "getter1", {get:function() { /*...*/ }});
Object.defineProperty(obj, "getter2", {get:function() { /*...*/ }});
obj
是可链接的,链接通常包括方法和 getter:obj.method2(a,b).getter1.method1(a).getter2
(对于示例)。
我知道这种 getter 的链式使用有点奇怪,在大多数情况下可能是不可取的,但这不是常规的 js 应用程序(它用于 DSL )。
但是如果(出于某种原因)我们真的想懒惰地执行这些链式方法/getter 怎么办?比如,仅在调用某个“最终”getter/方法时才执行它们?
obj.method2(a,b).getter1.method1(a).getter2.execute
在我的例子中,这个“最终”方法是 toString
,它可以由用户显式调用,或者当他们试图将它连接到字符串时隐式调用(valueOf
也会触发评估)。但我们将使用 execute
getter 示例来扩大这个问题,并希望对其他人有用。
所以想法是:代理 obj
并将所有 getter 调用和方法调用(及其参数)简单地存储在一个数组中。然后,当在代理上调用 execute
时,将所有存储的 getter/方法调用以正确的顺序应用到原始对象并返回结果:
var p = new Proxy(obj, {
capturedCalls: [],
get: function(target, property, receiver) {
if(property === "execute") {
let result = target;
for(let call of this.capturedCalls) {
if(call.type === "getter") {
result = result[call.name]
} else if(call.type === "method") {
result = result[call.name](call.args)
}
}
return result;
} else {
let desc = Object.getOwnPropertyDescriptor(target, property);
if(desc.value && typeof desc.value === 'function') {
this.capturedCalls.push({type:"method", name:property, args:[/* how do I get these? */]});
return receiver;
} else {
this.capturedCalls.push({type:"getter", name:property})
return receiver;
}
}
},
});
如您所见,我了解如何捕获方法的 getter 和名称,但我不知道如何获取方法的参数。我知道 apply陷阱,但我不太确定如何使用它,因为据我了解,它仅适用于实际附加到函数对象的代理。如果专业人士能在这里为我指明正确的方向,我将不胜感激。谢谢!
This question似乎有类似的目标。
最佳答案
我快到了!我假设有一些特殊的方法来处理方法,所以这让我陷入了 apply
陷阱和其他干扰,但事实证明你可以用 get
做任何事情> 陷阱:
var obj = {
counter: 0,
method1: function(a) { this.counter += a; return this; },
method2: function(a, b) { this.counter += a*b; return this; },
};
Object.defineProperty(obj, "getter1", {get:function() { this.counter += 7; return this; }});
Object.defineProperty(obj, "getter2", {get:function() { this.counter += 13; return this; }});
var p = new Proxy(obj, {
capturedCalls: [],
get: function(target, property, receiver) {
if(property === "execute") {
let result = target;
for(let call of this.capturedCalls) {
if(call.type === "getter") {
result = result[call.name]
} else if(call.type === "method") {
result = result[call.name].apply(target, call.args)
}
}
return result;
} else {
let desc = Object.getOwnPropertyDescriptor(target, property);
if(desc.value && typeof desc.value === 'function') {
let callDesc = {type:"method", name:property, args:null};
this.capturedCalls.push(callDesc);
return function(...args) { callDesc.args = args; return receiver; };
} else {
this.capturedCalls.push({type:"getter", name:property})
return receiver;
}
}
},
});
返回函数(...args) { callDesc.args = args;返回接收器; };
位是魔法发生的地方。当他们调用一个函数时,我们返回一个“虚拟函数”来捕获他们的参数,然后像往常一样返回代理。可以使用 p.getter1.method2(1,2).execute
等命令测试此解决方案(使用 obj.counter===9< 生成
)obj
/
这似乎工作得很好,但我仍在测试它,如果有任何需要修复的地方,我会更新这个答案。
注意:使用这种“惰性链接”方法,您必须在每次访问 obj
时创建一个新代理。为此,我只需将 obj
包装在“根”代理中,并在访问其属性之一时生成上述代理。
这可能对世界上除了我以外的每个人都没有用,但我想我还是把它贴在这里以防万一。以前的版本只能处理返回 this
的方法。此版本修复了该问题并使其更接近于记录链并仅在需要时才延迟执行它们的“通用”解决方案:
var fn = function(){};
var obj = {
counter: 0,
method1: function(a) { this.counter += a; return this; },
method2: function(a, b) { this.counter += a*b; return this; },
[Symbol.toPrimitive]: function(hint) { console.log(hint); return this.counter; }
};
Object.defineProperty(obj, "getter1", {get:function() { this.counter += 7; return this; }});
Object.defineProperty(obj, "getter2", {get:function() { this.counter += 13; return this; }});
let fn = function(){};
fn.obj = obj;
let rootProxy = new Proxy(fn, {
capturedCalls: [],
executionProperties: [
"toString",
"valueOf",
Symbol.hasInstance,
Symbol.isConcatSpreadable,
Symbol.iterator,
Symbol.match,
Symbol.prototype,
Symbol.replace,
Symbol.search,
Symbol.species,
Symbol.split,
Symbol.toPrimitive,
Symbol.toStringTag,
Symbol.unscopables,
Symbol.for,
Symbol.keyFor
],
executeChain: function(target, calls) {
let result = target.obj;
if(this.capturedCalls.length === 0) {
return target.obj;
}
let lastResult, secondLastResult;
for(let i = 0; i < capturedCalls.length; i++) {
let call = capturedCalls[i];
secondLastResult = lastResult; // needed for `apply` (since LAST result is the actual function, and not the object/thing that it's being being called from)
lastResult = result;
if(call.type === "get") {
result = result[call.name];
} else if(call.type === "apply") {
// in my case the `this` variable should be the thing that the method is being called from
// (this is done by default with getters)
result = result.apply(secondLastResult, call.args);
}
// Remember that `result` could be a Proxy
// If it IS a proxy, we want to append this proxy's capturedCalls array to the new one and execute it
if(result.___isProxy) {
leftOverCalls = capturedCalls.slice(i+1);
let allCalls = [...result.___proxyHandler.capturedCalls, ...leftOverCalls];
return this.executeChain(result.___proxyTarget, allCalls);
}
}
return result;
},
get: function(target, property, receiver) {
//console.log("getting:",property)
if(property === "___isProxy") { return true; }
if(property === "___proxyTarget") { return target; }
if(property === "___proxyHandler") { return this; }
if(this.executionProperties.includes(property)) {
let result = this.executeChain(target, this.capturedCalls);
let finalResult = result[property];
if(typeof finalResult === 'function') {
finalResult = finalResult.bind(result);
}
return finalResult;
} else {
// need to return new proxy
let newHandler = {};
Object.assign(newHandler, this);
newHandler.capturedCalls = this.capturedCalls.slice(0);
newHandler.capturedCalls.push({type:"get", name:property});
let np = new Proxy(target, newHandler)
return np;
}
},
apply: function(target, thisArg, args) {
// return a new proxy:
let newHandler = {};
Object.assign(newHandler, this);
newHandler.capturedCalls = this.capturedCalls.slice(0);
// add arguments to last call that was captured
newHandler.capturedCalls.push({type:"apply", args});
let np = new Proxy(target, newHandler);
return np;
},
isExtensible: function(target) { return Object.isExtensible(this.executeChain(target)); },
preventExtensions: function(target) { return Object.preventExtensions(this.executeChain(target)); },
getOwnPropertyDescriptor: function(target, prop) { return Object.getOwnPropertyDescriptor(this.executeChain(target), prop); },
defineProperty: function(target, property, descriptor) { return Object.defineProperty(this.executeChain(target), property, descriptor); },
has: function(target, prop) { return (prop in this.executeChain(target)); },
set: function(target, property, value, receiver) { Object.defineProperty(this.executeChain(target), property, {value, writable:true, configurable:true}); return value; },
deleteProperty: function(target, property) { return delete this.executeChain(target)[property]; },
ownKeys: function(target) { return Reflect.ownKeys(this.executeChain(target)); }
});
请注意,它代理了一个函数,因此它可以轻松捕获 apply
。另请注意,链中的每一步都需要创建一个新的代理。它可能需要一些调整以适应与我的不完全相同的目的。同样,我不怀疑它在 DSL 构建和其他元编程之外毫无用处 - 我将它放在这里主要是为了给其他试图实现类似目标的人以灵感。
关于javascript - 使用代理捕获所有链式方法和 getter(用于惰性执行),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41886355/
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!