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

170 lines
4.3 KiB

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