JS通过事件循环建模并发。结果,没有比赛条件。那么,在程序的主要范围内,以下类型的安全操作的缺点是什么,请注意以下几点:
const m = new Map([["foo", true]]);
//...
m.set("bar", false);
即使我要清空m
,也不会引起任何问题,因为依赖于m
的每个操作都应考虑为空情况。
也许有人可以说明可变数据类型所伴随的典型问题。
我知道这个问题可能太以意见为依据,因此,如果您认为不适合,请随时将其关闭。
提前感谢!
[[...]因为依赖于
m
的每个操作都应该考虑为空情况
命名困难。假设事物可以是危险的。
我认为自己是一个务实的开发人员,有时只是发生突变。但是,您的工作是知道可能出什么问题,并对周围的人进行危险教育,以便他们做出明智的决定。
我曾经在代码审查期间指出此函数正在更改其参数:(大致看起来像这样)
function foo(bar) {
const baz = {...bar};
baz.a.b.c = 10;
return baz;
}
该函数的作者回复说“不,我之前已经克隆了它。所以该函数是'pure'”。
如果我没有花时间和那个人坐下来,我们可能会遇到一个重大的生产问题。原来,他们正在改变应用程序状态,结果我们进行了一些误报测试。
对我来说,这是变异数据时可能发生的最糟糕的情况:混乱。
由突变引起的错误可能很难追溯。
[我总是鼓励团队中的人们不要为“不可能的情况”编写代码,因为这经常导致代码混乱不堪,以至于“以防万一”检查会增加维护工作量,并破坏我们对代码的信心。
但是,如果您允许uncontrol访问数据,那么“不可能的情况”就在拐角处等待。
我已经证明了人们确实在变异事物时没有意识到。当您的团队中有不同经验的人时,您可以做的最好的事情是:
也许不是您可能期望的“学术”答案,但我想我可以分享一些技巧。
我认为没有特定的缺点,只是有些模糊的忧虑。我们可以通过遵循以下经验法则来消除疑问:
应该更改...
当然,并非总是可以避免的。但是,我认为有两种技术可以缓解该问题。以下几段是这些技术的粗略概述。
如果不预先拥有所有属性/字段,则可以将复合值拆分为形状相同的单位。所以代替
const foo = [{name: "Bob"}, {name: "Ana"}];
// ...lots of code
foo.forEach(o => {
o.age = ...;
});
// ...lots of code
foo.forEach(o => {
if (hasNick(o.name)) o.nick = Some(...);
else o.nick = None;
});
我们可以写:
const foo = ["Bob", "Ana"];
// ...lots of code
const bar = [30, 28];
// ...lots of code
const baz = [None, Some("Apo")];
如果使用标记的并集(又名和类型),则可以完全避免冗余的None
/ null
值:
const foo = [Profile({name: "Bob", age: NaN), Profile({name: "Ana", age: NaN)];
// ...lots of code
foo.forEach(o => {
o.age = ...;
});
// ...lots of code
// for the sake of simplicity a perform an implicit mutation with forEach
foo.forEach((o, i, xs) => {
if (hasNick(o.name))
xs[i] = ProfileWithNick({name: o.name, age: o.age, nick: ...});
});
因为两个构造函数Profile
和ProfileWithNick
都创建了相同类型点2的值。