objects.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 { isTypedArray, 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. return obj;
  12. }
  13. const result = Array.isArray(obj) ? [] : {};
  14. Object.entries(obj).forEach(([key, value]) => {
  15. result[key] = value && typeof value === 'object' ? deepClone(value) : value;
  16. });
  17. return result;
  18. }
  19. export function deepFreeze(obj) {
  20. if (!obj || typeof obj !== 'object') {
  21. return obj;
  22. }
  23. const stack = [obj];
  24. while (stack.length > 0) {
  25. const obj = stack.shift();
  26. Object.freeze(obj);
  27. for (const key in obj) {
  28. if (_hasOwnProperty.call(obj, key)) {
  29. const prop = obj[key];
  30. if (typeof prop === 'object' && !Object.isFrozen(prop) && !isTypedArray(prop)) {
  31. stack.push(prop);
  32. }
  33. }
  34. }
  35. }
  36. return obj;
  37. }
  38. const _hasOwnProperty = Object.prototype.hasOwnProperty;
  39. export function cloneAndChange(obj, changer) {
  40. return _cloneAndChange(obj, changer, new Set());
  41. }
  42. function _cloneAndChange(obj, changer, seen) {
  43. if (isUndefinedOrNull(obj)) {
  44. return obj;
  45. }
  46. const changed = changer(obj);
  47. if (typeof changed !== 'undefined') {
  48. return changed;
  49. }
  50. if (Array.isArray(obj)) {
  51. const r1 = [];
  52. for (const e of obj) {
  53. r1.push(_cloneAndChange(e, changer, seen));
  54. }
  55. return r1;
  56. }
  57. if (isObject(obj)) {
  58. if (seen.has(obj)) {
  59. throw new Error('Cannot clone recursive data-structure');
  60. }
  61. seen.add(obj);
  62. const r2 = {};
  63. for (const i2 in obj) {
  64. if (_hasOwnProperty.call(obj, i2)) {
  65. r2[i2] = _cloneAndChange(obj[i2], changer, seen);
  66. }
  67. }
  68. seen.delete(obj);
  69. return r2;
  70. }
  71. return obj;
  72. }
  73. /**
  74. * Copies all properties of source into destination. The optional parameter "overwrite" allows to control
  75. * if existing properties on the destination should be overwritten or not. Defaults to true (overwrite).
  76. */
  77. export function mixin(destination, source, overwrite = true) {
  78. if (!isObject(destination)) {
  79. return source;
  80. }
  81. if (isObject(source)) {
  82. Object.keys(source).forEach(key => {
  83. if (key in destination) {
  84. if (overwrite) {
  85. if (isObject(destination[key]) && isObject(source[key])) {
  86. mixin(destination[key], source[key], overwrite);
  87. }
  88. else {
  89. destination[key] = source[key];
  90. }
  91. }
  92. }
  93. else {
  94. destination[key] = source[key];
  95. }
  96. });
  97. }
  98. return destination;
  99. }
  100. export function equals(one, other) {
  101. if (one === other) {
  102. return true;
  103. }
  104. if (one === null || one === undefined || other === null || other === undefined) {
  105. return false;
  106. }
  107. if (typeof one !== typeof other) {
  108. return false;
  109. }
  110. if (typeof one !== 'object') {
  111. return false;
  112. }
  113. if ((Array.isArray(one)) !== (Array.isArray(other))) {
  114. return false;
  115. }
  116. let i;
  117. let key;
  118. if (Array.isArray(one)) {
  119. if (one.length !== other.length) {
  120. return false;
  121. }
  122. for (i = 0; i < one.length; i++) {
  123. if (!equals(one[i], other[i])) {
  124. return false;
  125. }
  126. }
  127. }
  128. else {
  129. const oneKeys = [];
  130. for (key in one) {
  131. oneKeys.push(key);
  132. }
  133. oneKeys.sort();
  134. const otherKeys = [];
  135. for (key in other) {
  136. otherKeys.push(key);
  137. }
  138. otherKeys.sort();
  139. if (!equals(oneKeys, otherKeys)) {
  140. return false;
  141. }
  142. for (i = 0; i < oneKeys.length; i++) {
  143. if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
  144. return false;
  145. }
  146. }
  147. }
  148. return true;
  149. }
  150. export function getAllPropertyNames(obj) {
  151. let res = [];
  152. let proto = Object.getPrototypeOf(obj);
  153. while (Object.prototype !== proto) {
  154. res = res.concat(Object.getOwnPropertyNames(proto));
  155. proto = Object.getPrototypeOf(proto);
  156. }
  157. return res;
  158. }
  159. export function getAllMethodNames(obj) {
  160. const methods = [];
  161. for (const prop of getAllPropertyNames(obj)) {
  162. if (typeof obj[prop] === 'function') {
  163. methods.push(prop);
  164. }
  165. }
  166. return methods;
  167. }
  168. export function createProxyObject(methodNames, invoke) {
  169. const createProxyMethod = (method) => {
  170. return function () {
  171. const args = Array.prototype.slice.call(arguments, 0);
  172. return invoke(method, args);
  173. };
  174. };
  175. const result = {};
  176. for (const methodName of methodNames) {
  177. result[methodName] = createProxyMethod(methodName);
  178. }
  179. return result;
  180. }