Firefox includes a non-standard JavaScript extension that makes regular expressions callable as functions. This serves as a shorthand for calling a regex's exec
method. For example, in Firefox /regex/("string")
is equivalent to /regex/.exec("string")
. Early ECMAScript 4 proposals indicated this functionality would be added to the ES4 specification, but subsequent discussion on the ES4-discuss mailing list suggests it might be dropped.
However, you can implement something similar by adding call
and apply
methods to RegExp.prototype
, which could help with functional programming and duck-typed code that works with both functions and regular expressions. So let's add them:
RegExp.prototype.call = function (context, str) { return this.exec(str); }; RegExp.prototype.apply = function (context, args) { return this.exec(args[0]); };
Note that both of the above methods completely ignore the context
argument. You could pass in null
or whatever else as the context, and you'd get back the normal result of running exec
on the regex. Using the above methods, you can generically work with both regular expressions and functions wherever it's convenient to do so. A few obvious cases where this could be helpful are the JavaScript 1.6 array iteration methods. Following are implementations of filter
, every
, some
, and map
that allow them to be used cross-browser:
// Returns an array with the elements of an existng array for which the provided filtering function returns true Array.prototype.filter = function (func, context) { var results = []; for (var i = 0; i < this.length; i++) { if (i in this && func.call(context, this[i], i, this)) results.push(this[i]); } return results; }; // Returns true if every element in the array satisfies the provided testing function Array.prototype.every = function (func, context) { for (var i = 0; i < this.length; i++) { if (i in this && !func.call(context, this[i], i, this)) return false; } return true; }; // Returns true if at least one element in the array satisfies the provided testing function Array.prototype.some = function (func, context) { for (var i = 0; i < this.length; i++) { if (i in this && func.call(context, this[i], i, this)) return true; } return false; }; // Returns an array with the results of calling the provided function on every element in the provided array Array.prototype.map = function (func, context) { var results = []; for (var i = 0; i < this.length; i++) { if (i in this) results[i] = func.call(context, this[i], i, this); } return results; };
Because the array and null
values returned by exec
type-convert nicely to true
and false
, the above code allows you to use something like ["a","b","ab","ba"].filter(/^a/)
to return all values that start with "a": ["a","ab"]
. The code ["1",1,0,"a",3.1,256].filter(/^[1-9]\d*$/)
would return integers greater than zero, regardless of type: ["1",1,256]
. str.match(/a?b/g).filter(/^b/)
would return all matches of "b" not preceded by "a". This can be a convenient pattern since JavaScript doesn't support lookbehind.
All of the above examples already work with Firefox's native Array.prototype.filter
because of the indirect exec
calling feature in that browser, but they wouldn't work with the cross-browser implementation of filter
above without adding RegExp.prototype.call
.
Does this seem like something that would be useful to you? Can you think of other good examples where call
and apply
methods would be useful for regular expressions?
Update: This post has been translated into Chinese by PlanABC.net.
This brings me to an idea:…