1 | Immutable collections for JavaScript
|
---|
2 | ====================================
|
---|
3 |
|
---|
4 | [](https://travis-ci.org/facebook/immutable-js)
|
---|
5 |
|
---|
6 | [Immutable][] data cannot be changed once created, leading to much simpler
|
---|
7 | application development, no defensive copying, and enabling advanced memoization
|
---|
8 | and change detection techniques with simple logic. [Persistent][] data presents
|
---|
9 | a mutative API which does not update the data in-place, but instead always
|
---|
10 | yields new updated data.
|
---|
11 |
|
---|
12 | Immutable.js provides many Persistent Immutable data structures including:
|
---|
13 | `List`, `Stack`, `Map`, `OrderedMap`, `Set`, `OrderedSet` and `Record`.
|
---|
14 |
|
---|
15 | These data structures are highly efficient on modern JavaScript VMs by using
|
---|
16 | structural sharing via [hash maps tries][] and [vector tries][] as popularized
|
---|
17 | by Clojure and Scala, minimizing the need to copy or cache data.
|
---|
18 |
|
---|
19 | `Immutable` also provides a lazy `Seq`, allowing efficient
|
---|
20 | chaining of collection methods like `map` and `filter` without creating
|
---|
21 | intermediate representations. Create some `Seq` with `Range` and `Repeat`.
|
---|
22 |
|
---|
23 | Want to hear more? Watch the presentation about Immutable.js:
|
---|
24 |
|
---|
25 | <a href="https://youtu.be/I7IdS-PbEgI" target="_blank" alt="Immutable Data and React"><img src="https://img.youtube.com/vi/I7IdS-PbEgI/0.jpg" /></a>
|
---|
26 |
|
---|
27 | [Persistent]: http://en.wikipedia.org/wiki/Persistent_data_structure
|
---|
28 | [Immutable]: http://en.wikipedia.org/wiki/Immutable_object
|
---|
29 | [hash maps tries]: http://en.wikipedia.org/wiki/Hash_array_mapped_trie
|
---|
30 | [vector tries]: http://hypirion.com/musings/understanding-persistent-vector-pt-1
|
---|
31 |
|
---|
32 |
|
---|
33 | Getting started
|
---|
34 | ---------------
|
---|
35 |
|
---|
36 | Install `immutable` using npm.
|
---|
37 |
|
---|
38 | ```shell
|
---|
39 | npm install immutable
|
---|
40 | ```
|
---|
41 |
|
---|
42 | Then require it into any module.
|
---|
43 |
|
---|
44 | ```javascript
|
---|
45 | var Immutable = require('immutable');
|
---|
46 | var map1 = Immutable.Map({a:1, b:2, c:3});
|
---|
47 | var map2 = map1.set('b', 50);
|
---|
48 | map1.get('b'); // 2
|
---|
49 | map2.get('b'); // 50
|
---|
50 | ```
|
---|
51 |
|
---|
52 | ### Browser
|
---|
53 |
|
---|
54 | To use `immutable` from a browser, download [dist/immutable.min.js](https://github.com/facebook/immutable-js/blob/master/dist/immutable.min.js)
|
---|
55 | or use a CDN such as [CDNJS](https://cdnjs.com/libraries/immutable)
|
---|
56 | or [jsDelivr](http://www.jsdelivr.com/#!immutable.js).
|
---|
57 |
|
---|
58 | Then, add it as a script tag to your page:
|
---|
59 |
|
---|
60 | ```html
|
---|
61 | <script src="immutable.min.js"></script>
|
---|
62 | <script>
|
---|
63 | var map1 = Immutable.Map({a:1, b:2, c:3});
|
---|
64 | var map2 = map1.set('b', 50);
|
---|
65 | map1.get('b'); // 2
|
---|
66 | map2.get('b'); // 50
|
---|
67 | </script>
|
---|
68 | ```
|
---|
69 |
|
---|
70 | Or use an AMD loader (such as [RequireJS](http://requirejs.org/)):
|
---|
71 |
|
---|
72 | ```javascript
|
---|
73 | require(['./immutable.min.js'], function (Immutable) {
|
---|
74 | var map1 = Immutable.Map({a:1, b:2, c:3});
|
---|
75 | var map2 = map1.set('b', 50);
|
---|
76 | map1.get('b'); // 2
|
---|
77 | map2.get('b'); // 50
|
---|
78 | });
|
---|
79 | ```
|
---|
80 |
|
---|
81 | If you're using [browserify](http://browserify.org/), the `immutable` npm module
|
---|
82 | also works from the browser.
|
---|
83 |
|
---|
84 | ### TypeScript
|
---|
85 |
|
---|
86 | Use these Immutable collections and sequences as you would use native
|
---|
87 | collections in your [TypeScript](http://typescriptlang.org) programs while still taking
|
---|
88 | advantage of type generics, error detection, and auto-complete in your IDE.
|
---|
89 |
|
---|
90 | Just add a reference with a relative path to the type declarations at the top
|
---|
91 | of your file.
|
---|
92 |
|
---|
93 | ```javascript
|
---|
94 | ///<reference path='./node_modules/immutable/dist/immutable.d.ts'/>
|
---|
95 | import Immutable = require('immutable');
|
---|
96 | var map1: Immutable.Map<string, number>;
|
---|
97 | map1 = Immutable.Map({a:1, b:2, c:3});
|
---|
98 | var map2 = map1.set('b', 50);
|
---|
99 | map1.get('b'); // 2
|
---|
100 | map2.get('b'); // 50
|
---|
101 | ```
|
---|
102 |
|
---|
103 |
|
---|
104 | The case for Immutability
|
---|
105 | -------------------------
|
---|
106 |
|
---|
107 | Much of what makes application development difficult is tracking mutation and
|
---|
108 | maintaining state. Developing with immutable data encourages you to think
|
---|
109 | differently about how data flows through your application.
|
---|
110 |
|
---|
111 | Subscribing to data events throughout your application creates a huge overhead of
|
---|
112 | book-keeping which can hurt performance, sometimes dramatically, and creates
|
---|
113 | opportunities for areas of your application to get out of sync with each other
|
---|
114 | due to easy to make programmer error. Since immutable data never changes,
|
---|
115 | subscribing to changes throughout the model is a dead-end and new data can only
|
---|
116 | ever be passed from above.
|
---|
117 |
|
---|
118 | This model of data flow aligns well with the architecture of [React][]
|
---|
119 | and especially well with an application designed using the ideas of [Flux][].
|
---|
120 |
|
---|
121 | When data is passed from above rather than being subscribed to, and you're only
|
---|
122 | interested in doing work when something has changed, you can use equality.
|
---|
123 |
|
---|
124 | Immutable collections should be treated as *values* rather than *objects*. While
|
---|
125 | objects represents some thing which could change over time, a value represents
|
---|
126 | the state of that thing at a particular instance of time. This principle is most
|
---|
127 | important to understanding the appropriate use of immutable data. In order to
|
---|
128 | treat Immutable.js collections as values, it's important to use the
|
---|
129 | `Immutable.is()` function or `.equals()` method to determine value equality
|
---|
130 | instead of the `===` operator which determines object reference identity.
|
---|
131 |
|
---|
132 | ```javascript
|
---|
133 | var map1 = Immutable.Map({a:1, b:2, c:3});
|
---|
134 | var map2 = map1.set('b', 2);
|
---|
135 | assert(map1.equals(map2) === true);
|
---|
136 | var map3 = map1.set('b', 50);
|
---|
137 | assert(map1.equals(map3) === false);
|
---|
138 | ```
|
---|
139 |
|
---|
140 | Note: As a performance optimization `Immutable` attempts to return the existing
|
---|
141 | collection when an operation would result in an identical collection, allowing
|
---|
142 | for using `===` reference equality to determine if something definitely has not
|
---|
143 | changed. This can be extremely useful when used within memoization function
|
---|
144 | which would prefer to re-run the function if a deeper equality check could
|
---|
145 | potentially be more costly. The `===` equality check is also used internally by
|
---|
146 | `Immutable.is` and `.equals()` as a performance optimization.
|
---|
147 |
|
---|
148 | If an object is immutable, it can be "copied" simply by making another reference
|
---|
149 | to it instead of copying the entire object. Because a reference is much smaller
|
---|
150 | than the object itself, this results in memory savings and a potential boost in
|
---|
151 | execution speed for programs which rely on copies (such as an undo-stack).
|
---|
152 |
|
---|
153 | ```javascript
|
---|
154 | var map1 = Immutable.Map({a:1, b:2, c:3});
|
---|
155 | var clone = map1;
|
---|
156 | ```
|
---|
157 |
|
---|
158 | [React]: http://facebook.github.io/react/
|
---|
159 | [Flux]: http://facebook.github.io/flux/docs/overview.html
|
---|
160 |
|
---|
161 |
|
---|
162 | JavaScript-first API
|
---|
163 | --------------------
|
---|
164 |
|
---|
165 | While `immutable` is inspired by Clojure, Scala, Haskell and other functional
|
---|
166 | programming environments, it's designed to bring these powerful concepts to
|
---|
167 | JavaScript, and therefore has an Object-Oriented API that closely mirrors that
|
---|
168 | of [ES6][] [Array][], [Map][], and [Set][].
|
---|
169 |
|
---|
170 | [ES6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla
|
---|
171 | [Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
---|
172 | [Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
---|
173 | [Set]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
---|
174 |
|
---|
175 | The difference for the immutable collections is that methods which would mutate
|
---|
176 | the collection, like `push`, `set`, `unshift` or `splice` instead return a new
|
---|
177 | immutable collection. Methods which return new arrays like `slice` or `concat`
|
---|
178 | instead return new immutable collections.
|
---|
179 |
|
---|
180 | ```javascript
|
---|
181 | var list1 = Immutable.List.of(1, 2);
|
---|
182 | var list2 = list1.push(3, 4, 5);
|
---|
183 | var list3 = list2.unshift(0);
|
---|
184 | var list4 = list1.concat(list2, list3);
|
---|
185 | assert(list1.size === 2);
|
---|
186 | assert(list2.size === 5);
|
---|
187 | assert(list3.size === 6);
|
---|
188 | assert(list4.size === 13);
|
---|
189 | assert(list4.get(0) === 1);
|
---|
190 | ```
|
---|
191 |
|
---|
192 | Almost all of the methods on [Array][] will be found in similar form on
|
---|
193 | `Immutable.List`, those of [Map][] found on `Immutable.Map`, and those of [Set][]
|
---|
194 | found on `Immutable.Set`, including collection operations like `forEach()`
|
---|
195 | and `map()`.
|
---|
196 |
|
---|
197 | ```javascript
|
---|
198 | var alpha = Immutable.Map({a:1, b:2, c:3, d:4});
|
---|
199 | alpha.map((v, k) => k.toUpperCase()).join();
|
---|
200 | // 'A,B,C,D'
|
---|
201 | ```
|
---|
202 |
|
---|
203 | ### Accepts raw JavaScript objects.
|
---|
204 |
|
---|
205 | Designed to inter-operate with your existing JavaScript, `immutable`
|
---|
206 | accepts plain JavaScript Arrays and Objects anywhere a method expects an
|
---|
207 | `Iterable` with no performance penalty.
|
---|
208 |
|
---|
209 | ```javascript
|
---|
210 | var map1 = Immutable.Map({a:1, b:2, c:3, d:4});
|
---|
211 | var map2 = Immutable.Map({c:10, a:20, t:30});
|
---|
212 | var obj = {d:100, o:200, g:300};
|
---|
213 | var map3 = map1.merge(map2, obj);
|
---|
214 | // Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
|
---|
215 | ```
|
---|
216 |
|
---|
217 | This is possible because `immutable` can treat any JavaScript Array or Object
|
---|
218 | as an Iterable. You can take advantage of this in order to get sophisticated
|
---|
219 | collection methods on JavaScript Objects, which otherwise have a very sparse
|
---|
220 | native API. Because Seq evaluates lazily and does not cache intermediate
|
---|
221 | results, these operations can be extremely efficient.
|
---|
222 |
|
---|
223 | ```javascript
|
---|
224 | var myObject = {a:1,b:2,c:3};
|
---|
225 | Immutable.Seq(myObject).map(x => x * x).toObject();
|
---|
226 | // { a: 1, b: 4, c: 9 }
|
---|
227 | ```
|
---|
228 |
|
---|
229 | Keep in mind, when using JS objects to construct Immutable Maps, that
|
---|
230 | JavaScript Object properties are always strings, even if written in a quote-less
|
---|
231 | shorthand, while Immutable Maps accept keys of any type.
|
---|
232 |
|
---|
233 | ```js
|
---|
234 | var obj = { 1: "one" };
|
---|
235 | Object.keys(obj); // [ "1" ]
|
---|
236 | obj["1"]; // "one"
|
---|
237 | obj[1]; // "one"
|
---|
238 |
|
---|
239 | var map = Immutable.fromJS(obj);
|
---|
240 | map.get("1"); // "one"
|
---|
241 | map.get(1); // undefined
|
---|
242 | ```
|
---|
243 |
|
---|
244 | Property access for JavaScript Objects first converts the key to a string, but
|
---|
245 | since Immutable Map keys can be of any type the argument to `get()` is
|
---|
246 | not altered.
|
---|
247 |
|
---|
248 |
|
---|
249 | ### Converts back to raw JavaScript objects.
|
---|
250 |
|
---|
251 | All `immutable` Iterables can be converted to plain JavaScript Arrays and
|
---|
252 | Objects shallowly with `toArray()` and `toObject()` or deeply with `toJS()`.
|
---|
253 | All Immutable Iterables also implement `toJSON()` allowing them to be passed to
|
---|
254 | `JSON.stringify` directly.
|
---|
255 |
|
---|
256 | ```javascript
|
---|
257 | var deep = Immutable.Map({ a: 1, b: 2, c: Immutable.List.of(3, 4, 5) });
|
---|
258 | deep.toObject() // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
|
---|
259 | deep.toArray() // [ 1, 2, List [ 3, 4, 5 ] ]
|
---|
260 | deep.toJS() // { a: 1, b: 2, c: [ 3, 4, 5 ] }
|
---|
261 | JSON.stringify(deep) // '{"a":1,"b":2,"c":[3,4,5]}'
|
---|
262 | ```
|
---|
263 |
|
---|
264 | ### Embraces ES6
|
---|
265 |
|
---|
266 | `Immutable` takes advantage of features added to JavaScript in [ES6][],
|
---|
267 | the latest standard version of ECMAScript (JavaScript), including [Iterators][],
|
---|
268 | [Arrow Functions][], [Classes][], and [Modules][]. It's also inspired by the
|
---|
269 | [Map][] and [Set][] collections added to ES6. The library is "transpiled" to ES3
|
---|
270 | in order to support all modern browsers.
|
---|
271 |
|
---|
272 | All examples are presented in ES6. To run in all browsers, they need to be
|
---|
273 | translated to ES3.
|
---|
274 |
|
---|
275 | ```js
|
---|
276 | // ES6
|
---|
277 | foo.map(x => x * x);
|
---|
278 | // ES3
|
---|
279 | foo.map(function (x) { return x * x; });
|
---|
280 | ```
|
---|
281 |
|
---|
282 | [Iterators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol
|
---|
283 | [Arrow Functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
|
---|
284 | [Classes]: http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes
|
---|
285 | [Modules]: http://www.2ality.com/2014/09/es6-modules-final.html
|
---|
286 |
|
---|
287 |
|
---|
288 | Nested Structures
|
---|
289 | -----------------
|
---|
290 |
|
---|
291 | The collections in `immutable` are intended to be nested, allowing for deep
|
---|
292 | trees of data, similar to JSON.
|
---|
293 |
|
---|
294 | ```javascript
|
---|
295 | var nested = Immutable.fromJS({a:{b:{c:[3,4,5]}}});
|
---|
296 | // Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }
|
---|
297 | ```
|
---|
298 |
|
---|
299 | A few power-tools allow for reading and operating on nested data. The
|
---|
300 | most useful are `mergeDeep`, `getIn`, `setIn`, and `updateIn`, found on `List`,
|
---|
301 | `Map` and `OrderedMap`.
|
---|
302 |
|
---|
303 | ```javascript
|
---|
304 | var nested2 = nested.mergeDeep({a:{b:{d:6}}});
|
---|
305 | // Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }
|
---|
306 | ```
|
---|
307 |
|
---|
308 | ```javascript
|
---|
309 | nested2.getIn(['a', 'b', 'd']); // 6
|
---|
310 |
|
---|
311 | var nested3 = nested2.updateIn(['a', 'b', 'd'], value => value + 1);
|
---|
312 | // Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }
|
---|
313 |
|
---|
314 | var nested4 = nested3.updateIn(['a', 'b', 'c'], list => list.push(6));
|
---|
315 | // Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }
|
---|
316 | ```
|
---|
317 |
|
---|
318 |
|
---|
319 | Lazy Seq
|
---|
320 | --------
|
---|
321 |
|
---|
322 | `Seq` describes a lazy operation, allowing them to efficiently chain
|
---|
323 | use of all the Iterable methods (such as `map` and `filter`).
|
---|
324 |
|
---|
325 | **Seq is immutable** — Once a Seq is created, it cannot be
|
---|
326 | changed, appended to, rearranged or otherwise modified. Instead, any mutative
|
---|
327 | method called on a Seq will return a new Seq.
|
---|
328 |
|
---|
329 | **Seq is lazy** — Seq does as little work as necessary to respond to any
|
---|
330 | method call.
|
---|
331 |
|
---|
332 | For example, the following does not perform any work, because the resulting
|
---|
333 | Seq is never used:
|
---|
334 |
|
---|
335 | var oddSquares = Immutable.Seq.of(1,2,3,4,5,6,7,8)
|
---|
336 | .filter(x => x % 2).map(x => x * x);
|
---|
337 |
|
---|
338 | Once the Seq is used, it performs only the work necessary. In this
|
---|
339 | example, no intermediate arrays are ever created, filter is called three times,
|
---|
340 | and map is only called twice:
|
---|
341 |
|
---|
342 | console.log(oddSquares.get(1)); // 9
|
---|
343 |
|
---|
344 | Any collection can be converted to a lazy Seq with `.toSeq()`.
|
---|
345 |
|
---|
346 | var seq = Immutable.Map({a:1, b:1, c:1}).toSeq();
|
---|
347 |
|
---|
348 | Seq allow for the efficient chaining of sequence operations, especially when
|
---|
349 | converting to a different concrete type (such as to a JS object):
|
---|
350 |
|
---|
351 | seq.flip().map(key => key.toUpperCase()).flip().toObject();
|
---|
352 | // Map { A: 1, B: 1, C: 1 }
|
---|
353 |
|
---|
354 | As well as expressing logic that would otherwise seem memory-limited:
|
---|
355 |
|
---|
356 | Immutable.Range(1, Infinity)
|
---|
357 | .skip(1000)
|
---|
358 | .map(n => -n)
|
---|
359 | .filter(n => n % 2 === 0)
|
---|
360 | .take(2)
|
---|
361 | .reduce((r, n) => r * n, 1);
|
---|
362 | // 1006008
|
---|
363 |
|
---|
364 | Note: An iterable is always iterated in the same order, however that order may
|
---|
365 | not always be well defined, as is the case for the `Map`.
|
---|
366 |
|
---|
367 |
|
---|
368 | Equality treats Collections as Data
|
---|
369 | -----------------------------------
|
---|
370 |
|
---|
371 | `Immutable` provides equality which treats immutable data structures as pure
|
---|
372 | data, performing a deep equality check if necessary.
|
---|
373 |
|
---|
374 | ```javascript
|
---|
375 | var map1 = Immutable.Map({a:1, b:1, c:1});
|
---|
376 | var map2 = Immutable.Map({a:1, b:1, c:1});
|
---|
377 | assert(map1 !== map2); // two different instances
|
---|
378 | assert(Immutable.is(map1, map2)); // have equivalent values
|
---|
379 | assert(map1.equals(map2)); // alternatively use the equals method
|
---|
380 | ```
|
---|
381 |
|
---|
382 | `Immutable.is()` uses the same measure of equality as [Object.is][]
|
---|
383 | including if both are immutable and all keys and values are equal
|
---|
384 | using the same measure of equality.
|
---|
385 |
|
---|
386 | [Object.is]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
---|
387 |
|
---|
388 |
|
---|
389 | Batching Mutations
|
---|
390 | ------------------
|
---|
391 |
|
---|
392 | > If a tree falls in the woods, does it make a sound?
|
---|
393 | >
|
---|
394 | > If a pure function mutates some local data in order to produce an immutable
|
---|
395 | > return value, is that ok?
|
---|
396 | >
|
---|
397 | > — Rich Hickey, Clojure
|
---|
398 |
|
---|
399 | Applying a mutation to create a new immutable object results in some overhead,
|
---|
400 | which can add up to a minor performance penalty. If you need to apply a series
|
---|
401 | of mutations locally before returning, `Immutable` gives you the ability to
|
---|
402 | create a temporary mutable (transient) copy of a collection and apply a batch of
|
---|
403 | mutations in a performant manner by using `withMutations`. In fact, this is
|
---|
404 | exactly how `Immutable` applies complex mutations itself.
|
---|
405 |
|
---|
406 | As an example, building `list2` results in the creation of 1, not 3, new
|
---|
407 | immutable Lists.
|
---|
408 |
|
---|
409 | ```javascript
|
---|
410 | var list1 = Immutable.List.of(1,2,3);
|
---|
411 | var list2 = list1.withMutations(function (list) {
|
---|
412 | list.push(4).push(5).push(6);
|
---|
413 | });
|
---|
414 | assert(list1.size === 3);
|
---|
415 | assert(list2.size === 6);
|
---|
416 | ```
|
---|
417 |
|
---|
418 | Note: `immutable` also provides `asMutable` and `asImmutable`, but only
|
---|
419 | encourages their use when `withMutations` will not suffice. Use caution to not
|
---|
420 | return a mutable copy, which could result in undesired behavior.
|
---|
421 |
|
---|
422 | *Important!*: Only a select few methods can be used in `withMutations` including
|
---|
423 | `set`, `push` and `pop`. These methods can be applied directly against a
|
---|
424 | persistent data-structure where other methods like `map`, `filter`, `sort`,
|
---|
425 | and `splice` will always return new immutable data-structures and never mutate
|
---|
426 | a mutable collection.
|
---|
427 |
|
---|
428 |
|
---|
429 | Documentation
|
---|
430 | -------------
|
---|
431 |
|
---|
432 | [Read the docs](http://facebook.github.io/immutable-js/docs/) and eat your vegetables.
|
---|
433 |
|
---|
434 | Docs are automatically generated from [Immutable.d.ts](https://github.com/facebook/immutable-js/blob/master/type-definitions/Immutable.d.ts).
|
---|
435 | Please contribute!
|
---|
436 |
|
---|
437 | Also, don't miss the [Wiki](https://github.com/facebook/immutable-js/wiki) which
|
---|
438 | contains articles on specific topics. Can't find something? Open an [issue](https://github.com/facebook/immutable-js/issues).
|
---|
439 |
|
---|
440 |
|
---|
441 | Testing
|
---|
442 | -------
|
---|
443 |
|
---|
444 | If you are using the [Chai Assertion Library](http://chaijs.com/), [Chai Immutable](https://github.com/astorije/chai-immutable) provides a set of assertions to use against `Immutable` collections.
|
---|
445 |
|
---|
446 |
|
---|
447 | Contribution
|
---|
448 | ------------
|
---|
449 |
|
---|
450 | Use [Github issues](https://github.com/facebook/immutable-js/issues) for requests.
|
---|
451 |
|
---|
452 | We actively welcome pull requests, learn how to [contribute](./CONTRIBUTING.md).
|
---|
453 |
|
---|
454 |
|
---|
455 | Changelog
|
---|
456 | ---------
|
---|
457 |
|
---|
458 | Changes are tracked as [Github releases](https://github.com/facebook/immutable-js/releases).
|
---|
459 |
|
---|
460 |
|
---|
461 | Thanks
|
---|
462 | ------
|
---|
463 |
|
---|
464 | [Phil Bagwell](https://www.youtube.com/watch?v=K2NYwP90bNs), for his inspiration
|
---|
465 | and research in persistent data structures.
|
---|
466 |
|
---|
467 | [Hugh Jackson](https://github.com/hughfdjackson/), for providing the npm package
|
---|
468 | name. If you're looking for his unsupported package, see [this repository](https://github.com/hughfdjackson/immutable).
|
---|
469 |
|
---|
470 |
|
---|
471 | License
|
---|
472 | -------
|
---|
473 |
|
---|
474 | Immutable.js is [MIT-licensed](https://github.com/facebook/immutable-js/blob/master/LICENSE).
|
---|