objects.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { isArray, isObject, isUndefinedOrNull } from './types.js';
  6. export function deepClone(obj) {
  7. if (!obj || typeof obj !== 'object') {
  8. return obj;
  9. }
  10. if (obj instanceof RegExp) {
  11. // See https://github.com/microsoft/TypeScript/issues/10990
  12. return obj;
  13. }
  14. const result = Array.isArray(obj) ? [] : {};
  15. Object.keys(obj).forEach((key) => {
  16. if (obj[key] && typeof obj[key] === 'object') {
  17. result[key] = deepClone(obj[key]);
  18. }
  19. else {
  20. result[key] = obj[key];
  21. }
  22. });
  23. return result;
  24. }
  25. export function deepFreeze(obj) {
  26. if (!obj || typeof obj !== 'object') {
  27. return obj;
  28. }
  29. const stack = [obj];
  30. while (stack.length > 0) {
  31. const obj = stack.shift();
  32. Object.freeze(obj);
  33. for (const key in obj) {
  34. if (_hasOwnProperty.call(obj, key)) {
  35. const prop = obj[key];
  36. if (typeof prop === 'object' && !Object.isFrozen(prop)) {
  37. stack.push(prop);
  38. }
  39. }
  40. }
  41. }
  42. return obj;
  43. }
  44. const _hasOwnProperty = Object.prototype.hasOwnProperty;
  45. export function cloneAndChange(obj, changer) {
  46. return _cloneAndChange(obj, changer, new Set());
  47. }
  48. function _cloneAndChange(obj, changer, seen) {
  49. if (isUndefinedOrNull(obj)) {
  50. return obj;
  51. }
  52. const changed = changer(obj);
  53. if (typeof changed !== 'undefined') {
  54. return changed;
  55. }
  56. if (isArray(obj)) {
  57. const r1 = [];
  58. for (const e of obj) {
  59. r1.push(_cloneAndChange(e, changer, seen));
  60. }
  61. return r1;
  62. }
  63. if (isObject(obj)) {
  64. if (seen.has(obj)) {
  65. throw new Error('Cannot clone recursive data-structure');
  66. }
  67. seen.add(obj);
  68. const r2 = {};
  69. for (let i2 in obj) {
  70. if (_hasOwnProperty.call(obj, i2)) {
  71. r2[i2] = _cloneAndChange(obj[i2], changer, seen);
  72. }
  73. }
  74. seen.delete(obj);
  75. return r2;
  76. }
  77. return obj;
  78. }
  79. /**
  80. * Copies all properties of source into destination. The optional parameter "overwrite" allows to control
  81. * if existing properties on the destination should be overwritten or not. Defaults to true (overwrite).
  82. */
  83. export function mixin(destination, source, overwrite = true) {
  84. if (!isObject(destination)) {
  85. return source;
  86. }
  87. if (isObject(source)) {
  88. Object.keys(source).forEach(key => {
  89. if (key in destination) {
  90. if (overwrite) {
  91. if (isObject(destination[key]) && isObject(source[key])) {
  92. mixin(destination[key], source[key], overwrite);
  93. }
  94. else {
  95. destination[key] = source[key];
  96. }
  97. }
  98. }
  99. else {
  100. destination[key] = source[key];
  101. }
  102. });
  103. }
  104. return destination;
  105. }
  106. export function equals(one, other) {
  107. if (one === other) {
  108. return true;
  109. }
  110. if (one === null || one === undefined || other === null || other === undefined) {
  111. return false;
  112. }
  113. if (typeof one !== typeof other) {
  114. return false;
  115. }
  116. if (typeof one !== 'object') {
  117. return false;
  118. }
  119. if ((Array.isArray(one)) !== (Array.isArray(other))) {
  120. return false;
  121. }
  122. let i;
  123. let key;
  124. if (Array.isArray(one)) {
  125. if (one.length !== other.length) {
  126. return false;
  127. }
  128. for (i = 0; i < one.length; i++) {
  129. if (!equals(one[i], other[i])) {
  130. return false;
  131. }
  132. }
  133. }
  134. else {
  135. const oneKeys = [];
  136. for (key in one) {
  137. oneKeys.push(key);
  138. }
  139. oneKeys.sort();
  140. const otherKeys = [];
  141. for (key in other) {
  142. otherKeys.push(key);
  143. }
  144. otherKeys.sort();
  145. if (!equals(oneKeys, otherKeys)) {
  146. return false;
  147. }
  148. for (i = 0; i < oneKeys.length; i++) {
  149. if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
  150. return false;
  151. }
  152. }
  153. }
  154. return true;
  155. }
  156. export function getOrDefault(obj, fn, defaultValue) {
  157. const result = fn(obj);
  158. return typeof result === 'undefined' ? defaultValue : result;
  159. }