Callable regular expressions
Way back in the day when Netscape implemented regular expressions in JavaScript, it made them callable. If you slapped an argument list after a regular expression, it’d act as if you called RegExp.prototype.exec
on it with the provided arguments.
var r = /abc/, res; res = r("abc"); assert(res.length === 1); res = r("def"); assert(res === null);
Why? Beats me. I’d have thought .exec
was easy enough to type and clearer to boot, myself. Hopefully readers familiar with the history can explain in comments.
Problems
Callable regular expressions present one immediate problem to a “naive” implementation: their behavior with typeof
. According to ECMAScript, the typeof
for any object which is callable should be "function"
, and Netscape and Mozilla for a long time faithfully implemented this. This tended to cause much confusion in practice, so browsers that implemented callable regular expressions eventually changed typeof
to arguably “lie” for regular expressions and return "object"
. In SpiderMonkey the “fix” was an utterly inelegant hack which distinguished callables as either regular expressions or not, to determine typeof
behavior.
Past this, callable regular expressions complicate implementing callability and optimizations of it. Implementations supporting getters and setters (once purely as an extension, now standardized in ES5) must consider the case where the getter or setter is a regular expression and do something appropriate. And of course they must handle regular old calls, qualified (/a/()
) and unqualified (({ p: /a/ }).p()
) both. Mozilla’s had a solid trickle of bugs involving callable regular expressions, almost always filed as a result of Jesse‘s evil fuzzers (and not due to actual sites breaking).
It’s also hard to justify callable regular expressions as an extension. While ECMAScript explicitly permits extensions, it generally prefers extensions to be new methods or properties of existing objects. Regular expression callability is neither of these: instead it’s adding an internal hook to regular expressions to make them callable. This might not technically be contrary to the spec, but it goes against its spirit.
Regular expressions won’t be callable in Firefox 5
No one’s ever really used callable regular expressions. They’re non-standard, not all browsers implement them, and they unnecessarily complicate implementations. So, in concert with other browser engines like WebKit, we’re making regular expressions non-callable in Firefox 5. (Regular expressions are callable in Firefox 4, but of course don’t rely on this.)
You can experiment with a version of Firefox with these changes by downloading a TraceMonkey nightly build. Trunk’s still locked down for Firefox 4, so it won’t pick up the change until Firefox 4 branches and trunk reopens for changes targeted at the next release. (Don’t forget to use the profile manager if you want to keep the settings you use with your primary Firefox installation pristine.)
Sad to see all the various nice bits of JS (especially for extensions, where web-compat isn’t a concern) go because optimizing things is Hard. This means we lose nice things like […].some(/^\w+$/) (hey, it’s callable, of course it can stand in for callbacks).
I would consider that to be a perfectly valid extension: it’s extending the [[Call]] internal property of RegExp.prototype 🙂
In the mean time, I guess I need to go figure out how to work with proxies. (I think I’m doing it wrong – wrapping and toString()ing Boolean, Number, String, RegExp, or Date breaks with “TypeError on line ???: ???.prototype.toString called on incompatible Proxy”, but Array, Object, and Function are fine; wrapping window works for some things and not others.)
Comment by Mook — 06.03.11 @ 21:32
Is it such a burden to do
[...].some(function(v) { return /^\w+$/.exec(v); })
? It’s certainly much clearer. And of course, this only happens to work becauseexec
ignores the trailing index andthis
parameterssome
passes to it.Another thought: isn’t this sort of playing with fire, in that a regular expression literal like this implicitly carries around a
lastIndex
within it?It’s not so much about the validity of the extension. It’s about its nature. I think the
typeof
snafu demonstrates why making a specified-uncallable object callable is not a good idea.Your proxy examples don’t work because the spec requires in those places that the
this
object have a particular class, and proxies aren’t transparent for that. You could raise the concern ones-discuss
if you wanted. I believe others have done this for arrays ([].concat
has special behavior conditioned on class-is Array) in the past. This issue irritates me a bit, because the only way I see to address it is to have every place that does a class-check do…something other than an exact class-check. I don’t know what that would be. And requiring every place to do it is just asking for one place or another to be missed. Maybe this is just the rough edges of trying to retrofit a (new, different) layer of extensibility atop an older system not designed with that in mind.Comment by Jeff — 06.03.11 @ 22:17
…although I should say, since regular expression callability uses
exec
and nottest
, your hack is not going to be as efficient as[...].some(function(v) { return /^\w+$/.test(v); })
, which is even better than my suggestion, and better than yours as well.Comment by Jeff — 06.03.11 @ 22:21
Then extend some() to take a regular expression as an argument. Everybody happy. Constructive thinking, guys :).
Comment by Laurens Holst — 07.03.11 @ 11:25
Sorry for the off topic, and I am sorry to point it, but your website is absolutely not scrollable. Or, it scrolls, but is exceptionally slow — on a top-end machine with Google Chrome.
Comment by Martin — 07.03.11 @ 13:27
Strange, works fine for me, in Firefox, Chrome, Opera, and Epiphany all. I think I’ve seen it slightly laggy to scroll in older versions, at times, but not so horribly as to seriously interfere with usability.
Do note, however, that any code I write for the site (in posts, in the theme I use, etc.) I write primarily for “reasonably” “good” browsers. And I’m not averse to using the site to “nudge” viewers toward better browsers (for example, if I embed my own video, I use
<video>
to do it, without fallback beyond a “get a new browser” link, and I only provide “good” video formats like Ogg Theora [historically] or WebM [in the future]). So if something doesn’t work, for my own personal site, I’m as likely to recommend a browser upgrade as I am to fix whatever’s broken. I absolutely wouldn’t recommend this for most sites, and indeed I’d usually strongly discourage doing so. But let’s face it: nothing I say is so incredibly important that the site absolutely must work for everyone. So I’ll keep using it as my own little sandbox where I play with cool new stuff (like a fixed background withbackground-size: cover
, which I’m guessing is what causes woes for you), to some extent, for the foreseeable future.Comment by Jeff — 07.03.11 @ 20:03