提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

52 lines
2.0 KiB

  1. import { isArray, isPlainObject } from 'is-what';
  2. function assignProp(carry, key, newVal, originalObject, includeNonenumerable) {
  3. const propType = {}.propertyIsEnumerable.call(originalObject, key)
  4. ? 'enumerable'
  5. : 'nonenumerable';
  6. if (propType === 'enumerable')
  7. carry[key] = newVal;
  8. if (includeNonenumerable && propType === 'nonenumerable') {
  9. Object.defineProperty(carry, key, {
  10. value: newVal,
  11. enumerable: false,
  12. writable: true,
  13. configurable: true,
  14. });
  15. }
  16. }
  17. /**
  18. * Copy (clone) an object and all its props recursively to get rid of any prop referenced of the
  19. * original object. Arrays are also cloned, however objects inside arrays are still linked.
  20. *
  21. * @param target Target can be anything
  22. * @param [options={}] See type {@link Options} for more details.
  23. *
  24. * - `{ props: ['key1'] }` will only copy the `key1` property. When using this you will need to cast
  25. * the return type manually (in order to keep the TS implementation in here simple I didn't
  26. * built a complex auto resolved type for those few cases people want to use this option)
  27. * - `{ nonenumerable: true }` will copy all non-enumerable properties. Default is `{}`
  28. *
  29. * @returns The target with replaced values
  30. */
  31. export function copy(target, options = {}) {
  32. if (isArray(target)) {
  33. return target.map((item) => copy(item, options));
  34. }
  35. if (!isPlainObject(target)) {
  36. return target;
  37. }
  38. const props = Object.getOwnPropertyNames(target);
  39. const symbols = Object.getOwnPropertySymbols(target);
  40. return [...props, ...symbols].reduce((carry, key) => {
  41. // Skip __proto__ properties to prevent prototype pollution
  42. if (key === '__proto__')
  43. return carry;
  44. if (isArray(options.props) && !options.props.includes(key)) {
  45. return carry;
  46. }
  47. const val = target[key];
  48. const newVal = copy(val, options);
  49. assignProp(carry, key, newVal, target, options.nonenumerable);
  50. return carry;
  51. }, {});
  52. }