它仅在#toString中发生,并且仅当我(尝试)通过类似于missingMethod的陷阱访问它时才发生.
我有一个名为createIterface的工厂,该工厂使用大量方法返回对象的代理.在这些方法中,我同时拥有#toString()和#id(). #id返回一个具有与调用者相同属性的接口,并且工作正常; #toString应该将我的接口转换为String,但是失败.
所有接口的方法-包括#id和#toString-都在#Symbol.for(“ __ methods”)属性中.我这样做是为了调试目的:
const __methods = Symbol.for("__methods");
const missingMethod = ({
get: (obj, prop) => Reflect.has(obj, prop)
? Reflect.get(obj, prop)
: Reflect.has(obj[__methods], prop)
? Reflect.get(obj[__methods], prop)
: console.log(`No #${prop} property exists.`)
});
const createInterface = (...props) => new Proxy({
...props,
[__methods]: {
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`
}
}, missingMethod);
const interface = createInterface(0, 1, 2);
interface.id(); //works
interface.toString(); //error: Cannot convert a Symbol value to a string
引发的错误表明它无法(隐式)将Symbol转换为String(这是正确的).事实是,#toString不是符号.但是,有一个著名的符号#toStringTag定义了Object#toString()行为.当我用其他方法实现它时,我的#toString()被忽略,并且接口返回'[object Object]’:
// see code above
const createInterface = (...props) => new Proxy({
...props,
[__methods]: {
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`,
[Symbol.toStringTag]: () => "Interface"
}
}, missingMethod);
const interface = createInterface(0, 1, 2);
interface.id(); //works
interface.toString(); //bug: '[object Object]'
如果我在__methods之外对方法进行编码,那么一切都可以正常工作:
// see code above
const createInterface = (...props) => new Proxy({
...props,
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`
}, missingMethod);
const interface = createInterface(0, 1, 2);
const copycat = interface.id();
interface.toString() === copycat.toString(); //true
除了一些奇怪的浏览器错误(我正在运行最新的Chrome,在撰写本文时,它是第71.0.3578.98版),我不知道为什么会这样或如何解决.
有人可以帮忙吗?
解决方法:
问题是访问interface.toString首先要通过
get: (obj, prop) => Reflect.has(obj, prop)
? Reflect.get(obj, prop)
: Reflect.has(obj[__methods], prop)
...
您期望interface.toString在这里进入三进制并到达_methods,但是Reflect.has(obj,’toString’)由于Object.prototype.toString的计算结果将为true.然后,在对象上调用该函数将再次通过代理的getter操作,以查找要调用的#toStringTag.吸气剂经过所有三元分析,一无所获,因此抛出异常
console.log(`No #${prop} property exists.`)
因为prop是一个符号,不能被串联.
一种可能性是使用不继承自Object.prototype的对象:
const obj = Object.create(null);
const createInterface = (...props) => new Proxy(
Object.assign(obj, {
...props,
[__methods]: {
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`
}
})
, missingMethod
);
const __methods = Symbol.for("__methods");
const missingMethod = ({
get: (obj, prop) => Reflect.has(obj, prop)
? Reflect.get(obj, prop)
: Reflect.has(obj[__methods], prop)
? Reflect.get(obj[__methods], prop)
: console.log(`No #${prop} property exists.`)
});
const obj = Object.create(null);
const createInterface = (...props) => new Proxy(
Object.assign(obj, {
...props,
[__methods]: {
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`
}
})
, missingMethod
);
const interface = createInterface(0, 1, 2);
interface.id(); //works
console.log(interface.toString());
另一种可能是让吸气剂执行hasOwnProperty检查,而不是Reflect.has检查(Reflect.has与in基本上相同,并且’toString’将在几乎所有对象中):
get: (obj, prop) => obj.hasOwnProperty(prop)
const __methods = Symbol.for("__methods");
const missingMethod = ({
get: (obj, prop) => obj.hasOwnProperty(prop)
? Reflect.get(obj, prop)
: Reflect.has(obj[__methods], prop)
? Reflect.get(obj[__methods], prop)
: console.log(`No #${prop} property exists.`)
});
const createInterface = (...props) => new Proxy({
...props,
[__methods]: {
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`,
}
}, missingMethod);
const interface = createInterface(0, 1, 2);
interface.id(); //works
console.log(interface.toString());
第三种可能性是确保由初始Reflect.has找到的属性不是来自Object.prototype方法:
get: (obj, prop) => Reflect.has(obj, prop) && Reflect.get(obj, prop) !== Object.prototype[prop]
const __methods = Symbol.for("__methods");
const missingMethod = ({
get: (obj, prop) => Reflect.has(obj, prop) && Reflect.get(obj, prop) !== Object.prototype[prop]
? Reflect.get(obj, prop)
: Reflect.has(obj[__methods], prop)
? Reflect.get(obj[__methods], prop)
: console.log(`No #${prop} property exists.`)
});
const createInterface = (...props) => new Proxy({
...props,
[__methods]: {
id: () => createInterface (...props),
toString: () => `Interface(${ props.toString() })`
}
}, missingMethod);
const interface = createInterface(0, 1, 2);
interface.id(); //works
console.log(interface.toString());