object.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.canDefineProperty = canDefineProperty;
  7. exports.clone = clone;
  8. exports.deepExtend = deepExtend;
  9. exports.deepFlatten = deepFlatten;
  10. exports.deepStrictEqual = deepStrictEqual;
  11. exports.extend = extend;
  12. exports.get = get;
  13. exports.hasOwnProperty = hasOwnProperty;
  14. exports.isLegacyFactory = isLegacyFactory;
  15. exports.lazy = lazy;
  16. exports.mapObject = mapObject;
  17. exports.pick = pick;
  18. exports.pickShallow = pickShallow;
  19. exports.set = set;
  20. exports.traverse = traverse;
  21. exports.values = values;
  22. var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
  23. var _is = require("./is.js");
  24. /**
  25. * Clone an object
  26. *
  27. * clone(x)
  28. *
  29. * Can clone any primitive type, array, and object.
  30. * If x has a function clone, this function will be invoked to clone the object.
  31. *
  32. * @param {*} x
  33. * @return {*} clone
  34. */
  35. function clone(x) {
  36. var type = (0, _typeof2["default"])(x);
  37. // immutable primitive types
  38. if (type === 'number' || type === 'string' || type === 'boolean' || x === null || x === undefined) {
  39. return x;
  40. }
  41. // use clone function of the object when available
  42. if (typeof x.clone === 'function') {
  43. return x.clone();
  44. }
  45. // array
  46. if (Array.isArray(x)) {
  47. return x.map(function (value) {
  48. return clone(value);
  49. });
  50. }
  51. if (x instanceof Date) return new Date(x.valueOf());
  52. if ((0, _is.isBigNumber)(x)) return x; // bignumbers are immutable
  53. if (x instanceof RegExp) throw new TypeError('Cannot clone ' + x); // TODO: clone a RegExp
  54. // object
  55. return mapObject(x, clone);
  56. }
  57. /**
  58. * Apply map to all properties of an object
  59. * @param {Object} object
  60. * @param {function} callback
  61. * @return {Object} Returns a copy of the object with mapped properties
  62. */
  63. function mapObject(object, callback) {
  64. var clone = {};
  65. for (var key in object) {
  66. if (hasOwnProperty(object, key)) {
  67. clone[key] = callback(object[key]);
  68. }
  69. }
  70. return clone;
  71. }
  72. /**
  73. * Extend object a with the properties of object b
  74. * @param {Object} a
  75. * @param {Object} b
  76. * @return {Object} a
  77. */
  78. function extend(a, b) {
  79. for (var prop in b) {
  80. if (hasOwnProperty(b, prop)) {
  81. a[prop] = b[prop];
  82. }
  83. }
  84. return a;
  85. }
  86. /**
  87. * Deep extend an object a with the properties of object b
  88. * @param {Object} a
  89. * @param {Object} b
  90. * @returns {Object}
  91. */
  92. function deepExtend(a, b) {
  93. // TODO: add support for Arrays to deepExtend
  94. if (Array.isArray(b)) {
  95. throw new TypeError('Arrays are not supported by deepExtend');
  96. }
  97. for (var prop in b) {
  98. // We check against prop not being in Object.prototype or Function.prototype
  99. // to prevent polluting for example Object.__proto__.
  100. if (hasOwnProperty(b, prop) && !(prop in Object.prototype) && !(prop in Function.prototype)) {
  101. if (b[prop] && b[prop].constructor === Object) {
  102. if (a[prop] === undefined) {
  103. a[prop] = {};
  104. }
  105. if (a[prop] && a[prop].constructor === Object) {
  106. deepExtend(a[prop], b[prop]);
  107. } else {
  108. a[prop] = b[prop];
  109. }
  110. } else if (Array.isArray(b[prop])) {
  111. throw new TypeError('Arrays are not supported by deepExtend');
  112. } else {
  113. a[prop] = b[prop];
  114. }
  115. }
  116. }
  117. return a;
  118. }
  119. /**
  120. * Deep test equality of all fields in two pairs of arrays or objects.
  121. * Compares values and functions strictly (ie. 2 is not the same as '2').
  122. * @param {Array | Object} a
  123. * @param {Array | Object} b
  124. * @returns {boolean}
  125. */
  126. function deepStrictEqual(a, b) {
  127. var prop, i, len;
  128. if (Array.isArray(a)) {
  129. if (!Array.isArray(b)) {
  130. return false;
  131. }
  132. if (a.length !== b.length) {
  133. return false;
  134. }
  135. for (i = 0, len = a.length; i < len; i++) {
  136. if (!deepStrictEqual(a[i], b[i])) {
  137. return false;
  138. }
  139. }
  140. return true;
  141. } else if (typeof a === 'function') {
  142. return a === b;
  143. } else if (a instanceof Object) {
  144. if (Array.isArray(b) || !(b instanceof Object)) {
  145. return false;
  146. }
  147. for (prop in a) {
  148. // noinspection JSUnfilteredForInLoop
  149. if (!(prop in b) || !deepStrictEqual(a[prop], b[prop])) {
  150. return false;
  151. }
  152. }
  153. for (prop in b) {
  154. // noinspection JSUnfilteredForInLoop
  155. if (!(prop in a)) {
  156. return false;
  157. }
  158. }
  159. return true;
  160. } else {
  161. return a === b;
  162. }
  163. }
  164. /**
  165. * Recursively flatten a nested object.
  166. * @param {Object} nestedObject
  167. * @return {Object} Returns the flattened object
  168. */
  169. function deepFlatten(nestedObject) {
  170. var flattenedObject = {};
  171. _deepFlatten(nestedObject, flattenedObject);
  172. return flattenedObject;
  173. }
  174. // helper function used by deepFlatten
  175. function _deepFlatten(nestedObject, flattenedObject) {
  176. for (var prop in nestedObject) {
  177. if (hasOwnProperty(nestedObject, prop)) {
  178. var value = nestedObject[prop];
  179. if ((0, _typeof2["default"])(value) === 'object' && value !== null) {
  180. _deepFlatten(value, flattenedObject);
  181. } else {
  182. flattenedObject[prop] = value;
  183. }
  184. }
  185. }
  186. }
  187. /**
  188. * Test whether the current JavaScript engine supports Object.defineProperty
  189. * @returns {boolean} returns true if supported
  190. */
  191. function canDefineProperty() {
  192. // test needed for broken IE8 implementation
  193. try {
  194. if (Object.defineProperty) {
  195. Object.defineProperty({}, 'x', {
  196. get: function get() {
  197. return null;
  198. }
  199. });
  200. return true;
  201. }
  202. } catch (e) {}
  203. return false;
  204. }
  205. /**
  206. * Attach a lazy loading property to a constant.
  207. * The given function `fn` is called once when the property is first requested.
  208. *
  209. * @param {Object} object Object where to add the property
  210. * @param {string} prop Property name
  211. * @param {Function} valueResolver Function returning the property value. Called
  212. * without arguments.
  213. */
  214. function lazy(object, prop, valueResolver) {
  215. var _uninitialized = true;
  216. var _value;
  217. Object.defineProperty(object, prop, {
  218. get: function get() {
  219. if (_uninitialized) {
  220. _value = valueResolver();
  221. _uninitialized = false;
  222. }
  223. return _value;
  224. },
  225. set: function set(value) {
  226. _value = value;
  227. _uninitialized = false;
  228. },
  229. configurable: true,
  230. enumerable: true
  231. });
  232. }
  233. /**
  234. * Traverse a path into an object.
  235. * When a namespace is missing, it will be created
  236. * @param {Object} object
  237. * @param {string | string[]} path A dot separated string like 'name.space'
  238. * @return {Object} Returns the object at the end of the path
  239. */
  240. function traverse(object, path) {
  241. if (path && typeof path === 'string') {
  242. return traverse(object, path.split('.'));
  243. }
  244. var obj = object;
  245. if (path) {
  246. for (var i = 0; i < path.length; i++) {
  247. var key = path[i];
  248. if (!(key in obj)) {
  249. obj[key] = {};
  250. }
  251. obj = obj[key];
  252. }
  253. }
  254. return obj;
  255. }
  256. /**
  257. * A safe hasOwnProperty
  258. * @param {Object} object
  259. * @param {string} property
  260. */
  261. function hasOwnProperty(object, property) {
  262. return object && Object.hasOwnProperty.call(object, property);
  263. }
  264. /**
  265. * Test whether an object is a factory. a factory has fields:
  266. *
  267. * - factory: function (type: Object, config: Object, load: function, typed: function [, math: Object]) (required)
  268. * - name: string (optional)
  269. * - path: string A dot separated path (optional)
  270. * - math: boolean If true (false by default), the math namespace is passed
  271. * as fifth argument of the factory function
  272. *
  273. * @param {*} object
  274. * @returns {boolean}
  275. */
  276. function isLegacyFactory(object) {
  277. return object && typeof object.factory === 'function';
  278. }
  279. /**
  280. * Get a nested property from an object
  281. * @param {Object} object
  282. * @param {string | string[]} path
  283. * @returns {Object}
  284. */
  285. function get(object, path) {
  286. if (typeof path === 'string') {
  287. if (isPath(path)) {
  288. return get(object, path.split('.'));
  289. } else {
  290. return object[path];
  291. }
  292. }
  293. var child = object;
  294. for (var i = 0; i < path.length; i++) {
  295. var key = path[i];
  296. child = child ? child[key] : undefined;
  297. }
  298. return child;
  299. }
  300. /**
  301. * Set a nested property in an object
  302. * Mutates the object itself
  303. * If the path doesn't exist, it will be created
  304. * @param {Object} object
  305. * @param {string | string[]} path
  306. * @param {*} value
  307. * @returns {Object}
  308. */
  309. function set(object, path, value) {
  310. if (typeof path === 'string') {
  311. if (isPath(path)) {
  312. return set(object, path.split('.'), value);
  313. } else {
  314. object[path] = value;
  315. return object;
  316. }
  317. }
  318. var child = object;
  319. for (var i = 0; i < path.length - 1; i++) {
  320. var key = path[i];
  321. if (child[key] === undefined) {
  322. child[key] = {};
  323. }
  324. child = child[key];
  325. }
  326. if (path.length > 0) {
  327. var lastKey = path[path.length - 1];
  328. child[lastKey] = value;
  329. }
  330. return object;
  331. }
  332. /**
  333. * Create an object composed of the picked object properties
  334. * @param {Object} object
  335. * @param {string[]} properties
  336. * @param {function} [transform] Optional value to transform a value when picking it
  337. * @return {Object}
  338. */
  339. function pick(object, properties, transform) {
  340. var copy = {};
  341. for (var i = 0; i < properties.length; i++) {
  342. var key = properties[i];
  343. var value = get(object, key);
  344. if (value !== undefined) {
  345. set(copy, key, transform ? transform(value, key) : value);
  346. }
  347. }
  348. return copy;
  349. }
  350. /**
  351. * Shallow version of pick, creating an object composed of the picked object properties
  352. * but not for nested properties
  353. * @param {Object} object
  354. * @param {string[]} properties
  355. * @return {Object}
  356. */
  357. function pickShallow(object, properties) {
  358. var copy = {};
  359. for (var i = 0; i < properties.length; i++) {
  360. var key = properties[i];
  361. var value = object[key];
  362. if (value !== undefined) {
  363. copy[key] = value;
  364. }
  365. }
  366. return copy;
  367. }
  368. function values(object) {
  369. return Object.keys(object).map(function (key) {
  370. return object[key];
  371. });
  372. }
  373. // helper function to test whether a string contains a path like 'user.name'
  374. function isPath(str) {
  375. return str.indexOf('.') !== -1;
  376. }