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;