- The rules for the flags
/gand/yare complicated. - Regular expression operations keep state in RegExp objects, via property
.lastIndex. That leads to several pitfalls if a RegExp object is used multiple times. It also makes it impossible to freeze instances of RegExp. String.prototype.replace()accepts a callback where accessing named captures is inconvenient (via an object that is passed as the last argument).
- Improving the RegExp API without introducing a new constructor.
Common characteristics:
- Options object:
.startIndex = 0.returnIndices = false
- Don’t change the RegExp object in any way.
- Completely ignore
.lastIndex.
- Completely ignore
- Completely ignore the following flags:
/g(.global): not needed because each method is either global or non-global/y(.sticky): replaced by the assertion\G/d(.hasIndices): replaced by option.returnIndices- Not as important but I’d prefer an option for a method over a RegExp flag because this toggle is more about how an operation works than about how a RegExp matches.
.execOnce(str, options?): MatchObject.execMany(str, options?): Iteratable<MatchObject>.testOnce(str, options?): boolean
Better callback type signature: callback(matchObject)
.replaceOnce(stringOrRegExp, stringOrCallback, options?): string.replaceMany(stringOrRegExp, stringOrCallback, options?): string
Open questions:
- Should these methods forward to Symbol-keyed properties of
stringOrRegExp?- Another option: turn them into
RegExp.prototype.*methods.
- Another option: turn them into
String.prototype.search(patternStringOrRegExp): numberString.prototype.split(verbatimStringOrRegExp?, limit?): Array<string>
Open question:
- Would it make sense to support an additional argument
options?
RegExp.prototype.execRegExp.prototype.testString.prototype.matchString.prototype.matchAllString.prototype.replaceString.prototype.replaceAll
- Matches at the current matching position (0 or
.startIndex). - Loosely related to the
^assertion
How should legacy methods handle \G?
- It clashes with flag
/ybecause with that flag, a regular expression implicitly starts with\G.- Thus: throwing an exception when
\Gis used with/yseems the best option.
- Thus: throwing an exception when
- Other than that, we could specify a “current position” for all legacy methods (sometimes
.lastIndex, sometimes 0) and use that with\G. /yis ignored by.split(), so supporting\Gwould be an improvement.
- List of upcoming RegExp proposals (collapsed section “Future: Active proposals”): https://github.com/slevithan/awesome-regex#javascript-regex-evolution
- Currently, there is no plan to support multi-line RegExp literals in JavaScript. A template tag is a good alternative and would be very useful for the proposed flag
/x. - Two TC39 members have expressed an interest in adding a template tags for RegExps to JavaScript (source).
- A template tag could look like this: https://github.com/slevithan/regex
Allis already taken.- It’s just a first idea – suggestions welcome!
- Other options:
Multi,OnceOrMore
- Other options:
- I find non-overloaded methods easier to understand (they are also easier to statically type):
.execOnceand.execManyhave different return types. - I also like to avoid single big methods that do too much.
- Precedents for method pairs in the current API:
.replace()and.replaceAll().match()and.matchAll()
- Thanks to Steven Levithan for inspiring this proposal and his feedback to my ideas.
@slevithan Great feedback, thanks! I updated the Gist.