租房小程序前端代码
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.

171 lines
4.3 KiB

6 months ago
2 months ago
6 months ago
2 months ago
6 months ago
  1. import {
  2. VOID, PRIMITIVE,
  3. ARRAY, OBJECT,
  4. DATE, REGEXP, MAP, SET,
  5. ERROR, BIGINT
  6. } from './types.js';
  7. const EMPTY = '';
  8. const {toString} = {};
  9. const {keys} = Object;
  10. const typeOf = value => {
  11. const type = typeof value;
  12. if (type !== 'object' || !value)
  13. return [PRIMITIVE, type];
  14. const asString = toString.call(value).slice(8, -1);
  15. switch (asString) {
  16. case 'Array':
  17. return [ARRAY, EMPTY];
  18. case 'Object':
  19. return [OBJECT, EMPTY];
  20. case 'Date':
  21. return [DATE, EMPTY];
  22. case 'RegExp':
  23. return [REGEXP, EMPTY];
  24. case 'Map':
  25. return [MAP, EMPTY];
  26. case 'Set':
  27. return [SET, EMPTY];
  28. case 'DataView':
  29. return [ARRAY, asString];
  30. }
  31. if (asString.includes('Array'))
  32. return [ARRAY, asString];
  33. if (asString.includes('Error'))
  34. return [ERROR, asString];
  35. return [OBJECT, asString];
  36. };
  37. const shouldSkip = ([TYPE, type]) => (
  38. TYPE === PRIMITIVE &&
  39. (type === 'function' || type === 'symbol')
  40. );
  41. const serializer = (strict, json, $, _) => {
  42. const as = (out, value) => {
  43. const index = _.push(out) - 1;
  44. $.set(value, index);
  45. return index;
  46. };
  47. const pair = value => {
  48. if ($.has(value))
  49. return $.get(value);
  50. let [TYPE, type] = typeOf(value);
  51. switch (TYPE) {
  52. case PRIMITIVE: {
  53. let entry = value;
  54. switch (type) {
  55. case 'bigint':
  56. TYPE = BIGINT;
  57. entry = value.toString();
  58. break;
  59. case 'function':
  60. case 'symbol':
  61. if (strict)
  62. throw new TypeError('unable to serialize ' + type);
  63. entry = null;
  64. break;
  65. case 'undefined':
  66. return as([VOID], value);
  67. }
  68. return as([TYPE, entry], value);
  69. }
  70. case ARRAY: {
  71. if (type) {
  72. let spread = value;
  73. if (type === 'DataView') {
  74. spread = new Uint8Array(value.buffer);
  75. }
  76. else if (type === 'ArrayBuffer') {
  77. spread = new Uint8Array(value);
  78. }
  79. return as([type, [...spread]], value);
  80. }
  81. const arr = [];
  82. const index = as([TYPE, arr], value);
  83. for (const entry of value)
  84. arr.push(pair(entry));
  85. return index;
  86. }
  87. case OBJECT: {
  88. if (type) {
  89. switch (type) {
  90. case 'BigInt':
  91. return as([type, value.toString()], value);
  92. case 'Boolean':
  93. case 'Number':
  94. case 'String':
  95. return as([type, value.valueOf()], value);
  96. }
  97. }
  98. if (json && ('toJSON' in value))
  99. return pair(value.toJSON());
  100. const entries = [];
  101. const index = as([TYPE, entries], value);
  102. for (const key of keys(value)) {
  103. if (strict || !shouldSkip(typeOf(value[key])))
  104. entries.push([pair(key), pair(value[key])]);
  105. }
  106. return index;
  107. }
  108. case DATE:
  109. return as([TYPE, value.toISOString()], value);
  110. case REGEXP: {
  111. const {source, flags} = value;
  112. return as([TYPE, {source, flags}], value);
  113. }
  114. case MAP: {
  115. const entries = [];
  116. const index = as([TYPE, entries], value);
  117. for (const [key, entry] of value) {
  118. if (strict || !(shouldSkip(typeOf(key)) || shouldSkip(typeOf(entry))))
  119. entries.push([pair(key), pair(entry)]);
  120. }
  121. return index;
  122. }
  123. case SET: {
  124. const entries = [];
  125. const index = as([TYPE, entries], value);
  126. for (const entry of value) {
  127. if (strict || !shouldSkip(typeOf(entry)))
  128. entries.push(pair(entry));
  129. }
  130. return index;
  131. }
  132. }
  133. const {message} = value;
  134. return as([TYPE, {name: type, message}], value);
  135. };
  136. return pair;
  137. };
  138. /**
  139. * @typedef {Array<string,any>} Record a type representation
  140. */
  141. /**
  142. * Returns an array of serialized Records.
  143. * @param {any} value a serializable value.
  144. * @param {{json?: boolean, lossy?: boolean}?} options an object with a `lossy` or `json` property that,
  145. * if `true`, will not throw errors on incompatible types, and behave more
  146. * like JSON stringify would behave. Symbol and Function will be discarded.
  147. * @returns {Record[]}
  148. */
  149. export const serialize = (value, {json, lossy} = {}) => {
  150. const _ = [];
  151. return serializer(!(json || lossy), !!json, new Map, _)(value), _;
  152. };