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?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

人工智能基础

人工智能基础

汤晓鸥、陈玉琨 / 华东师范大学出版社 / 2018-4-1 / 35.00元

人工智能基础(高中版)》是面向高中学生的教材。讲授人工智能的发展历史、基本概念以及实际应用,使学生理解人工智能的基本原理,特别是数据、算法与应用之间的相互关系。并结合常见的应用场景,理解人工智能技术(包括感知与决策)的基本工作方式,通过动手实践,更深入地理解人工智能技术的原理、能力,以及在实用中面临的挑战。本书强调人工智能基本理念与原理的传递,注重创造力、想象力、整体思考,以及动手能力的提升。一起来看看 《人工智能基础》 这本书的介绍吧!

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

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具