What's the deal with immutability in JavaScript?

栏目: IT技术 · 发布时间: 4年前

内容简介:TheThe basic definition ofThe first thing to notice is that all

The immutability is quite a buzzword lately. Today we attempt to find out why. In this article, we explain what the immutability is and how we can benefit from it. When defining immutability, we look into various aspects of it. We investigate primitive values, assignment immutability, and how to keep our data structures immutable. We also look into how we can achieve immutability using built-in features.

Defining immutability

The basic definition of immutability is to be unable to change. It is one of the core principles of  functional programming . Even if we don’t aim to write code that fully follows the rules of functional programming, we can profit from diving into it.

Types immutability

The first thing to notice is that all primitive values are in their nature immutable. We can distinguish seven primitive data types: number, bigint, boolean, string, null, undefined, and symbol.

We sometimes perceive strings as arrays. That suggests that we might mutate them, but this is not the case:

const name = 'John Smith';
name[0] = 'j';
 
console.log(name); // 'John Smith';

The interesting thing is that JavaScript wraps primitive types in objects. An example of such is the String . We can observe this in the below example:

console.log(typeof name); // 'string'
 
String.prototype.getType = function () {
  return typeof this;
};
 
console.log(name.getType()); // 'object'

The wrappers are mutable, but they disappear after they are not needed anymore. Every time we access a property on a primitive value, we have a new wrapper.

The above behavior is sometimes referred to as boxing

The primitive values being immutable is the reason why all methods that operate on primitives return new values instead of mutating them.

A good example is String.prototype.toUpperCase()

Assignment immutability

It is important not to confuse value immutability with the assignment immutability .

let hello = 'Hello';
hello += ' World!';

Above, we don’t mutate the string. Instead, we assign a new value to the hello variable that is a concatenation of two strings.

The above assignment would fail if we were to use the const keyword. Let’s inspect the example below:

const user = {
  name: 'Smith'
};
 
user.name = 'John';

We can successfully mutate the object that the user variable holds, even though we used  const to define it. This is because it only ensures the  assignment immutability .

On the other hand, when using TypeScript, we can use a const assertion .

const user = {
  name: 'Smith'
} as const;

Doing the above marks all of the properties of the above object as readonly .

If you want to know more, check out TypeScript type inference with const assertions and the infer keyword

Reasons to worry about value mutability

First, let’s look into why we might consider keeping our values immutable .

Keeping our structures immutable makes any changes to the data more apparent. Doing so makes our code easier to read.

function doubleTheNumbers(arrayOfNumbers) {
  for(let i = 0; i < arrayOfNumbers.length; ++i) {
    arrayOfNumbers[i] += arrayOfNumbers[i];
  }
}
const numbers = [1, 2, 3];
doubleTheNumbers(numbers);
 
console.log(numbers); // [2, 4, 6]

The doubleTheNumbers function doubles all of the provided numbers.

Right after writing all of the above, we know precisely how it works. Unfortunately, if the code is a lot longer and we come back to it after a few months, we might lose track of how we mutate our array. Also, such code is not very readable for other teammates.

function getDoubledNumbers(arrayOfNumbers) {
  return arrayOfNumbers.map(number => number * 2);
}
const numbers = [1, 2, 3];
const doubledNumbers = getDoubledNumbers(numbers);
 
console.log(numbers); // [1, 2, 3];
console.log(doubledNumbers); // [2, 4, 6];

Our getDoubledNumbers function returns a new array instead of modifying a new one. Since we now don’t perform a  side effect , we can consider the function  pure .

If you want to know more about pure functions and side effects, check out Improving our code with pure functions

Also, notice that the name of the function differs. Calling it getDoubledNumbers suggests that we now get a new array, instead of modifying the old one.

JavaScript has a lot of methods that operate on data without mutating it.

const doubleTheNumber = number => number * 2;
const doubledNumbers = numbers.map(doubleTheNumber);

The above approach really shines in terms of readability when chaining multiple operations.

Putting JavaScript aside for a second, immutability might prove to be useful when avoiding race conditions while performing multi-threaded operations.

On the other hand, Node.js makes an attempt of creating memory shared between threads using a SharedArrayBuffer . If you want to know more, check out  Node.js TypeScript #13. Sending data between Worker Threads

Summing up, making changes to our data in an apparent way helps us avoid trouble. Also, avoiding mutating data makes our codebase easier to test.

Achieving immutability using built-in features

The first thing that comes into mind when attempting to achieve immutability is Object.freeze .

Object frozen using the above function can no longer be changed. It means that we can’t add and remove properties, or change them in any way.

const user = Object.freeze({
  name: 'Smith'
});
user.name = 'John';

The above attempt to mutate the value fails silently or results in throwing an error if we are in a strict mode.

An interesting thought is that the Object . freeze mutates the data in a particular way. Instead of returning a new instance, it freezes the provided object.

Also, a very important thing is that the immutability we achieve above is shallow .

const user = Object.freeze({
  name: 'Smith',
  address: {
    street: 'Sesame Street',
    city: 'New York'
  }
});
user.address.city = 'Los Angeles';
console.log(user.address.city); // Los Angeles

If we would like to achieve deep immutability with Object.freeze , we would need to traverse the whole structure manually in a loop.

Object.seal

The Object.seal function is also somewhat associated with immutability. It does provide it to a lesser extent, though.

The above function seals an object by preventing us from adding or removing properties. We can still change existing properties.

const user = Object.seal({
  firstName: 'John'
});
user.firstName = 'James'; // works without issues
user.lastName = 'Smith'; // fails

Summary

The immutability of values is not about not changing them at all. It is more about tracking those changes in a more structured, dependable manner and therefore being more confident in our code. While there are some libraries and native features that can help us in ensuring immutability, we might just be better off treating our data structures as immutable.

All of the advantages mentioned in the above article seem worth pursuing. Unfortunately, declaring new values instead of mutating existing ones might impact performance. We need to consider multiple things. For example, if you create said structures just a few times throughout the life of your application, the impact almost certainly is not a concern. If you do it very frequently, you might want to reconsider your approach if you jump into performance issues. You might also want to look into libraries like Immutable.js .

Approaching our code with avoiding the mutation of data might be very useful, but we need to consider the context of our application. Just as other disciplines such as Test-driven Development, static type analysis, and Object-Oriented Programming, it has its uses. We, as the programmers, are responsible for making the decision based on the pros and cons.


以上所述就是小编给大家介绍的《What's the deal with immutability in JavaScript?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

思考的技术

思考的技术

[日]大前研一 / 刘锦秀、谢育容 / 中信出版社 / 2010-11 / 32.00元

思路决定出路,没有了思路,也就没有了出路。 在充满危机与冒险的当下,我们缺乏的不是技巧而是揭发事务本质的动力和好奇心,缺少怀疑一切的心态和对固有模式的怠惰。 大前研一凭借他30多年的管理咨询经验,为我们提供了一种全新的可借鉴的思考方式。 企业和个人惟有改变既有的思考模式,放弃对过去成功经验的迷恋,学习有创意的思考方法,方能找到正确的经营思路。一起来看看 《思考的技术》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器