我在我的 JS 项目中以一种功能性的方式工作。
这也意味着我不会变异 object
或 array
实体。相反,我总是创建一个新实例并替换一个旧实例。
例如
let obj = {a: 'aa', b: 'bb'}
obj = {...obj, b: 'some_new_value'}
问题是:如何使用 javascript Maps 以功能(不可变)方式工作?
我想我可以使用以下代码来添加值:
let map = new Map()
...
map = new Map(map).set(something)
但是删除项目呢? 我做不到
new Map(map).delete(something)
, 因为 .delete
的结果是一个 bool 值。附: 我知道 ImmutableJS 的存在,但我不想使用它,因为您永远无法 100% 确定您现在使用的是普通的 JS 对象还是 immutablejs 的对象(尤其是嵌套结构)。而且由于对 TypeScript 的支持不好,顺便说一句。
最佳答案
如果您不想使用持久映射数据结构,那么您将无法绕过突变,或者必须进行效率极低的浅拷贝。请注意,突变本身无害,但仅与共享底层可变值一起使用。
如果我们能够限制访问可变值的方式,我们可以获得安全的可变数据类型。不过,它们是有代价的。你不能像往常一样使用它们。事实上,使用它们需要一些时间来熟悉。这是一个权衡。
这是一个带有原生 Map
的示例:
// MUTABLE
const Mutable = clone => refType => // strict variant
record(Mutable, app(([o, initialCall, refType]) => {
o.mutable = {
run: k => {
o.mutable.run = _ => {
throw new TypeError("illegal subsequent inspection");
};
o.mutable.set = _ => {
throw new TypeError("illegal subsequent mutation");
};
return k(refType);
},
set: k => {
if (initialCall) {
initialCall = false;
refType = clone(refType);
}
k(refType);
return o;
}
}
return o;
}) ([{}, true, refType]));
const mutRun = k => o =>
o.mutable.run(k);
const mutSet = k => o =>
o.mutable.set(k);
// MAP
const mapClone = m => new Map(m);
const mapDelx = k => m => // safe-in-place-update variant
mutSet(m_ =>
m_.has(k)
? m_.delete(k)
: m_) (m);
const mapGet = k => m =>
m.get(k);
const mapSetx = k => v => // safe-in-place-update variant
mutSet(m_ => m_.set(k, v));
const mapUpdx = k => f => // safe-in-place-update variant
mutSet(m_ => m_.set(k, f(m_.get(k))));
const MutableMap = Mutable(mapClone);
// auxiliary functions
const record = (type, o) => (
o[Symbol.toStringTag] = type.name || type, o);
const app = f => x => f(x);
const id = x => x;
// MAIN
const m = MutableMap(new Map([[1, "foo"], [2, "bar"], [3, "baz"]]));
mapDelx(2) (m);
mapUpdx(3) (s => s.toUpperCase()) (m);
const m_ = mutRun(Array.from) (m);
console.log(m_); // [[1, "foo"], [3, "BAZ"]]
try {mapSetx(4) ("bat") (m)} // illegal subsequent mutation
catch (e) {console.log(e.message)}
try {mutRun(mapGet(1)) (m)} // illegal subsequent inspection
catch (e) {console.log(e.message)}
如果你仔细看看
Mutable
您会看到它也创建了一个浅拷贝,但最初只创建一次。然后,您可以根据需要进行尽可能多的突变,直到您第一次检查可变值。您可以在我的 scriptum library 中找到具有多个实例的实现。 .这是 post以及有关该概念的更多背景信息。
我从 Rust 借用了这个概念,它被称为所有权。类型理论背景是仿射类型,如果您有兴趣,可以将其归入线性类型。
关于javascript - 如何在没有突变的情况下使用 javascript Map,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65594804/