es6.regexp.replace.js 4.5 KB

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