# JavaScript Immutability
# Concept:
In JavaScript, if the variable is mutable, when we modifies the copy also the original variable get modified.
If variable is immutable, when we modifies the copy, the copy does not affect the original variable
.
2 major variable types:
- primitive type:
string
,null
,undefined
,boolean
,number
andsymbol
(Immutable) [(Unchangeable)] - reference type:
object
andarray
(Mutable) [(Changeable)]
We often use map
, filter
, reduce
array methods to deal with variable values in an immutable way
# Code Examples:
1). Primitive (Immutable) Type vs Reference (Mutable) Type
// Primitive Example
var a = 10;
var b = a;
b = 20;
console.log(a, b); // 10 20 -> value of 'a' stay unchanged
// original no change
// new value changed
// Reference Example
var x = {name: 'damon'};
var y = x;
y.name = 'unknown';
x.name; // unknown -> value of x get updated
// original changed
// new value changed
// Last note: JavaScript stores primitive value on the stack and stores reference value on the heap
2). Immutable way of handling data !!!!!
// filter array method:
var values = [{name: 'dameng', happy: true}, {name: 'unknown', happy: false}];
console.log('Output happy value(s)', values.filter(value => value.happy === true));
console.log('Output original values array: ', values); // values array stay unchanged
// spread operator: immutable way handle data (basically assign to a new variable)
const newValues = [...values, {name: 'haha', happy: 'true'}];
console.log('Output newValues array: ', newValues);
console.log('Output original values array: ', values); // values array stay unchanged
// slice is get certain data without changing the original variable value(s)
const numbers = [1,2,3,4];
numbers.slice(0,3); // output as [1,2,3]
// and now we output numbers, we get
// [1,2,3,4] // which is unchanged (original unchanged)
3). Difference between push
and concat
:
// push method: (is mutable)
const values = [];
const pushValues = values.push({name: 'hehe', happy: true});
console.log('Output pushValues array: ', pushValues); // 1
console.log('Output original values array: ', values);
// values array get changed form [] to [{name: 'hehe', happy: true}]
// concat method: (is immutable)
const concatValues = values.concat([{name: 'huhu', happy: true}]);
console.log('Output concatValues array: ', concatValues);
// [{name: 'hehe', happy: true}, {name: 'huhu', happy: true}]
console.log('Output original values array: ', values); // values array stay unchanged !!!
// Conclusion: in redux, we use concat instead of push to handle our data (Immutable way) !!!!
In React, immutability
makes state more predictable, that's why we need immutable
way to handle our variable values, especially state
.
Good tool: immutable.js
4). Common library Immutable.JS
usages:
Map()
:
Immutable.Map() // {} returns an empty object
// If we do this, Immutable.Map({ a: 1, b: 2})
// it will return {'a' => 1, 'b' => 2} as the result
const map1 = Immutable.Map({a: 1, b: 2});
const map2 = map1.set('b', 10);
console.log(map2); // {"a" => 1, "b" => 10} // get updated!!!
console.log(map1); // {"a" => 1, "b" => 2} // stay unchanged!
const menuArray = [['first', 'fries'],
['second', 'chips']];
// then, covert to Map:
Immutable.Map(menuArray) // {'first' => 'fries', 'second' => 'chips'}
List()
vs List.of()
List()
: takes a single parameter of some kind of iterable-like structure
List.of()
: use each parameter as a separate element in the created list
// Eg 1:
const str = 'text';
Immutable.List(str) // ['t', 'e' ,'x' ,'t']
Immutable.List.of(str) // ['text']
// Eg 2:
List.of("a", "b", "c"); // ["a", "b", "c"]
List(["a", "b", "c"]); // ["a", "b", "c"]
5). Beside Spread Operator, another way to make it immutable:
let object = { x: 1 }; // undefined
let mutableObject = object; // undefined
mutableObject.x = 2; // 2
mutableObject; // {x: 2}
object; // {x: 2}
let immutableObject = JSON.parse(JSON.stringify(object));
immutableObject.x = 3; // 3
object; // {x: 2}
immutableObject; // {x: 3}