代表太低,无法添加评论,所以我在这里添加它作为答案。
提前为文字墙道歉。
我喜欢 CTS_AE 的回答,也想走同样的路。
但是,有一点需要注意,那就是数组是如何放入 String 的。
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log([1,2,3].toString(); // "1,2,3"
console.log(memory["1,2,3"]); // "123"
如果您知道自己放入的内容完全是数组,那么这不会是一个问题......或者会这样吗?
关于 Array.prototype.toString() 的 MDN 说
对于 Array 对象,toString 方法连接数组并返回一个字符串,该字符串包含每个数组元素,以逗号分隔。
这带来了两个大问题:
- 通过
array 索引与通过array.toSring() 索引相同
- 由于
toString() 是递归的,嵌套数组最终会被字符串化为与单级数组相同的格式。
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log(memory["1,2,3"]); // "123"
console.log(memory[[[1],[2],[3]]]); // "123"
console.log(memory[[[[1],2],3]]); // "123"
...列表还在继续。不难看出这些问题何时会真正破坏您的项目。前段时间我在尝试记忆时遇到了这样的问题
function doSomeStuff(string, sourceIdxs, targetIdxs) {
if (memo[[string, sourceIdxs, targetIdxs]])
return memo[[string, sourceIdxs, targetIdxs]];
// ...
}
在这种情况下,例如 ["foo", [1, 3, 5], [6, 10]] 和 ["foo", [1, 3], [5, 6, 10]] 指向相同的值,我最终覆盖了现有值,从而有效地破坏了函数内存。
现在,在上述ArraySet 答案的特定情况下,问题仍然存在。虽然您不介意用另一个“相同”密钥覆盖现有密钥,但最终可能会出现误报。
那么如何解决这个问题?
选项 1. 字符串化
简单的方法是使用JSON.stringify() 编写所有关键数据的“精确”字符串表示。
let memory = {};
memory[JSON.stringify([1,2,3])] = "123";
console.log(memory[JSON.stringify([1,2,3])]); // "123"
console.log(memory[JSON.stringify([[1],[2,3]])); // undefined
这有助于处理误报……有点。一方面,您不会遇到数组中重叠元素的问题。 [[1,2],[3]] 不再指向 [[1],[2,3]] 所在的位置。
但是,[1,2,3] 和 "[1,2,3]" 可以。此外(在某些奇怪的情况下),数组元素可能包含 [ 或 ] 字符,这可能会使问题进一步复杂化。
而且您可能不关心这种情况。您的钥匙可能仍然受到足够好的约束,不会发生此类事情。你甚至可能想要这样的行为。如果你这样做了,那就去吧。
好:
不好:
选项 2. 分隔符
更简单、更快捷,同时与旧解决方案相比仍有一些优势。
function toSepString(arr) { return arr.join("|") }
let memory = {};
memory[toSepString([[1,2],3])] = "123";
console.log(memory[toSepString([1,[2,3]])]); // undefined
当然,这只对“最外层”有帮助。
console.log(toSepString([1,[2,[3]]])); // "1|2,3"
console.log(toSepString([1,[[2],[[3]]]]); // "1|2,3"
所以如果你使用它,你需要确保你的键数组的特定元素在转换为字符串时不会变得模棱两可。
当然,您可以使函数递归并在每个开头和结尾添加"[", "]",实质上复制了JSON.stringify() 的部分功能。我想这会比直接调用JSON.stringify() 更高效,但这需要测试。
好:
不好: