javascript - `Proxy` 将 `this[toString]` 与 `this[Symbol.toStringTag]` 混淆
问题描述
#toString
只有当我(尝试)通过missingMethod
-like访问它时才会发生这种情况trap
。
我有一个工厂createIterface
,它返回一个Proxy
具有大量方法的对象。在这些方法中,我有#toString()
和#id()
。#id
返回interface
与调用者具有相同属性的 a 并且工作得很好;#toString
应该将 my 转换interface
为字符串,但它失败了。所有interface
的方法——包括#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
不是一个符号。但是,有一个众所周知的 Symbol#toStringTag
来定义Object#toString()
行为。当我用其他方法实现它时,我#toString()
被忽略并interface
返回'[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,在撰写本文时它是 v. 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')
由于true
. Object.prototype.toString
然后,在对象上调用该函数再次通过代理的 getter 操作,搜索#toStringTag
要调用的。getter 遍历了所有的三元组并没有找到任何东西,所以它抛出了在线
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());
另一种可能性是让 getter 进行hasOwnProperty
检查而不是Reflect.has
检查(Reflect.has
与 基本相同in
,并且几乎'toString'
可以是in
任何对象):
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());
推荐阅读
- html - 带有容器文本和标题的背景图像
- highcharts - 我想添加一个加载微调器或添加一些对话框,直到加载 highcharts 并且在 react typescript 中加载图表后应该关闭?
- java - 物体越来越快,没有任何理由
- elasticsearch - Elasticsearch match_pharse 查询但提升文本前后没有任何单词的结果
- c# - 如何实现具有不同参数的一种方法。C#
- symfony - 使用 API 平台配置 Gedmo Abstract Personal Translatable
- node.js - 如何解密加密的文本
- raspberry-pi - 树莓派/boot/cmdline.txt的原始文件内容
- powershell - 在 PowerShell 中创建 HMAC SHA256 哈希
- php - Laravel - 在不多次查询数据库的情况下获取一列的总和