📜  为什么不可变性在 JavaScript 中如此重要?

📅  最后修改于: 2021-10-19 04:57:52             🧑  作者: Mango

为什么不可变性在 JavaScript 中如此重要?

可变性或改变代码中的对象有什么问题?

这不是让事情变得简单吗?

在我们深入理解不变性的重要性之前,我们应该首先了解软件开发中的可变性和不变性的概念。

公主亲吻青蛙,希望它能变成英俊的王子。不变性的概念说青蛙永远是青蛙。不变性概念主要起源于函数式和面向对象的编程。每当我们想要更改某些数据(例如更改对象或数组)时,我们应该使用更新后的数据返回一个新对象,而不是直接修改原始对象。

为什么不变性在 JavaScript 中如此重要

将不变性视为“另存为”,因为您知道它返回一个新更改的对象,而传统的就地更改就像“保存”意味着更新原始状态并放弃较早的状态。不可变数据的结构不能改变。

数据将是原始数据,一旦创建了对象,我们就无法在不可变对象中修改其状态。将不变性概念视为不受外界影响的人体。一种安心的状态。

不变性

突变的概念反之亦然。可变性描述了对象的状态在声明后是否可以修改。把它想象成亲吻青蛙会导致王子的转变。举个例子,我们有一个变量,我们给它赋值。稍后如果我们需要修改这个变量的值,我们改变它然后改变它的状态,这个对象被认为是可变的

从未接触过不变性概念的开发人员可能会对将变量分配给新值或重新分配感到困惑。在 JavaScript 中,字符串和数字是不可变的数据类型。

JavaScript 不是一种以不可变方式处理数据的好语言。在 JavaScript 中,数组和对象不是一成不变的。它们的值可以随着时间的推移而改变。两者都始终作为参考传递,并且绝大多数可用方法会在适当的位置改变数据。你可以考虑下面给出的例子……

const a = [2, 1, 4, 3];
console.log(a);
// output: [2, 1, 4, 3]
const b= a.sort();
console.log(b);
// output: [1, 2, 3, 4]
console.log(a)
// output: [1, 2, 3, 4]

我们已将一个数组分配给变量“a”,并对其执行排序操作。排序后的数组被分配给变量“b”,你可以看到我们在执行排序操作后得到了两个变量的排序后的数组。这里对象和数组作为引用传递,而不是复制指向内存中同一个数组的变量“a”和“b”,并且该数组由方法 sort 直接改变。

很多数组方法都是 Mutator 方法。 copyWithin、fill、pop、push、reverse、shift、sort、splice、unshift 所有这些方法都允许可变性。 map 和 filter 是不可变的,因为它在不改变原始数组的情况下创建了一个新数组。要使对象和数组不可变,您可以使用 JavaScript 中的一些技术并在不修改原始内容的情况下创建新值。不要直接更改对象的原始值。将此对象视为不可变的,并返回一个具有更新值的全新对象。

让我们再举一个例子来理解这个概念。我们有一组数据,其中包含一名员工在某个项目上工作的天数。

var daysWorked = [7, 7, 5, 9, 8, 6, 6, 7, 9, 5];

console.log(daysWorked);

// op:  [7, 7, 5, 9, 8, 6, 6, 7, 9, 5];

let's check last 6 months of work

var lastSixMonths = daysWorked.slice(0,6);

console.log(lastSixMonths);

// op: [7, 7, 5, 9, 8, 6]

// let's sort the days worked

var sortedDaysWorked = daysWorked.sort();

console.log(sortedDaysWorked);

// op:  [5, 5, 6, 6, 7, 7, 7, 8, 9, 9]

var lastSevenMonths = hoursWorked.slice(0,7);

console.log(lastSevenMonths);

// op: [5, 5, 6, 6, 7, 7]  but the output was [7, 7, 5, 9, 8, 6] expected.

在上面的方法中,我们在变量 daysWorked 上使用了两种方法。

  • Slice:在上面的例子中,我们使用 slice 方法来实现不变性。此方法对原始对象进行切片,而不是修改原始数据集,我们正在获取对象 lastSixMonths 的新副本。
  • 排序:此方法是一种可变方式,它对原始数据集进行排序。这种方法阻止我们对数据集进行进一步的计算。

JavaScript 中不变性的重要性

不变性的好处是什么?为什么我们首先需要关心不变性?为什么还要打扰?

不变性可以立即对您的数据进行更严格的控制,从而使您的代码更安全、更可预测。换句话说,不可变对象允许您以可预测的方式控制界面和数据流,从而有效地发现变化。它还可以更轻松地实现复杂功能,例如撤消/重做、时间旅行调试、乐观更新和回滚等。

如果我们谈论前端库 React、Vue os 状态管理库 Redux,那么不变性也可以通过在更改前后的状态版本之间进行快速且廉价的比较来帮助实现更好的性能。组件可以利用这一点,并仅在需要时智能地重新渲染自身。这可以显着提高性能。

不变性的主要好处是可预测性、性能和更好的突变跟踪。

1. 可预测性

在任何应用程序中,当我们处理一些前端库时,我们会在其中声明很多状态。我们执行异步操作并更新原始状态(变异)。一旦最终用户开始使用它,更新后的状态将与初始状态显着不同。改变状态会隐藏更改,并会产生可能导致多个错误的副作用。在这种情况下,调试变得困难。

当您保持应用程序架构不变且心智模型简单时,在任何给定时间预测状态中的数据变得更加容易,然后您可以放心,它不会产生任何令人讨厌的副作用。

2. 性能

创建一个不可变的对象会消耗内存。如何?当你向一个不可变对象添加值时,你需要创建一个新对象,在这个新对象中,你用新值复制现有值,这需要额外的内存。为了减少内存消耗,我们使用结构共享。

我们所做的任何更新都会返回新值,但我们在内部共享结构以减少内存消耗。例如,如果您附加到一个包含 100 个元素的向量,它不会创建一个长度为 101 个元素的新向量。内部只分配了几个小对象。

3.突变追踪

不变性的优点之一是您可以通过使用引用和值相等来优化您的应用程序。这可以很容易地识别是否有任何变化。您可以考虑 React 组件中状态更改的示例。 shouldComponentUpdate 可用于检查状态是否相同,比较状态对象并防止其进行不必要的渲染。

不变性允许您像一系列事件一样跟踪这些对象发生的变化。与现有变量相比,变量具有易于跟踪的新引用。这有助于调试代码和构建并发应用程序。此外,事件调试器可帮助您通过用于跟踪突变的视频回放来重放 DOM 事件。

第一次,不变性的概念可能会让您感到困惑。如果您了解不变性的需求和好处,您可以做得更好。当状态发生变异时,你会遇到许多错误,处理它会让你更好地理解不变性的概念。