es.string.replace.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. 'use strict';
  2. var fixRegExpWellKnownSymbolLogic = require('../internals/fix-regexp-well-known-symbol-logic');
  3. var anObject = require('../internals/an-object');
  4. var toObject = require('../internals/to-object');
  5. var toLength = require('../internals/to-length');
  6. var toInteger = require('../internals/to-integer');
  7. var requireObjectCoercible = require('../internals/require-object-coercible');
  8. var advanceStringIndex = require('../internals/advance-string-index');
  9. var regExpExec = require('../internals/regexp-exec-abstract');
  10. var max = Math.max;
  11. var min = Math.min;
  12. var floor = Math.floor;
  13. var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d\d?|<[^>]*>)/g;
  14. var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d\d?)/g;
  15. var maybeToString = function (it) {
  16. return it === undefined ? it : String(it);
  17. };
  18. // @@replace logic
  19. fixRegExpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, maybeCallNative, reason) {
  20. var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE;
  21. var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0;
  22. var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
  23. return [
  24. // `String.prototype.replace` method
  25. // https://tc39.github.io/ecma262/#sec-string.prototype.replace
  26. function replace(searchValue, replaceValue) {
  27. var O = requireObjectCoercible(this);
  28. var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
  29. return replacer !== undefined
  30. ? replacer.call(searchValue, O, replaceValue)
  31. : nativeReplace.call(String(O), searchValue, replaceValue);
  32. },
  33. // `RegExp.prototype[@@replace]` method
  34. // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
  35. function (regexp, replaceValue) {
  36. if (
  37. (!REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && REPLACE_KEEPS_$0) ||
  38. (typeof replaceValue === 'string' && replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1)
  39. ) {
  40. var res = maybeCallNative(nativeReplace, regexp, this, replaceValue);
  41. if (res.done) return res.value;
  42. }
  43. var rx = anObject(regexp);
  44. var S = String(this);
  45. var functionalReplace = typeof replaceValue === 'function';
  46. if (!functionalReplace) replaceValue = String(replaceValue);
  47. var global = rx.global;
  48. if (global) {
  49. var fullUnicode = rx.unicode;
  50. rx.lastIndex = 0;
  51. }
  52. var results = [];
  53. while (true) {
  54. var result = regExpExec(rx, S);
  55. if (result === null) break;
  56. results.push(result);
  57. if (!global) break;
  58. var matchStr = String(result[0]);
  59. if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
  60. }
  61. var accumulatedResult = '';
  62. var nextSourcePosition = 0;
  63. for (var i = 0; i < results.length; i++) {
  64. result = results[i];
  65. var matched = String(result[0]);
  66. var position = max(min(toInteger(result.index), S.length), 0);
  67. var captures = [];
  68. // NOTE: This is equivalent to
  69. // captures = result.slice(1).map(maybeToString)
  70. // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
  71. // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
  72. // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
  73. for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
  74. var namedCaptures = result.groups;
  75. if (functionalReplace) {
  76. var replacerArgs = [matched].concat(captures, position, S);
  77. if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
  78. var replacement = String(replaceValue.apply(undefined, replacerArgs));
  79. } else {
  80. replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
  81. }
  82. if (position >= nextSourcePosition) {
  83. accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
  84. nextSourcePosition = position + matched.length;
  85. }
  86. }
  87. return accumulatedResult + S.slice(nextSourcePosition);
  88. }
  89. ];
  90. // https://tc39.github.io/ecma262/#sec-getsubstitution
  91. function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
  92. var tailPos = position + matched.length;
  93. var m = captures.length;
  94. var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
  95. if (namedCaptures !== undefined) {
  96. namedCaptures = toObject(namedCaptures);
  97. symbols = SUBSTITUTION_SYMBOLS;
  98. }
  99. return nativeReplace.call(replacement, symbols, function (match, ch) {
  100. var capture;
  101. switch (ch.charAt(0)) {
  102. case '$': return '$';
  103. case '&': return matched;
  104. case '`': return str.slice(0, position);
  105. case "'": return str.slice(tailPos);
  106. case '<':
  107. capture = namedCaptures[ch.slice(1, -1)];
  108. break;
  109. default: // \d\d?
  110. var n = +ch;
  111. if (n === 0) return match;
  112. if (n > m) {
  113. var f = floor(n / 10);
  114. if (f === 0) return match;
  115. if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
  116. return match;
  117. }
  118. capture = captures[n - 1];
  119. }
  120. return capture === undefined ? '' : capture;
  121. });
  122. }
  123. });