Immutability ( Immutability
) is a core principle of functional programming, and it has a lot of applications in object-oriented programming. In this article, I'll show you what immutability is ( Immutability
), why she's still so badass, and how to apply it in JavaScript
.
What is immutability ( Immutability
)?
Let's take a look at the dogmatic definition of variability ( Mutability
): "liable or subject to change or alteration" Bar)". In programming, we use mutability ( Mutability
) to describe an object whose state can still be changed after it is created. Then when we say immutable ( Immutable
), it is the opposite of mutable ( Mutable
) - meaning, Once created, it can no longer be modified.
If what I said makes you feel weird again, forgive me for a little reminder, in fact, many things we usually use are actually immutable!
var statement = 'I am an immutable value';
var otherStr = statement.slice(8, 17);
I guess no one will be surprised that statement.slice(8, 17)
has not changed statement
variables? In fact, none of the methods on the string
object modify the original string
, they all return the new string
. The reason is simple, because string
is immutable ( Immutable
) - they cannot be modified, what we can do is to get one based on the original string
operation New string
.
Note that string
JavaScript
not the only built-in immutable ( Immutable
) data type in ---8f34ad8fcddc592ee04f597d0eb9f371---. number
is also immutable ( Immutable
). Otherwise, try to imagine this expression 2 + 3
, if the meaning of 2
can be modified, how should the code be written|_|. It sounds absurd, but we often do this to object
and array
in programming.
JavaScript is full of changes
In JavaScript
, string
and number
are immutable from the beginning ( Immutable
) by design. However, take a look at the following example about array
:
var arr = [];
var v2 = arr.push(2);
Let me ask you, what is the value of v2
? array
88141959977ee4f86ecf09d880ef912d---和string
、 number
变( Immutable
)的,那v2
is a new array
containing a number 2
2ba1e8658c164b4d5b7984fd40434abf---. In fact, it really isn't like that.这里arr
引用的array
修改了,里面添了一个数字2
,这时v2
的值(也就是arr.push(2)
The return value of arr.push(2)
) is actually arr
the length at this time- is 1
.
Imagine we have an immutable array ( ImmutableArray
). Like string
, number
, she should be able to be used like this:
var arr = new ImmutableArray([1, 2, 3, 4]);
var v2 = arr.push(5);
arr.toArray(); // [1, 2, 3, 4]
v2.toArray(); // [1, 2, 3, 4, 5]
Similarly, there can also be an immutable Map ( ImmutableMap
), which can theoretically replace object
in most scenarios, she should have a set
method, But this set
method doesn't stuff anything into the original Map
, but returns a new Map
containing the stuffed value:
var person = new ImmutableMap({name: 'Chris', age: 32});
var olderPerson = person.set('age', 33);
person.toObject(); // {name: 'Chris', age: 32}
olderPerson.toObject(); // {name: 'Chris', age: 33}
Like 2 + 3
in this expression, we can't change the meaning of 2
or 3
which is celebrated in his person
His 33rd birthday doesn't affect the fact that he was once 32.
JavaScript immutability ( Immutability
) combat
There are no immutable JavaScript
list
and map
, so we still need the help of the third party library for now. There are two very good ones, one is Mori Facebook
she brought the ClojureScript
API
to JavaScript
; Facebook
produced immutable.js . In the following examples, I will use immutable.js because her API
is more developer friendly to JavaScript
.
In the following example, we use immutable ( Immutable
) knowledge to build a Minesweeper game. We use an immutable map
to build the minesweeper game panel, of which the tiles
(minefield block) part is worth paying attention to, it is an immutable map
-Composed of immutable map
list
(Translator's Note: Started to circle again), each of which is immutable map
represents a tile
(mine block) .整个这个雷区面板都是由JavaScript
的object
array
,最后由immutable.js的fromJS
Do immutable processing:
function createGame(options) {
return Immutable.fromJS({
cols: options.cols,
rows: options.rows,
tiles: initTiles(options.rows, options.cols, options.mines)
});
}
The remaining main logic part is "mine sweeping", pass in the mine sweeping game object (an immutable structure) as the first parameter, and the one to be "sweeped" tile
(mine block) object , and finally returns the new minesweeper game instance. Below we will talk about this revealTile
function. When it is called, the state of tile
(the mine block) is reset to the "swept" state. In the case of variable programming, the code is simple:
function revealTile(game, tile) {
game.tiles[tile].isRevealed = true;
}
Then look at the immutable data structure introduced above, and frankly, the code becomes a bit ugly at first:
function revealTile(game, tile) {
var updatedTile = game.get('tiles').get(tile).set('isRevealed', true);
var updatedTiles = game.get('tiles').set(tile, updatedTile);
return game.set('tiles', updatedTiles);
}
I'm going, it's ugly and there is no wood!
Fortunately, immutability does not stop there, there must be salvation! This need is very common, so the tool has long considered it, and can do this:
function revealTile(game, tile) {
return game.setIn(['tiles', tile, 'isRevealed'], true);
}
revealTile
返回一个新的实例了,新tile
(雷区块)的isRevealed
就game
The example is different. The setIn
used here is a function of null-safe
(null value safe), any keyPath
in key
will create a new immutable at this location map
It is not clear, the original work did not elaborate, so I will not say more, and those who are interested can come here to figure it out for themselves). This null-safe
feature is not suitable for our current example of a minesweeper game, because "sweeping" a non-existent tile
(mine block) means that we are trying to clear a place outside the minefield, That's clearly not right! Here we need to do one more check, check whether tile
(mine block) exists through the getIn
method, and then "scan" it:
function revealTile(game, tile) {
return game.getIn(['tiles', tile]) ?
game.setIn(['tiles', tile, 'isRevealed'], true) :
game;
}
If tile
(mine block) does not exist, we return to the original minesweeper game instance. This is an exercise about immutability ( Immutability
) that can be quickly started. If you want to know more, you can see the codepen and click the preview. The complete implementation is inside.
How is the performance?
You might think, this fucking Performance
should low
explode, I can only say that in some cases you are right. Whenever you want to add something to an immutable ( Immutable
) object, it must first copy the existing value to the new instance, then add content to the new instance, and finally return the new instance. Compared with mutable objects, this will inevitably consume more memory and computation.
Because immutable ( Immutable
) objects never change, there is actually an implementation strategy called "struct sharing", which makes her memory consumption much less than you think. Although there is still additional overhead compared to the built-in "change" of array
, object
, this starts constant and can definitely be made immutable ( Immutability
) are eliminated and reduced by many other advantages brought by . In practice, the advantages of immutability ( Immutability
) can greatly optimize the overall performance of a program, even if some of the individual operations are more expensive.
Improve change tracking
Various UI
frameworks, the hardest part is always change tracking. This is JavaScript
a common problem in the community, so EcmaScript 7
provides a separate API
that can be tracked on the premise that Performance
changes : Object.observe()
. Many people are excited about it, but many people think that this API
is not the same. They believe that, in any case, this API
does not solve the change tracking problem well:
var tiles = [{id: 0, isRevealed: false}, {id: 1, isRevealed: true}];
Object.observe(tiles, function () { /* ... */ });
tiles[0].id = 2;
In the above example, the change of tiles[0]
did not trigger observer
, so in fact this proposal fails even the simplest change tracking. How is immutability ( Immutability
) solved? Suppose there is an application state a
, and then its internal value is changed, so a new instance b
is obtained:
if (a === b) {
// 数据没变,停止操作
}
a
没有被修改,那b
就是a
,它们指向同一个实例, ===
做other things. Of course, this requires us to track the reference of the application state, but the complexity of the whole problem has been greatly simplified. Now we only need to judge whether they are references to the same instance. I really don't need to investigate whether the certain field in it has changed. .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。