es.json.stringify.js 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. 'use strict';
  2. var $ = require('../internals/export');
  3. var getBuiltIn = require('../internals/get-built-in');
  4. var apply = require('../internals/function-apply');
  5. var call = require('../internals/function-call');
  6. var uncurryThis = require('../internals/function-uncurry-this');
  7. var fails = require('../internals/fails');
  8. var isCallable = require('../internals/is-callable');
  9. var isSymbol = require('../internals/is-symbol');
  10. var arraySlice = require('../internals/array-slice');
  11. var getReplacerFunction = require('../internals/get-json-replacer-function');
  12. var NATIVE_SYMBOL = require('../internals/symbol-constructor-detection');
  13. var $String = String;
  14. var $stringify = getBuiltIn('JSON', 'stringify');
  15. var exec = uncurryThis(/./.exec);
  16. var charAt = uncurryThis(''.charAt);
  17. var charCodeAt = uncurryThis(''.charCodeAt);
  18. var replace = uncurryThis(''.replace);
  19. var numberToString = uncurryThis(1.0.toString);
  20. var tester = /[\uD800-\uDFFF]/g;
  21. var low = /^[\uD800-\uDBFF]$/;
  22. var hi = /^[\uDC00-\uDFFF]$/;
  23. var WRONG_SYMBOLS_CONVERSION = !NATIVE_SYMBOL || fails(function () {
  24. var symbol = getBuiltIn('Symbol')('stringify detection');
  25. // MS Edge converts symbol values to JSON as {}
  26. return $stringify([symbol]) !== '[null]'
  27. // WebKit converts symbol values to JSON as null
  28. || $stringify({ a: symbol }) !== '{}'
  29. // V8 throws on boxed symbols
  30. || $stringify(Object(symbol)) !== '{}';
  31. });
  32. // https://github.com/tc39/proposal-well-formed-stringify
  33. var ILL_FORMED_UNICODE = fails(function () {
  34. return $stringify('\uDF06\uD834') !== '"\\udf06\\ud834"'
  35. || $stringify('\uDEAD') !== '"\\udead"';
  36. });
  37. var stringifyWithSymbolsFix = function (it, replacer) {
  38. var args = arraySlice(arguments);
  39. var $replacer = getReplacerFunction(replacer);
  40. if (!isCallable($replacer) && (it === undefined || isSymbol(it))) return; // IE8 returns string on undefined
  41. args[1] = function (key, value) {
  42. // some old implementations (like WebKit) could pass numbers as keys
  43. if (isCallable($replacer)) value = call($replacer, this, $String(key), value);
  44. if (!isSymbol(value)) return value;
  45. };
  46. return apply($stringify, null, args);
  47. };
  48. var fixIllFormed = function (match, offset, string) {
  49. var prev = charAt(string, offset - 1);
  50. var next = charAt(string, offset + 1);
  51. if ((exec(low, match) && !exec(hi, next)) || (exec(hi, match) && !exec(low, prev))) {
  52. return '\\u' + numberToString(charCodeAt(match, 0), 16);
  53. } return match;
  54. };
  55. if ($stringify) {
  56. // `JSON.stringify` method
  57. // https://tc39.es/ecma262/#sec-json.stringify
  58. $({ target: 'JSON', stat: true, arity: 3, forced: WRONG_SYMBOLS_CONVERSION || ILL_FORMED_UNICODE }, {
  59. // eslint-disable-next-line no-unused-vars -- required for `.length`
  60. stringify: function stringify(it, replacer, space) {
  61. var args = arraySlice(arguments);
  62. var result = apply(WRONG_SYMBOLS_CONVERSION ? stringifyWithSymbolsFix : $stringify, null, args);
  63. return ILL_FORMED_UNICODE && typeof result == 'string' ? replace(result, tester, fixIllFormed) : result;
  64. }
  65. });
  66. }