我目前有一个Proxy对象,如果未定义属性,我想捕获属性调用。 我的代码的基本版本就是这样的。
var a = new Proxy({}, {
get: function(target, name, receiver) {
if (target in name) {
return target[name];
} else {
function a() {
return arguments;
}
var args = a();
return [target, name, receiver, args];
}
}
});
对此处的属性调用(即:a.b; a.c()
等)应该返回属性调用的目标,名称,接收者和参数。
然而,我想要解决的问题要求我知道属性调用是属性还是函数,这样我可以对每个属性应用不同的处理。检查参数对象的长度不起作用,因为调用a.c()
将产生与a.b
类似的长度0,因此它将被视为普通属性而不是方法。
因此,是否有办法确定尝试访问的属性是否被调用为函数。
更新:我应该澄清,如果访问的属性/方法未定义,以及现有属性/方法,此方法需要工作。
答案 0 :(得分:2)
这可能是一种非常黑客的方式。如果属性为undefined
,我们返回一个函数。如果调用此函数,则我们知道用户正在尝试将该属性作为函数调用。如果它永远不会,它被称为财产。为了检查函数是否被调用,我们利用了在事件循环的下一次迭代中调用Promise
的回调这一事实。这意味着我们不会知道它是否是属性,直到以后,因为用户需要有机会首先调用该函数(因为我们的代码是一个getter)。
此方法的一个缺点是,如果用户期望属性,则从对象返回的值将是新函数,而不是undefined
。如果您需要立即获得结果并且不能等到下一个事件循环迭代,这对您不起作用。
const obj = {
func: undefined,
realFunc: () => "Real Func Called",
prop: undefined,
realProp: true
};
const handlers = {
get: (target, name) => {
const prop = target[name];
if (prop != null) { return prop; }
let isProp = true;
Promise.resolve().then(() => {
if (isProp) {
console.log(`Undefined ${name} is Prop`)
} else {
console.log(`Undefined ${name} is Func`);
}
});
return new Proxy(()=>{}, {
get: handlers.get,
apply: () => {
isProp = false;
return new Proxy(()=>{}, handlers);
}
});
}
};
const proxied = new Proxy(obj, handlers);
let res = proxied.func();
res = proxied.func;
res = proxied.prop;
res = proxied.realFunc();
console.log(`realFunc: ${res}`);
res = proxied.realProp;
console.log(`realProp: ${res}`);
proxied.propC1.funcC2().propC3.funcC4().funcC5();
答案 1 :(得分:1)
您无法提前知道它是一个呼叫表达式还是一个成员表达式,但您可以同时处理这两种情况。
通过返回一个代理原始属性的深层克隆的代理,该代理反映了除原来的两个陷阱处理程序以外的所有陷阱处理程序,您可以链接或调用每个成员表达式。
问题是代理目标也需要可调用,以便handler.apply
陷阱不会抛出TypeError
:
function watch(value, name) {
// create handler for proxy
const handler = new Proxy({
apply (target, thisArg, argsList) {
// something was invoked, so return custom array
return [value, name, receiver, argsList];
},
get (target, property) {
// a property was accessed, so wrap it in a proxy if possible
const {
writable,
configurable
} = Object.getOwnPropertyDescriptor(target, property) || { configurable: true };
return writable || configurable
? watch(value === object ? value[property] : undefined, property)
: target[property];
}
}, {
get (handler, trap) {
if (trap in handler) {
return handler[trap];
}
// reflect intercepted traps as if operating on original value
return (target, ...args) => Reflect[trap].call(handler, value, ...args);
}
});
// coerce to object if value is primitive
const object = Object(value);
// create callable target without any own properties
const target = () => {};
delete target.length;
delete target.name;
// set target to deep clone of object
Object.setPrototypeOf(
Object.defineProperties(target, Object.getOwnPropertyDescriptors(object)),
Object.getPrototypeOf(object)
);
// create proxy of target
const receiver = new Proxy(target, handler);
return receiver;
}
var a = watch({ b: { c: 'string' }, d: 5 }, 'a');
console.log(a('foo', 'bar'));
console.log(a.b());
console.log(a.b.c());
console.log(a.d('hello', 'world'));
console.log(a.f());
console.log(a.f.test());

Open Developer Tools to view Console.

Stack Snippets Console尝试以一种引发receiver
的奇怪方式对TypeError
进行字符串化,但在本机控制台和Node.js中它可以正常工作。
答案 2 :(得分:0)
typeof运算符会为你工作吗?
例如:
if(typeof(a) === "function")
{
...
}
else
{
...
}