@samitouri / QOS-React-2 / commits / 06f762da77

Add Immutable

As titled. First of 3 diffs to add Immutable, ImmutableMap, and ImmutableObject. No logic changes.

Kunal Mehta committed Mar 4, 2014 at 18:11 UTC 06f762da77196ac604777c38276a4e9b47defb25
1 file changed +132
src/utils/immutable/Immutable.js new
+132
@@ -0,0 +1,132 @@
1 +/**
2 + * Copyright 2014 Facebook, Inc.
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + *
16 + * @providesModule Immutable
17 + */
18 +
19 +var invariant = require('invariant');
20 +var isNode = require('isNode');
21 +var keyOf = require('keyOf');
22 +var mergeInto = require('mergeInto');
23 +
24 +var SECRET_KEY = keyOf({_DONT_EVER_TYPE_THIS_SECRET_KEY: null});
25 +
26 +/**
27 + * `Immutable` provides a guarantee of immutability at developer time when
28 + * strict mode is used. The extra computations required to enforce immutability
29 + * are stripped out in production for performance reasons. `Immutable`
30 + * guarantees to enforce immutability for enumerable, own properties. This
31 + * allows easy wrapping of `Immutable` with the ability to store non-enumerable
32 + * properties on the instance that only your static methods reason about. In
33 + * order to achieve IE8 compatibility (which doesn't have the ability to define
34 + * non-enumerable properties), modules that want to build their own reasoning
35 + * of `Immutable`s and store computations can define their non-enumerable
36 + * properties under the name `toString`, and in IE8 only define a standard
37 + * property called `toString` which will mistakenly be considered not
38 + * enumerable due to its name (but only in IE8). The only limitation is that no
39 + * one can store their own `toString` property.
40 + * https://developer.mozilla.org/en-US/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
41 + */
42 +class Immutable {
43 + /**
44 + * An instance of `Immutable` appears to be a plain JavaScript object, except
45 + * `instanceof Immutable` evaluates to `true`, and it is deeply frozen in
46 + * development mode.
47 + *
48 + * @param {number} secret Ensures this isn't accidentally constructed outside
49 + * of convenience constructors. If created outside of a convenience
50 + * constructor, may not be frozen. Forbidding that use case for now until we
51 + * have a better API.
52 + */
53 + constructor(secret) {
54 + invariant(
55 + secret === Immutable[SECRET_KEY],
56 + 'Only certain classes should create instances of `Immutable`.' +
57 + 'You probably want something like ImmutableObject.create.'
58 + );
59 + }
60 +
61 + /**
62 + * Helper method for classes that make use of `Immutable`.
63 + * @param {Immutable} immutable Object to merge properties into.
64 + * @param {Array<object>} propertyObjects List of objects to merge into
65 + * `destination`.
66 + */
67 + static mergeAllPropertiesInto(destination, propertyObjects) {
68 + var argLength = propertyObjects.length;
69 + for (var i = 0; i < argLength; i++) {
70 + mergeInto(destination, propertyObjects[i]);
71 + }
72 + }
73 +
74 +
75 + /**
76 + * Freezes the supplied object deeply. Other classes may implement their own
77 + * version based on this.
78 + *
79 + * @param {*} object The object to freeze.
80 + */
81 + static deepFreezeRootNode(object) {
82 + if (isNode(object)) {
83 + return; // Don't try to freeze DOM nodes.
84 + }
85 + Object.freeze(object); // First freeze the object.
86 + for (var prop in object) {
87 + if (object.hasOwnProperty(prop)) {
88 + Immutable.recurseDeepFreeze(object[prop]);
89 + }
90 + }
91 + Object.seal(object);
92 + }
93 +
94 + /**
95 + * Differs from `deepFreezeRootNode`, in that we first check if this is a
96 + * necessary recursion. If the object is already an `Immutable`, then the
97 + * recursion is unnecessary as it is already frozen. That check obviously
98 + * wouldn't work for the root node version `deepFreezeRootNode`!
99 + */
100 + static recurseDeepFreeze(object) {
101 + if (isNode(object) || !Immutable.shouldRecurseFreeze(object)) {
102 + return; // Don't try to freeze DOM nodes.
103 + }
104 + Object.freeze(object); // First freeze the object.
105 + for (var prop in object) {
106 + if (object.hasOwnProperty(prop)) {
107 + Immutable.recurseDeepFreeze(object[prop]);
108 + }
109 + }
110 + Object.seal(object);
111 + }
112 +
113 + /**
114 + * Checks if an object should be deep frozen. Instances of `Immutable` are
115 + * assumed to have already been deep frozen, so we can have large `__DEV__`
116 + * time savings by skipping freezing of them.
117 + *
118 + * @param {*} object The object to check.
119 + * @return {boolean} Whether or not deep freeze is needed.
120 + */
121 + static shouldRecurseFreeze(object) {
122 + return (
123 + typeof object === 'object' &&
124 + !(object instanceof Immutable) &&
125 + object !== null
126 + );
127 + }
128 +}
129 +
130 +Immutable._DONT_EVER_TYPE_THIS_SECRET_KEY = Math.random();
131 +
132 +module.exports = Immutable;