是否有JavaScript的String.indexOf()允许正则expression式的版本?

在JavaScript中,是否有相当于String.indexOf()采取正则expression式,而不是一个string的第一个第一个参数,同时仍然允许第二个参数?

我需要做类似的事情

str.indexOf(/[abc]/ , i); 

 str.lastIndexOf(/[abc]/ , i); 

虽然String.search()将一个正则expression式作为参数,但它不允许我指定第二个参数!

编辑:
这原来比我原先想象的更难,所以我写了一个小的testing函数来testing所有提供的解决scheme…它假定regexIndexOf和regexLastIndexOf已被添加到string对象。

 function test (str) { var i = str.length +2; while (i--) { if (str.indexOf('a',i) != str.regexIndexOf(/a/,i)) alert (['failed regexIndexOf ' , str,i , str.indexOf('a',i) , str.regexIndexOf(/a/,i)]) ; if (str.lastIndexOf('a',i) != str.regexLastIndexOf(/a/,i) ) alert (['failed regexLastIndexOf ' , str,i,str.lastIndexOf('a',i) , str.regexLastIndexOf(/a/,i)]) ; } } 

我正在testing如下,以确保至less为一个字符正则expression式,结果是一样的,如果我们使用indexOf

//找出x中的一个
试验( 'XXX');
试验( 'AXX');
试验( 'XAX');
试验( 'XXA');
试验( 'AXA');
试验( '的Xaa');
试验( 'AAX');
试验( 'AAA');

结合已经提到的一些方法(indexOf显然是相当简单的),我认为这些function可以做到这一点:

 String.prototype.regexIndexOf = function(regex, startpos) { var indexOf = this.substring(startpos || 0).search(regex); return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf; } String.prototype.regexLastIndexOf = function(regex, startpos) { regex = (regex.global) ? regex : new RegExp(regex.source, "g" + (regex.ignoreCase ? "i" : "") + (regex.multiLine ? "m" : "")); if(typeof (startpos) == "undefined") { startpos = this.length; } else if(startpos < 0) { startpos = 0; } var stringToWorkWith = this.substring(0, startpos + 1); var lastIndexOf = -1; var nextStop = 0; while((result = regex.exec(stringToWorkWith)) != null) { lastIndexOf = result.index; regex.lastIndex = ++nextStop; } return lastIndexOf; } 

显然,修改内置的String对象会为大多数人发出红旗,但是这可能是一次不是什么大事; 只要意识到这一点。


更新:编辑regexLastIndexOf() ,似乎现在模仿lastIndexOf() 。 请让我知道,如果它仍然失败,在什么情况下。


更新:通过在这个页面上的评论和我自己的所有testing。 当然,这并不意味着它是防弹的。 任何反馈赞赏。

String构造函数的实例有一个.search()方法 ,它接受RegExp并返回第一个匹配的索引。

要从一个特定的位置开始search(伪造.indexOf()的第二个参数),可以切掉第一个字符:

 str.slice(i).search(/re/) 

但是,这将得到较短的string中的索引(第一部分被切掉后),所以如果不是-1 ,则需要将切掉的部分( i )的长度添加到返回的索引中。 这会给你在原始string的索引:

 function regexIndexOf(text, re, i) { var indexInSuffix = text.slice(i).search(re); return indexInSuffix < 0 ? indexInSuffix : indexInSuffix + i; } 

我有一个简短的版本给你。 这对我来说很有效!

 var match = str.match(/[abc]/gi); var firstIndex = str.indexOf(match[0]); var lastIndex = str.lastIndexOf(match[match.length-1]); 

如果你想要一个原型版本:

 String.prototype.indexOfRegex = function(regex){ var match = this.match(regex); return match ? this.indexOf(match[0]) : -1; } String.prototype.lastIndexOfRegex = function(regex){ var match = this.match(regex); return match ? this.lastIndexOf(match[match.length-1]) : -1; } 

编辑 :如果你想添加fromIndex的支持

 String.prototype.indexOfRegex = function(regex, fromIndex){ var str = fromIndex ? this.substring(fromIndex) : this; var match = str.match(regex); return match ? str.indexOf(match[0]) + fromIndex : -1; } String.prototype.lastIndexOfRegex = function(regex, fromIndex){ var str = fromIndex ? this.substring(0, fromIndex) : this; var match = str.match(regex); return match ? str.lastIndexOf(match[match.length-1]) : -1; } 

要使用它,就像这样简单:

 var firstIndex = str.indexOfRegex(/[abc]/gi); var lastIndex = str.lastIndexOfRegex(/[abc]/gi); 

使用:

 str.search(regex) 

请参阅这里的文档。

根据BaileyP的回答。 主要区别在于如果模式不匹配,这些方法返回-1

编辑:谢谢Jason Bunting的回答,我有一个想法。 为什么不修改正则expression式的.lastIndex属性? 尽pipe这只适用于带有全局标志( /g )的模式。

编辑:更新以通过testing用例。

 String.prototype.regexIndexOf = function(re, startPos) { startPos = startPos || 0; if (!re.global) { var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":""); re = new RegExp(re.source, flags); } re.lastIndex = startPos; var match = re.exec(this); if (match) return match.index; else return -1; } String.prototype.regexLastIndexOf = function(re, startPos) { startPos = startPos === undefined ? this.length : startPos; if (!re.global) { var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":""); re = new RegExp(re.source, flags); } var lastSuccess = -1; for (var pos = 0; pos <= startPos; pos++) { re.lastIndex = pos; var match = re.exec(this); if (!match) break; pos = match.index; if (pos <= startPos) lastSuccess = pos; } return lastSuccess; } 

你可以使用substr。

 str.substr(i).match(/[abc]/); 

它本身不是,但你当然可以添加这个function

 <script type="text/javascript"> String.prototype.regexIndexOf = function( pattern, startIndex ) { startIndex = startIndex || 0; var searchResult = this.substr( startIndex ).search( pattern ); return ( -1 === searchResult ) ? -1 : searchResult + startIndex; } String.prototype.regexLastIndexOf = function( pattern, startIndex ) { startIndex = startIndex === undefined ? this.length : startIndex; var searchResult = this.substr( 0, startIndex ).reverse().regexIndexOf( pattern, 0 ); return ( -1 === searchResult ) ? -1 : this.length - ++searchResult; } String.prototype.reverse = function() { return this.split('').reverse().join(''); } // Indexes 0123456789 var str = 'caabbccdda'; alert( [ str.regexIndexOf( /[cd]/, 4 ) , str.regexLastIndexOf( /[cd]/, 4 ) , str.regexIndexOf( /[yz]/, 4 ) , str.regexLastIndexOf( /[yz]/, 4 ) , str.lastIndexOf( 'd', 4 ) , str.regexLastIndexOf( /d/, 4 ) , str.lastIndexOf( 'd' ) , str.regexLastIndexOf( /d/ ) ] ); </script> 

我没有完全testing这些方法,但他们似乎工作到目前为止。

RexExp实例已经有一个lastIndex属性(如果它们是全局的),所以我正在做的是复制正则expression式,稍微修改它以适应我们的目的,在string上exec它并查看lastIndex 。 这将不可避免地比在string上循环更快。 (你有足够的例子来说明如何把它放在string原型,对不对?)

 function reIndexOf(reIn, str, startIndex) { var re = new RegExp(reIn.source, 'g' + (reIn.ignoreCase ? 'i' : '') + (reIn.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; function reLastIndexOf(reIn, str, startIndex) { var src = /\$$/.test(reIn.source) && !/\\\$$/.test(reIn.source) ? reIn.source : reIn.source + '(?![\\S\\s]*' + reIn.source + ')'; var re = new RegExp(src, 'g' + (reIn.ignoreCase ? 'i' : '') + (reIn.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; reIndexOf(/[abc]/, "tommy can eat"); // Returns 6 reIndexOf(/[abc]/, "tommy can eat", 8); // Returns 11 reLastIndexOf(/[abc]/, "tommy can eat"); // Returns 11 

您也可以将函数原型化到RegExp对象上:

 RegExp.prototype.indexOf = function(str, startIndex) { var re = new RegExp(this.source, 'g' + (this.ignoreCase ? 'i' : '') + (this.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; RegExp.prototype.lastIndexOf = function(str, startIndex) { var src = /\$$/.test(this.source) && !/\\\$$/.test(this.source) ? this.source : this.source + '(?![\\S\\s]*' + this.source + ')'; var re = new RegExp(src, 'g' + (this.ignoreCase ? 'i' : '') + (this.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; /[abc]/.indexOf("tommy can eat"); // Returns 6 /[abc]/.indexOf("tommy can eat", 8); // Returns 11 /[abc]/.lastIndexOf("tommy can eat"); // Returns 11 

我如何修改RegExp的快速说明:对于indexOf我只需要确保设置了全局标志。 对于lastIndexOf ,我使用否定性前瞻来查找最后一次出现,除非RegExp在string末尾已经匹配。

在所有build议的解决scheme以某种方式失败我的testing(编辑:一些更新,以通过testing后,我写这个)我发现了Array.indexOf和Array.lastIndexOf的mozilla实现

我用这些来实现我的版本的String.prototype.regexIndexOf和String.prototype.regexLastIndexOf如下:

 String.prototype.regexIndexOf = function(elt /*, from*/) { var arr = this.split(''); var len = arr.length; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in arr && elt.exec(arr[from]) ) return from; } return -1; }; String.prototype.regexLastIndexOf = function(elt /*, from*/) { var arr = this.split(''); var len = arr.length; var from = Number(arguments[1]); if (isNaN(from)) { from = len - 1; } else { from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; else if (from >= len) from = len - 1; } for (; from > -1; from--) { if (from in arr && elt.exec(arr[from]) ) return from; } return -1; }; 

他们似乎通过了我在问题中提供的testingfunction。

显然他们只有在正则expression式匹配一个字符的情况下才能工作,但这对于我的目的来说已经足够了,因为我将用它作为([abc],\ s,\ W,\ D)

我会继续监视这个问题,以防有人提供一个更好/更快/更清洁/更通用的实现,可以在任何正则expression式上工作。

我也需要一个regexIndexOf函数,所以我自己regexIndexOf了一个。 不过,我怀疑,它的优化,但我想它应该正常工作。

 Array.prototype.regexIndexOf = function (regex, startpos = 0) { len = this.length; for(x = startpos; x < len; x++){ if(typeof this[x] != 'undefined' && (''+this[x]).match(regex)){ return x; } } return -1; } arr = []; arr.push(null); arr.push(NaN); arr[3] = 7; arr.push('asdf'); arr.push('qwer'); arr.push(9); arr.push('...'); console.log(arr); arr.regexIndexOf(/\d/, 4); 

在某些简单的情况下,您可以使用split来简化向后search。

 function regexlast(string,re){ var tokens=string.split(re); return (tokens.length>1)?(string.length-tokens[tokens.length-1].length):null; } 

这有几个严重的问题:

  1. 重叠的比赛不会出现
  2. 返回的索引是比赛的结束,而不是开始(如果你的正则expression式是一个常量罚款)

但是,光明的一面是代码less。 对于不能重叠的恒定长度的正则expression式(比如/\s\w/用于查找单词边界),这已经足够了。

对于稀疏匹配的数据,使用string.search是跨浏览器最快的。 它将每个迭代的string重新切片为:

 function lastIndexOfSearch(string, regex, index) { if(index === 0 || index) string = string.slice(0, Math.max(0,index)); var idx; var offset = -1; while ((idx = string.search(regex)) !== -1) { offset += idx + 1; string = string.slice(idx + 1); } return offset; } 

对于密集的数据,我做了这个。 与执行方法相比,这是相当复杂的,但是对于密集数据来说,比我尝试的其他方法快2-10倍,比接受的解决scheme快100倍。 要点是:

  1. 它调用一次传入的正则expression式来validation是否匹配或提前退出。 我这样做(?=在类似的方法,但IE浏览器检查与exec是显着更快。
  2. 它构造并caching格式为“(r)”的修改后的正则expression式。 (?! ?r)'
  3. 新的正则expression式被执行,并返回exec或第一个exec的结果;

     function lastIndexOfGroupSimple(string, regex, index) { if (index === 0 || index) string = string.slice(0, Math.max(0, index + 1)); regex.lastIndex = 0; var lastRegex, index flags = 'g' + (regex.multiline ? 'm' : '') + (regex.ignoreCase ? 'i' : ''), key = regex.source + '$' + flags, match = regex.exec(string); if (!match) return -1; if (lastIndexOfGroupSimple.cache === undefined) lastIndexOfGroupSimple.cache = {}; lastRegex = lastIndexOfGroupSimple.cache[key]; if (!lastRegex) lastIndexOfGroupSimple.cache[key] = lastRegex = new RegExp('.*(' + regex.source + ')(?!.*?' + regex.source + ')', flags); index = match.index; lastRegex.lastIndex = match.index; return (match = lastRegex.exec(string)) ? lastRegex.lastIndex - match[1].length : index; }; 

方法的jsPerf

我不明白上面testing的目的。 需要一个正则expression式的情况是不可能与索引的索引进行比较的,我认为这是首先制定该方法的要点。 为了让testing通过,使用'xxx +(?!x)'比调整正则expression式迭代的方式更有意义。

杰森·邦廷的最后一个索引不起作用。 矿不是最佳的,但它的工作。

 //Jason Bunting's String.prototype.regexIndexOf = function(regex, startpos) { var indexOf = this.substring(startpos || 0).search(regex); return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf; } String.prototype.regexLastIndexOf = function(regex, startpos) { var lastIndex = -1; var index = this.regexIndexOf( regex ); startpos = startpos === undefined ? this.length : startpos; while ( index >= 0 && index < startpos ) { lastIndex = index; index = this.regexIndexOf( regex, index + 1 ); } return lastIndex; } 

仍然没有执行请求的任务的本地方法。

这是我正在使用的代码。 它模仿了String.prototype.indexOf和String.prototype.lastIndexOf方法的行为,但除了表示要search的值的string之外,它们还接受RegExp作为search参数。

是的,答案是相当长的,因为它尽可能接近当前的标准,当然也包含了合理数量的JSDOC评论。 然而,一旦缩小,代码只有2.27k,一旦gzipped传输只有1023字节。

这个添加到String.prototype的2个方法(在可用的情况下使用Object.defineProperty )是:

  1. searchOf
  2. searchLastOf

它通过了OP发布的所有testing,另外我已经在日常使用中对这些例程进行了相当全面的testing,并试图确保它们可以在多个环境中工作,但总是欢迎反馈/问题。

 /*jslint maxlen:80, browser:true */ /* * Properties used by searchOf and searchLastOf implementation. */ /*property MAX_SAFE_INTEGER, abs, add, apply, call, configurable, defineProperty, enumerable, exec, floor, global, hasOwnProperty, ignoreCase, index, lastIndex, lastIndexOf, length, max, min, multiline, pow, prototype, remove, replace, searchLastOf, searchOf, source, toString, value, writable */ /* * Properties used in the testing of searchOf and searchLastOf implimentation. */ /*property appendChild, createTextNode, getElementById, indexOf, lastIndexOf, length, searchLastOf, searchOf, unshift */ (function () { 'use strict'; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1, getNativeFlags = new RegExp('\\/([az]*)$', 'i'), clipDups = new RegExp('([\\s\\S])(?=[\\s\\S]*\\1)', 'g'), pToString = Object.prototype.toString, pHasOwn = Object.prototype.hasOwnProperty, stringTagRegExp; /** * Defines a new property directly on an object, or modifies an existing * property on an object, and returns the object. * * @private * @function * @param {Object} object * @param {string} property * @param {Object} descriptor * @returns {Object} * @see https://goo.gl/CZnEqg */ function $defineProperty(object, property, descriptor) { if (Object.defineProperty) { Object.defineProperty(object, property, descriptor); } else { object[property] = descriptor.value; } return object; } /** * Returns true if the operands are strictly equal with no type conversion. * * @private * @function * @param {*} a * @param {*} b * @returns {boolean} * @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4 */ function $strictEqual(a, b) { return a === b; } /** * Returns true if the operand inputArg is undefined. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isUndefined(inputArg) { return $strictEqual(typeof inputArg, 'undefined'); } /** * Provides a string representation of the supplied object in the form * "[object type]", where type is the object type. * * @private * @function * @param {*} inputArg The object for which a class string represntation * is required. * @returns {string} A string value of the form "[object type]". * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.2 */ function $toStringTag(inputArg) { var val; if (inputArg === null) { val = '[object Null]'; } else if ($isUndefined(inputArg)) { val = '[object Undefined]'; } else { val = pToString.call(inputArg); } return val; } /** * The string tag representation of a RegExp object. * * @private * @type {string} */ stringTagRegExp = $toStringTag(getNativeFlags); /** * Returns true if the operand inputArg is a RegExp. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isRegExp(inputArg) { return $toStringTag(inputArg) === stringTagRegExp && pHasOwn.call(inputArg, 'ignoreCase') && typeof inputArg.ignoreCase === 'boolean' && pHasOwn.call(inputArg, 'global') && typeof inputArg.global === 'boolean' && pHasOwn.call(inputArg, 'multiline') && typeof inputArg.multiline === 'boolean' && pHasOwn.call(inputArg, 'source') && typeof inputArg.source === 'string'; } /** * The abstract operation throws an error if its argument is a value that * cannot be converted to an Object, otherwise returns the argument. * * @private * @function * @param {*} inputArg The object to be tested. * @throws {TypeError} If inputArg is null or undefined. * @returns {*} The inputArg if coercible. * @see https://goo.gl/5GcmVq */ function $requireObjectCoercible(inputArg) { var errStr; if (inputArg === null || $isUndefined(inputArg)) { errStr = 'Cannot convert argument to object: ' + inputArg; throw new TypeError(errStr); } return inputArg; } /** * The abstract operation converts its argument to a value of type string * * @private * @function * @param {*} inputArg * @returns {string} * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring */ function $toString(inputArg) { var type, val; if (inputArg === null) { val = 'null'; } else { type = typeof inputArg; if (type === 'string') { val = inputArg; } else if (type === 'undefined') { val = type; } else { if (type === 'symbol') { throw new TypeError('Cannot convert symbol to string'); } val = String(inputArg); } } return val; } /** * Returns a string only if the arguments is coercible otherwise throws an * error. * * @private * @function * @param {*} inputArg * @throws {TypeError} If inputArg is null or undefined. * @returns {string} */ function $onlyCoercibleToString(inputArg) { return $toString($requireObjectCoercible(inputArg)); } /** * The function evaluates the passed value and converts it to an integer. * * @private * @function * @param {*} inputArg The object to be converted to an integer. * @returns {number} If the target value is NaN, null or undefined, 0 is * returned. If the target value is false, 0 is returned * and if true, 1 is returned. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4 */ function $toInteger(inputArg) { var number = +inputArg, val = 0; if ($strictEqual(number, number)) { if (!number || number === Infinity || number === -Infinity) { val = number; } else { val = (number > 0 || -1) * Math.floor(Math.abs(number)); } } return val; } /** * Copies a regex object. Allows adding and removing native flags while * copying the regex. * * @private * @function * @param {RegExp} regex Regex to copy. * @param {Object} [options] Allows specifying native flags to add or * remove while copying the regex. * @returns {RegExp} Copy of the provided regex, possibly with modified * flags. */ function $copyRegExp(regex, options) { var flags, opts, rx; if (options !== null && typeof options === 'object') { opts = options; } else { opts = {}; } // Get native flags in use flags = getNativeFlags.exec($toString(regex))[1]; flags = $onlyCoercibleToString(flags); if (opts.add) { flags += opts.add; flags = flags.replace(clipDups, ''); } if (opts.remove) { // Would need to escape `options.remove` if this was public rx = new RegExp('[' + opts.remove + ']+', 'g'); flags = flags.replace(rx, ''); } return new RegExp(regex.source, flags); } /** * The abstract operation ToLength converts its argument to an integer * suitable for use as the length of an array-like object. * * @private * @function * @param {*} inputArg The object to be converted to a length. * @returns {number} If len <= +0 then +0 else if len is +INFINITY then * 2^53-1 else min(len, 2^53-1). * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength */ function $toLength(inputArg) { return Math.min(Math.max($toInteger(inputArg), 0), MAX_SAFE_INTEGER); } /** * Copies a regex object so that it is suitable for use with searchOf and * searchLastOf methods. * * @private * @function * @param {RegExp} regex Regex to copy. * @returns {RegExp} */ function $toSearchRegExp(regex) { return $copyRegExp(regex, { add: 'g', remove: 'y' }); } /** * Returns true if the operand inputArg is a member of one of the types * Undefined, Null, Boolean, Number, Symbol, or String. * * @private * @function * @param {*} inputArg * @returns {boolean} * @see https://goo.gl/W68ywJ * @see https://goo.gl/ev7881 */ function $isPrimitive(inputArg) { var type = typeof inputArg; return type === 'undefined' || inputArg === null || type === 'boolean' || type === 'string' || type === 'number' || type === 'symbol'; } /** * The abstract operation converts its argument to a value of type Object * but fixes some environment bugs. * * @private * @function * @param {*} inputArg The argument to be converted to an object. * @throws {TypeError} If inputArg is not coercible to an object. * @returns {Object} Value of inputArg as type Object. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.9 */ function $toObject(inputArg) { var object; if ($isPrimitive($requireObjectCoercible(inputArg))) { object = Object(inputArg); } else { object = inputArg; } return object; } /** * Converts a single argument that is an array-like object or list (eg. * arguments, NodeList, DOMTokenList (used by classList), NamedNodeMap * (used by attributes property)) into a new Array() and returns it. * This is a partial implementation of the ES6 Array.from * * @private * @function * @param {Object} arrayLike * @returns {Array} */ function $toArray(arrayLike) { var object = $toObject(arrayLike), length = $toLength(object.length), array = [], index = 0; array.length = length; while (index < length) { array[index] = object[index]; index += 1; } return array; } if (!String.prototype.searchOf) { /** * This method returns the index within the calling String object of * the first occurrence of the specified value, starting the search at * fromIndex. Returns -1 if the value is not found. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object or a String. * Anything else is implicitly converted to * a String. * @param {Number} [fromIndex] The location within the calling string * to start the search from. It can be any * integer. The default value is 0. If * fromIndex < 0 the entire string is * searched (same as passing 0). If * fromIndex >= str.length, the method will * return -1 unless searchValue is an empty * string in which case str.length is * returned. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, 'searchOf', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), args = $toArray(arguments), result = -1, fromIndex, match, rx; if (!$isRegExp(regex)) { return String.prototype.indexOf.apply(str, args); } if ($toLength(args.length) > 1) { fromIndex = +args[1]; if (fromIndex < 0) { fromIndex = 0; } } else { fromIndex = 0; } if (fromIndex >= $toLength(str.length)) { return result; } rx = $toSearchRegExp(regex); rx.lastIndex = fromIndex; match = rx.exec(str); if (match) { result = +match.index; } return result; } }); } if (!String.prototype.searchLastOf) { /** * This method returns the index within the calling String object of * the last occurrence of the specified value, or -1 if not found. * The calling string is searched backward, starting at fromIndex. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object or a String. * Anything else is implicitly converted to * a String. * @param {Number} [fromIndex] Optional. The location within the * calling string to start the search at, * indexed from left to right. It can be * any integer. The default value is * str.length. If it is negative, it is * treated as 0. If fromIndex > str.length, * fromIndex is treated as str.length. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, 'searchLastOf', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), args = $toArray(arguments), result = -1, fromIndex, length, match, pos, rx; if (!$isRegExp(regex)) { return String.prototype.lastIndexOf.apply(str, args); } length = $toLength(str.length); if (!$strictEqual(args[1], args[1])) { fromIndex = length; } else { if ($toLength(args.length) > 1) { fromIndex = $toInteger(args[1]); } else { fromIndex = length - 1; } } if (fromIndex >= 0) { fromIndex = Math.min(fromIndex, length - 1); } else { fromIndex = length - Math.abs(fromIndex); } pos = 0; rx = $toSearchRegExp(regex); while (pos <= fromIndex) { rx.lastIndex = pos; match = rx.exec(str); if (!match) { break; } pos = +match.index; if (pos <= fromIndex) { result = pos; } pos += 1; } return result; } }); } }()); (function () { 'use strict'; /* * testing as follow to make sure that at least for one character regexp, * the result is the same as if we used indexOf */ var pre = document.getElementById('out'); function log(result) { pre.appendChild(document.createTextNode(result + '\n')); } function test(str) { var i = str.length + 2, r, a, b; while (i) { a = str.indexOf('a', i); b = str.searchOf(/a/, i); r = ['Failed', 'searchOf', str, i, a, b]; if (a === b) { r[0] = 'Passed'; } log(r); a = str.lastIndexOf('a', i); b = str.searchLastOf(/a/, i); r = ['Failed', 'searchLastOf', str, i, a, b]; if (a === b) { r[0] = 'Passed'; } log(r); i -= 1; } } /* * Look for the a among the xes */ test('xxx'); test('axx'); test('xax'); test('xxa'); test('axa'); test('xaa'); test('aax'); test('aaa'); }()); 
 <pre id="out"></pre> 

Well, as you are just looking to match the position of a character , regex is possibly overkill.

I presume all you want is, instead of "find first of these this character" , just find first of these characters.

This of course is the simple answer, but does what your question sets out to do, albeit without the regex part ( because you didn't clarify why specifically it had to be a regex )

 function mIndexOf( str , chars, offset ) { var first = -1; for( var i = 0; i < chars.length; i++ ) { var p = str.indexOf( chars[i] , offset ); if( p < first || first === -1 ) { first = p; } } return first; } String.prototype.mIndexOf = function( chars, offset ) { return mIndexOf( this, chars, offset ); # I'm really averse to monkey patching. }; mIndexOf( "hello world", ['a','o','w'], 0 ); >> 4 mIndexOf( "hello world", ['a'], 0 ); >> -1 mIndexOf( "hello world", ['a','o','w'], 4 ); >> 4 mIndexOf( "hello world", ['a','o','w'], 5 ); >> 6 mIndexOf( "hello world", ['a','o','w'], 7 ); >> -1 mIndexOf( "hello world", ['a','o','w','d'], 7 ); >> 10 mIndexOf( "hello world", ['a','o','w','d'], 10 ); >> 10 mIndexOf( "hello world", ['a','o','w','d'], 11 ); >> -1