UNPKG

4.33 kBJavaScriptView Raw
1'use strict';
2
3var clone = (function(global) {
4
5/**
6 * Clones (copies) an Object using deep copying.
7 *
8 * This function supports circular references by default, but if you are certain
9 * there are no circular references in your object, you can save some CPU time
10 * by calling clone(obj, false).
11 *
12 * Caution: if `circular` is false and `parent` contains circular references,
13 * your program may enter an infinite loop and crash.
14 *
15 * @param `parent` - the object to be cloned
16 * @param `circular` - set to true if the object to be cloned may contain
17 * circular references. (optional - true by default)
18 * @param `depth` - set to a number if the object is only to be cloned to
19 * a particular depth. (optional - defaults to Infinity)
20 * @param `prototype` - sets the prototype to be used when cloning an object.
21 * (optional - defaults to parent prototype).
22*/
23
24function clone(parent, circular, depth, prototype) {
25 var filter;
26 if (typeof circular === 'object') {
27 depth = circular.depth;
28 prototype = circular.prototype;
29 filter = circular.filter;
30 circular = circular.circular
31 }
32 // maintain two arrays for circular references, where corresponding parents
33 // and children have the same index
34 var allParents = [];
35 var allChildren = [];
36
37 var useBuffer = typeof Buffer != 'undefined';
38
39 if (typeof circular == 'undefined')
40 circular = true;
41
42 if (typeof depth == 'undefined')
43 depth = Infinity;
44
45 // recurse this function so we don't reset allParents and allChildren
46 function _clone(parent, depth) {
47 // cloning null always returns null
48 if (parent === null)
49 return null;
50
51 if (depth == 0)
52 return parent;
53
54 var child;
55 var proto;
56 if (typeof parent != 'object') {
57 return parent;
58 }
59
60 if (isArray(parent)) {
61 child = [];
62 } else if (isRegExp(parent)) {
63 child = new RegExp(parent.source, clone.getRegExpFlags(parent));
64 if (parent.lastIndex) child.lastIndex = parent.lastIndex;
65 } else if (isDate(parent)) {
66 child = new Date(parent.getTime());
67 } else if (useBuffer && Buffer.isBuffer(parent)) {
68 child = new Buffer(parent.length);
69 parent.copy(child);
70 return child;
71 } else {
72 if (typeof prototype == 'undefined') {
73 proto = Object.getPrototypeOf(parent);
74 child = Object.create(proto);
75 }
76 else {
77 child = Object.create(prototype);
78 proto = prototype;
79 }
80 }
81
82 if (circular) {
83 var index = allParents.indexOf(parent);
84
85 if (index != -1) {
86 return allChildren[index];
87 }
88 allParents.push(parent);
89 allChildren.push(child);
90 }
91
92 for (var i in parent) {
93 var attrs;
94 if (proto) {
95 attrs = Object.getOwnPropertyDescriptor(proto, i);
96 }
97
98 if (attrs && attrs.set == null) {
99 continue;
100 }
101 child[i] = _clone(parent[i], depth - 1);
102 }
103
104 return child;
105 }
106
107 return _clone(parent, depth);
108}
109
110/**
111 * Simple flat clone using prototype, accepts only objects, usefull for property
112 * override on FLAT configuration object (no nested props).
113 *
114 * USE WITH CAUTION! This may not behave as you wish if you do not know how this
115 * works.
116 */
117clone.clonePrototype = function(parent) {
118 if (parent === null)
119 return null;
120
121 var c = function () {};
122 c.prototype = parent;
123 return new c();
124};
125
126function getRegExpFlags(re) {
127 var flags = '';
128 re.global && (flags += 'g');
129 re.ignoreCase && (flags += 'i');
130 re.multiline && (flags += 'm');
131 return flags;
132}
133
134function objectToString(o) {
135 return Object.prototype.toString.call(o);
136}
137
138function isDate(o) {
139 return typeof o === 'object' && objectToString(o) === '[object Date]';
140}
141
142function isArray(o) {
143 return typeof o === 'object' && objectToString(o) === '[object Array]';
144}
145
146function isRegExp(o) {
147 return typeof o === 'object' && objectToString(o) === '[object RegExp]';
148}
149
150if (global.TESTING) clone.getRegExpFlags = getRegExpFlags;
151if (global.TESTING) clone.objectToString = objectToString;
152if (global.TESTING) clone.isDate = isDate;
153if (global.TESTING) clone.isArray = isArray;
154if (global.TESTING) clone.isRegExp = isRegExp;
155
156return clone;
157
158})( typeof(global) === 'object' ? global :
159 typeof(window) === 'object' ? window : this);
160
161if (module && module.exports)
162 module.exports = clone;