UNPKG

17 kBMarkdownView Raw
1seamless-immutable
2==================
3
4Immutable JS data structures which are backwards-compatible with normal Arrays and Objects.
5
6Use them in `for` loops, pass them to functions expecting vanilla JavaScript data structures, etc.
7
8```javascript
9var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]);
10
11array[1] = "I'm going to mutate you!"
12array[1] // "immutable"
13
14array[2].hammer = "hm, surely I can mutate this nested object..."
15array[2].hammer // "Can’t Touch This"
16
17for (var index in array) { console.log(array[index]); }
18// "totally"
19// "immutable"
20// { hammer: 'Can’t Touch This' }
21
22JSON.stringify(array) // '["totally","immutable",{"hammer":"Can’t Touch This"}]'
23```
24
25This level of backwards compatibility requires [ECMAScript 5](http://kangax.github.io/compat-table/es5/) features like [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) and [Object.freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) to exist and work correctly, which limits the browsers that can use this library to the ones shown in the test results below. (tl;dr [IE9+](https://saucelabs.com/u/seamless-immutable))
26
27[![build status][1]][2] [![NPM version][3]][4] [![coverage status][5]][6]
28
29[![Sauce Test Status](https://saucelabs.com/browser-matrix/seamless-immutable.svg)](https://saucelabs.com/u/seamless-immutable)
30
31## Performance
32
33Whenever you deeply clone large nested objects, it should typically go much faster with `Immutable` data structures. This is because the library reuses the existing nested objects rather than instantiating new ones.
34
35In the development build, objects are [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze). (Note that [Safari is relatively slow to iterate over frozen objects](http://jsperf.com/performance-frozen-object/20).) The development build also overrides unsupported methods (methods that ordinarily mutate the underlying data structure) to throw helpful exceptions.
36
37The production (minified) build does neither of these, which significantly improves performance.
38
39We generally recommend to use the "development" build that enforces immutability (and this is the default in Node.js). Only switch to the production build when you encounter performance problems. (See #50 for how to do that in Node or using a build tool - essentially do explicitely refer to the production build.)
40
41## Intentional Abstraction Leaks
42
43By popular demand, functions, dates, and [React](https://facebook.github.io/react/)
44components are treated as immutable even though technically they can be mutated.
45(It turns out that trying to make these immutable leads to more bad things
46than good.) If you call `Immutable()` on any of these, be forewarned: they will
47not actually be immutable!
48
49## API Overview
50
51`Immutable()` returns a backwards-compatible immutable representation of whatever you pass it, so feel free to pass it absolutely anything that can be serialized as JSON. (As is the case with JSON, objects containing circular references are not allowed. Functions are allowed, unlike in JSON, but they will not be touched.)
52
53Since numbers, strings, `undefined`, and `null` are all immutable to begin with, the only unusual things it returns are Immutable Arrays and Immutable Objects. These have the same [ES5 methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) you’re used to seeing on them, but with these important differences:
54
551. All the methods that would normally mutate the data structures instead throw `ImmutableError`.
562. All the methods that return a relevant value now return an immutable equivalent of that value.
573. Attempting to reassign values to their elements (e.g. `foo[5] = bar`) will not work. Browsers other than Internet Explorer will throw a `TypeError` if [use strict](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) is enabled, and in all other cases it will fail silently.
584. A few additional methods have been added for convenience.
59
60For example:
61
62```javascript
63Immutable([3, 1, 4]).sort()
64// This will throw an ImmutableError, because sort() is a mutating method.
65
66Immutable([1, 2, 3]).concat([10, 9, 8]).sort()
67// This will also throw ImmutableError, because an Immutable Array's methods
68// (including concat()) are guaranteed to return other immutable values.
69
70[1, 2, 3].concat(Immutable([6, 5, 4])).sort()
71// This will succeed, and will yield a sorted mutable array containing
72// [1, 2, 3, 4, 5, 6], because a vanilla array's concat() method has
73// no knowledge of Immutable.
74
75Immutable({all: "your base", are: {belong: "to them"}}).merge({are: {belong: "to us"}})
76// This handy new method will return the following:
77// Immutable({all: "your base", are: {belong: "to us"}})
78```
79
80## Immutable.from
81
82If your linter cringes with the use of `Immutable` without a preceding `new`
83(e.g. ESLint's [new-cap](http://eslint.org/docs/rules/new-cap) rule),
84use `Immutable.from`:
85
86```javascript
87Immutable.from([1, 2, 3]);
88// is functionally the same as calling:
89Immutable([1, 2, 3])
90```
91
92## Immutable Array
93
94Like a regular Array, but immutable! You can construct these by passing
95an array to `Immutable()`:
96
97```javascript
98Immutable([1, 2, 3])
99// An immutable array containing 1, 2, and 3.
100```
101
102Beyond [the usual Array fare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Accessor_methods), the following methods have been added.
103
104### flatMap
105
106```javascript
107Immutable(["here", "we", "go"]).flatMap(function(str) {
108 return [str, str, str];
109});
110// returns Immutable(["here", "here", "here", "we", "we", "we", "go", "go", "go"])
111
112Immutable(["drop the numbers!", 3, 2, 1, 0, null, undefined]).flatMap(function(value) {
113 if (typeof value === "number") {
114 return [];
115 } else {
116 return value;
117 }
118});
119// returns Immutable(["drop the numbers!", null, undefined])
120```
121
122Effectively performs a [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) over the elements in the array, except that whenever the provided
123iterator function returns an Array, that Array's elements are each added to the final result.
124
125### asObject
126
127```javascript
128Immutable(["hey", "you"]).asObject(function(str) {
129 return [str, str.toUpperCase()];
130});
131// returns Immutable({hey: "HEY", you: "YOU"})
132```
133
134Effectively performs a [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) over the elements in the array, expecting that the iterator function
135will return an array of two elements - the first representing a key, the other
136a value. Then returns an Immutable Object constructed of those keys and values.
137
138You can also call `.asObject` without passing an iterator, in which case it will proceed assuming the Array
139is already organized as desired.
140
141### asMutable
142
143```javascript
144var mutableArray = Immutable(["hello", "world"]).asMutable();
145
146mutableArray.push("!!!");
147
148mutableArray // ["hello", "world", "!!!"]
149```
150
151Returns a mutable copy of the array. For a deeply mutable copy, in which any instances of `Immutable` contained in nested data structures within the array have been converted back to mutable data structures, call `.asMutable({deep: true})` instead.
152
153### All object and array methods
154
155Every other methods on immutable objects and arrays can also be used as static
156methods of `Immutable`. For instance, the lines below are equivalent:
157
158```
159obj.setIn(['key'], value);
160
161Immutable.setIn(obj, ['key'], value);
162```
163
164## Immutable Object
165
166Like a regular Object, but immutable! You can construct these by passing an
167object to `Immutable()`.
168
169```javascript
170Immutable({foo: "bar"})
171// An immutable object containing the key "foo" and the value "bar".
172```
173
174To construct an Immutable Object with a custom prototype, simply specify the
175prototype in `options` (while useful for preserving prototypes, please note
176that custom mutator methods will not work as the object will be immutable):
177
178```javascript
179function Square(length) { this.length = length };
180Square.prototype.area = function() { return Math.pow(this.length, 2) };
181
182Immutable(new Square(2), {prototype: Square.prototype}).area();
183// An immutable object, with prototype Square,
184// containing the key "length" and method `area()` returning 4
185```
186
187Beyond [the usual Object fare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#Methods_of_Object_instances), the following methods have been added.
188
189### Stack overflow protection
190
191Currently you can't construct Immutable from an object with circular references. To protect from ugly stack overflows, we provide a simple protection during development. We stop at a suspiciously deep stack level and [show an error message][deep].
192
193If your objects are deep, but not circular, you can increase this level from default `64`. For example:
194
195```javascript
196Immutable(deepObject, null, 256);
197```
198
199This check is not performed in the production build.
200
201[deep]: https://github.com/rtfeldman/seamless-immutable/wiki/Deeply-nested-object-was-detected
202
203### merge
204
205```javascript
206Immutable({status: "good", hypothesis: "plausible", errors: 0}).merge({status: "funky", hypothesis: "confirmed"})
207// returns Immutable({status: "funky", hypothesis: "confirmed", errors: 0})
208
209Immutable({status: "bad", errors: 37}).merge([
210 {status: "funky", errors: 1}, {status: "groovy", errors: 2}, {status: "sweet"}])
211// returns Immutable({status: "sweet", errors: 2})
212// because passing an Array (or just multiple arguments) is shorthand for
213// invoking a separate merge for each object in turn.
214```
215Returns an Immutable Object containing the properties and values of both
216this object and the provided object, prioritizing the provided object's
217values whenever the same key is present in both objects.
218
219Multiple objects can be provided in an Array in which case more `merge`
220invocations will be performed using each provided object in turn.
221
222A second argument can be provided to perform a deep merge: `{deep: true}`.
223
224### replace
225
226```javascript
227var obj1 = Immutable({a: {b: 'test'}, c: 'test'})
228var obj2 = obj1.replace({a: {b: 'test'}}, {deep: true})
229// returns Immutable({a: {b: 'test'}});
230obj1 === obj2
231// returns false
232obj1.b === obj2.b
233// returns true because child .b objects were identical
234```
235
236Returns an Immutable Object containing the properties and values of the
237second object only. With deep merge, all child objects are checked for
238equality and the original immutable object is returned when possible.
239
240A second argument can be provided to perform a deep merge: `{deep: true}`.
241
242### set
243
244```javascript
245Immutable({type: "parrot", subtype: "Norwegian Blue", status: "alive"}).set("status", "dead")
246// returns Immutable({type: "parrot", subtype: "Norwegian Blue", status: "dead"})
247```
248
249Returns an Immutable Object with a single property set to the provided value.
250Basically a more straightforward way of saying
251```javascript
252Immutable({type: "parrot", subtype: "Norwegian Blue", status: "alive"}).merge({status: "dead"})
253```
254(and more convenient with non-literal keys unless you have ES6 ```[computed_property_names]```).
255
256A second argument can be provided to perform a deep compare: `{deep: true}`.
257
258### setIn
259
260Like [set](#set), but accepts a nested path to the property.
261
262```javascript
263Immutable({type: {main: "parrot", sub: "Norwegian Blue"}, status: "alive"}).setIn(["type", "sub"], "Norwegian Ridgeback")
264// returns Immutable({type: {main: "parrot", sub: "Norwegian Ridgeback"}, status: "alive"})
265```
266
267A second argument can be provided to perform a deep compare: `{deep: true}`.
268
269### update
270
271Returns an Immutable Object with a single property updated using the provided updater function.
272
273```javascript
274function inc (x) { return x + 1 }
275Immutable({foo: 1}).update("foo", inc)
276// returns Immutable({foo: 2})
277```
278
279All additional arguments will be passed to the updater function.
280
281```javascript
282function add (x, y) { return x + y }
283Immutable({foo: 1}).update("foo", add, 10)
284// returns Immutable({foo: 11})
285```
286
287### updateIn
288
289Like [update](#update), but accepts a nested path to the property.
290
291```javascript
292function add (x, y) { return x + y }
293Immutable({foo: {bar: 1}}).updateIn(["foo", "bar"], add, 10)
294// returns Immutable({foo: {bar: 11}})
295```
296
297### without
298
299```javascript
300Immutable({the: "forests", will: "echo", with: "laughter"}).without("with")
301// returns Immutable({the: "forests", will: "echo"})
302
303Immutable({the: "forests", will: "echo", with: "laughter"}).without(["will", "with"])
304// returns Immutable({the: "forests"})
305
306Immutable({the: "forests", will: "echo", with: "laughter"}).without("will", "with")
307// returns Immutable({the: "forests"})
308
309Immutable({the: "forests", will: "echo", with: "laughter"}).without((value, key) => key === "the" || value === "echo")
310// returns Immutable({with: "laughter"})
311```
312
313Returns an Immutable Object excluding the given keys or keys/values satisfying
314the given predicate from the existing object.
315
316Multiple keys can be provided, either in an Array or as extra arguments.
317
318### asMutable
319
320```javascript
321var mutableObject = Immutable({when: "the", levee: "breaks"}).asMutable();
322
323mutableObject.have = "no place to go";
324
325mutableObject // {when: "the", levee: "breaks", have: "no place to go"}
326```
327
328Returns a mutable copy of the object. For a deeply mutable copy, in which any instances of `Immutable` contained in nested data structures within the object have been converted back to mutable data structures, call `.asMutable({deep: true})` instead.
329
330### Releases
331
332#### 6.3.0
333
334Adds optional deep compare for `.set`, `.setIn` and `.replace`
335
336#### 6.2.0
337
338Adds static alternatives to methods, e.g. `Immutable.setIn`
339
340#### 6.1.4
341
342Fixes [bug with deep merge() on an array argument](https://github.com/rtfeldman/seamless-immutable/pull/140).
343
344#### 6.1.3
345
346Fixes bug with setting a new object on an existing leaf array.
347
348#### 6.1.2
349
350Fixes bug where on some systems arrays are treated as plain objects.
351
352#### 6.1.1
353
354`without` now handles numeric keys the same way as string keys.
355
356#### 6.1.0
357
358Alias `Immutable.from()` to `Immutable()` for linters.
359
360#### 6.0.1
361
362React components are now considered immutable.
363
364#### 6.0.0
365
366Add cycle detection.
367
368#### 5.2.0
369
370Add `update` and `updateIn`.
371
372#### 5.1.1
373
374`Immutable(Object.create(null))` now works as expected.
375
376#### 5.1.0
377
378Add predicate support to `without()`
379
380#### 5.0.1
381
382Fix missing dev/prod builds for 5.0.0
383
384#### 5.0.0
385
386In development build, freeze Dates and ban mutating methods. (Note: dev and prod builds were mistakenly
387not generated for this, so to get this functionality in those builds, use 5.0.1)
388
389#### 4.1.1
390
391Make `setIn` more null safe.
392
393#### 4.1.0
394
395Adds `set` and `setIn`
396
397#### 4.0.1
398
399Now when you `require("seamless-immutable")`, you get the development build by default.
400
401#### 4.0.0
402
403`main` now points to `src/seamless-immutable.js` so you can more easily build with `envify` yourself.
404
405#### 3.0.0
406
407Add support for optional prototyping.
408
409#### 2.4.2
410
411Calling .asMutable({deep: true}) on an Immutable data structure with a nested Date no longer throws an exception.
412
413#### 2.4.1
414
415Arrays with nonstandard prototypes no longer throw exceptions when passed to `Immutable`.
416
417#### 2.4.0
418
419Custom mergers now check for reference equality and abort early if there is no more work needed, allowing improved performance.
420
421#### 2.3.2
422
423Fixes a bug where indices passed into iterators for flatMap and asObject were strings instead of numbers.
424
425#### 2.3.1
426
427Fixes an IE and Firefox bug related to cloning Dates while preserving their prototypes.
428
429#### 2.3.0
430
431Dates now retain their prototypes, the same way Arrays do.
432
433#### 2.2.0
434
435Adds a minified production build with no freezing or defensive unsupported methods, for a ~2x performance boost.
436
437#### 2.1.0
438
439Adds optional `merger` function to `#merge`.
440
441#### 2.0.2
442
443Bugfix: `#merge` with `{deep: true}` no longer attempts (unsuccessfully) to deeply merge arrays as though they were regular objects.
444
445#### 2.0.1
446
447Minor documentation typo fix.
448
449#### 2.0.0
450
451Breaking API change: `#merge` now takes exactly one or exactly two arguments. The second is optional and allows specifying `deep: true`.
452
453#### 1.3.0
454
455Don't bother returning a new value from `#merge` if no changes would result.
456
457#### 1.2.0
458
459Make error message for invalid `#asObject` less fancy, resulting in a performance improvement.
460
461#### 1.1.0
462
463Adds `#asMutable`
464
465#### 1.0.0
466
467Initial stable release
468
469## Development
470
471Run `npm install -g grunt-cli`, `npm install` and then `grunt` to build and test it.
472
473[1]: https://travis-ci.org/rtfeldman/seamless-immutable.svg?branch=master
474[2]: https://travis-ci.org/rtfeldman/seamless-immutable
475[3]: https://badge.fury.io/js/seamless-immutable.svg
476[4]: https://badge.fury.io/js/seamless-immutable
477[5]: http://img.shields.io/coveralls/rtfeldman/seamless-immutable.svg
478[6]: https://coveralls.io/r/rtfeldman/seamless-immutable