clone.js 8.0 KB

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