clone.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /* eslint-disable */
  2. var clone = (function () {
  3. 'use strict';
  4. function _instanceof(obj, type) {
  5. return type != null && obj instanceof type;
  6. }
  7. var nativeMap;
  8. try {
  9. nativeMap = Map;
  10. } catch (_) {
  11. // maybe a reference error because no `Map`. Give it a dummy value that no
  12. // value will ever be an instanceof.
  13. nativeMap = function () {
  14. };
  15. }
  16. var nativeSet;
  17. try {
  18. nativeSet = Set;
  19. } catch (_) {
  20. nativeSet = function () {
  21. };
  22. }
  23. var nativePromise;
  24. try {
  25. nativePromise = Promise;
  26. } catch (_) {
  27. nativePromise = function () {
  28. };
  29. }
  30. /**
  31. * Clones (copies) an Object using deep copying.
  32. *
  33. * This function supports circular references by default, but if you are certain
  34. * there are no circular references in your object, you can save some CPU time
  35. * by calling clone(obj, false).
  36. *
  37. * Caution: if `circular` is false and `parent` contains circular references,
  38. * your program may enter an infinite loop and crash.
  39. *
  40. * @param `parent` - the object to be cloned
  41. * @param `circular` - set to true if the object to be cloned may contain
  42. * circular references. (optional - true by default)
  43. * @param `depth` - set to a number if the object is only to be cloned to
  44. * a particular depth. (optional - defaults to Infinity)
  45. * @param `prototype` - sets the prototype to be used when cloning an object.
  46. * (optional - defaults to parent prototype).
  47. * @param `includeNonEnumerable` - set to true if the non-enumerable properties
  48. * should be cloned as well. Non-enumerable properties on the prototype
  49. * chain will be ignored. (optional - false by default)
  50. */
  51. function clone(parent, circular, depth, prototype, includeNonEnumerable) {
  52. if (typeof circular === 'object') {
  53. depth = circular.depth;
  54. prototype = circular.prototype;
  55. includeNonEnumerable = circular.includeNonEnumerable;
  56. circular = circular.circular;
  57. }
  58. // maintain two arrays for circular references, where corresponding parents
  59. // and children have the same index
  60. var allParents = [];
  61. var allChildren = [];
  62. var useBuffer = typeof Buffer != 'undefined';
  63. if (typeof circular == 'undefined')
  64. circular = true;
  65. if (typeof depth == 'undefined')
  66. depth = Infinity;
  67. // recurse this function so we don't reset allParents and allChildren
  68. function _clone(parent, depth) {
  69. // cloning null always returns null
  70. if (parent === null)
  71. return null;
  72. if (depth === 0)
  73. return parent;
  74. var child;
  75. var proto;
  76. if (typeof parent != 'object') {
  77. return parent;
  78. }
  79. if (_instanceof(parent, nativeMap)) {
  80. child = new nativeMap();
  81. } else if (_instanceof(parent, nativeSet)) {
  82. child = new nativeSet();
  83. } else if (_instanceof(parent, nativePromise)) {
  84. child = new nativePromise(function (resolve, reject) {
  85. parent.then(function (value) {
  86. resolve(_clone(value, depth - 1));
  87. }, function (err) {
  88. reject(_clone(err, depth - 1));
  89. });
  90. });
  91. } else if (clone.__isArray(parent)) {
  92. child = [];
  93. } else if (clone.__isRegExp(parent)) {
  94. child = new RegExp(parent.source, __getRegExpFlags(parent));
  95. if (parent.lastIndex) child.lastIndex = parent.lastIndex;
  96. } else if (clone.__isDate(parent)) {
  97. child = new Date(parent.getTime());
  98. } else if (useBuffer && Buffer.isBuffer(parent)) {
  99. if (Buffer.from) {
  100. // Node.js >= 5.10.0
  101. child = Buffer.from(parent);
  102. } else {
  103. // Older Node.js versions
  104. child = new Buffer(parent.length);
  105. parent.copy(child);
  106. }
  107. return child;
  108. } else if (_instanceof(parent, Error)) {
  109. child = Object.create(parent);
  110. } else {
  111. if (typeof prototype == 'undefined') {
  112. proto = Object.getPrototypeOf(parent);
  113. child = Object.create(proto);
  114. } else {
  115. child = Object.create(prototype);
  116. proto = prototype;
  117. }
  118. }
  119. if (circular) {
  120. var index = allParents.indexOf(parent);
  121. if (index != -1) {
  122. return allChildren[index];
  123. }
  124. allParents.push(parent);
  125. allChildren.push(child);
  126. }
  127. if (_instanceof(parent, nativeMap)) {
  128. parent.forEach(function (value, key) {
  129. var keyChild = _clone(key, depth - 1);
  130. var valueChild = _clone(value, depth - 1);
  131. child.set(keyChild, valueChild);
  132. });
  133. }
  134. if (_instanceof(parent, nativeSet)) {
  135. parent.forEach(function (value) {
  136. var entryChild = _clone(value, depth - 1);
  137. child.add(entryChild);
  138. });
  139. }
  140. for (var i in parent) {
  141. var attrs = Object.getOwnPropertyDescriptor(parent, i);
  142. if (attrs) {
  143. child[i] = _clone(parent[i], depth - 1);
  144. }
  145. try {
  146. var objProperty = Object.getOwnPropertyDescriptor(parent, i);
  147. if (objProperty.set === 'undefined') {
  148. // no setter defined. Skip cloning this property
  149. continue;
  150. }
  151. child[i] = _clone(parent[i], depth - 1);
  152. } catch (e) {
  153. if (e instanceof TypeError) {
  154. // when in strict mode, TypeError will be thrown if child[i] property only has a getter
  155. // we can't do anything about this, other than inform the user that this property cannot be set.
  156. continue
  157. } else if (e instanceof ReferenceError) {
  158. //this may happen in non strict mode
  159. continue
  160. }
  161. }
  162. }
  163. if (Object.getOwnPropertySymbols) {
  164. var symbols = Object.getOwnPropertySymbols(parent);
  165. for (var i = 0; i < symbols.length; i++) {
  166. // Don't need to worry about cloning a symbol because it is a primitive,
  167. // like a number or string.
  168. var symbol = symbols[i];
  169. var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
  170. if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
  171. continue;
  172. }
  173. child[symbol] = _clone(parent[symbol], depth - 1);
  174. Object.defineProperty(child, symbol, descriptor);
  175. }
  176. }
  177. if (includeNonEnumerable) {
  178. var allPropertyNames = Object.getOwnPropertyNames(parent);
  179. for (var i = 0; i < allPropertyNames.length; i++) {
  180. var propertyName = allPropertyNames[i];
  181. var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
  182. if (descriptor && descriptor.enumerable) {
  183. continue;
  184. }
  185. child[propertyName] = _clone(parent[propertyName], depth - 1);
  186. Object.defineProperty(child, propertyName, descriptor);
  187. }
  188. }
  189. return child;
  190. }
  191. return _clone(parent, depth);
  192. }
  193. /**
  194. * Simple flat clone using prototype, accepts only objects, usefull for property
  195. * override on FLAT configuration object (no nested props).
  196. *
  197. * USE WITH CAUTION! This may not behave as you wish if you do not know how this
  198. * works.
  199. */
  200. clone.clonePrototype = function clonePrototype(parent) {
  201. if (parent === null)
  202. return null;
  203. var c = function () {
  204. };
  205. c.prototype = parent;
  206. return new c();
  207. };
  208. // private utility functions
  209. function __objToStr(o) {
  210. return Object.prototype.toString.call(o);
  211. }
  212. clone.__objToStr = __objToStr;
  213. function __isDate(o) {
  214. return typeof o === 'object' && __objToStr(o) === '[object Date]';
  215. }
  216. clone.__isDate = __isDate;
  217. function __isArray(o) {
  218. return typeof o === 'object' && __objToStr(o) === '[object Array]';
  219. }
  220. clone.__isArray = __isArray;
  221. function __isRegExp(o) {
  222. return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
  223. }
  224. clone.__isRegExp = __isRegExp;
  225. function __getRegExpFlags(re) {
  226. var flags = '';
  227. if (re.global) flags += 'g';
  228. if (re.ignoreCase) flags += 'i';
  229. if (re.multiline) flags += 'm';
  230. return flags;
  231. }
  232. clone.__getRegExpFlags = __getRegExpFlags;
  233. return clone;
  234. })();
  235. export default clone