es.string.replace.js 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. 'use strict';
  2. var fixRegExpWellKnownSymbolLogic = require('../internals/fix-regexp-well-known-symbol-logic');
  3. var anObject = require('../internals/an-object');
  4. var toLength = require('../internals/to-length');
  5. var toInteger = require('../internals/to-integer');
  6. var requireObjectCoercible = require('../internals/require-object-coercible');
  7. var advanceStringIndex = require('../internals/advance-string-index');
  8. var getSubstitution = require('../internals/get-substitution');
  9. var regExpExec = require('../internals/regexp-exec-abstract');
  10. var max = Math.max;
  11. var min = Math.min;
  12. var maybeToString = function (it) {
  13. return it === undefined ? it : String(it);
  14. };
  15. // @@replace logic
  16. fixRegExpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, maybeCallNative, reason) {
  17. var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE;
  18. var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0;
  19. var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
  20. return [
  21. // `String.prototype.replace` method
  22. // https://tc39.es/ecma262/#sec-string.prototype.replace
  23. function replace(searchValue, replaceValue) {
  24. var O = requireObjectCoercible(this);
  25. var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
  26. return replacer !== undefined
  27. ? replacer.call(searchValue, O, replaceValue)
  28. : nativeReplace.call(String(O), searchValue, replaceValue);
  29. },
  30. // `RegExp.prototype[@@replace]` method
  31. // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
  32. function (regexp, replaceValue) {
  33. if (
  34. (!REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && REPLACE_KEEPS_$0) ||
  35. (typeof replaceValue === 'string' && replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1)
  36. ) {
  37. var res = maybeCallNative(nativeReplace, regexp, this, replaceValue);
  38. if (res.done) return res.value;
  39. }
  40. var rx = anObject(regexp);
  41. var S = String(this);
  42. var functionalReplace = typeof replaceValue === 'function';
  43. if (!functionalReplace) replaceValue = String(replaceValue);
  44. var global = rx.global;
  45. if (global) {
  46. var fullUnicode = rx.unicode;
  47. rx.lastIndex = 0;
  48. }
  49. var results = [];
  50. while (true) {
  51. var result = regExpExec(rx, S);
  52. if (result === null) break;
  53. results.push(result);
  54. if (!global) break;
  55. var matchStr = String(result[0]);
  56. if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
  57. }
  58. var accumulatedResult = '';
  59. var nextSourcePosition = 0;
  60. for (var i = 0; i < results.length; i++) {
  61. result = results[i];
  62. var matched = String(result[0]);
  63. var position = max(min(toInteger(result.index), S.length), 0);
  64. var captures = [];
  65. // NOTE: This is equivalent to
  66. // captures = result.slice(1).map(maybeToString)
  67. // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
  68. // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
  69. // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
  70. for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
  71. var namedCaptures = result.groups;
  72. if (functionalReplace) {
  73. var replacerArgs = [matched].concat(captures, position, S);
  74. if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
  75. var replacement = String(replaceValue.apply(undefined, replacerArgs));
  76. } else {
  77. replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
  78. }
  79. if (position >= nextSourcePosition) {
  80. accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
  81. nextSourcePosition = position + matched.length;
  82. }
  83. }
  84. return accumulatedResult + S.slice(nextSourcePosition);
  85. }
  86. ];
  87. });