Re: Object.getPrototypeOf(/123/)

183 views
Skip to first unread message

Axel Rauschmayer

unread,
Sep 5, 2012, 2:07:50 AM9/5/12
to
When you create an instance of a type Foo then that instance’s prototype is Foo.prototype:

    % function Foo() {}
    % var f = new Foo();
    % Object.getPrototypeOf(f) === Foo.prototype
    true

In fact, that is how the test instanceof Foo is performed [1]. Keeping this in mind, it follows that the regular expression /123/ should have the prototype RegExp.prototype:

    % Object.getPrototypeOf(/123/) === RegExp.prototype
    true

Because RegExp is a subtype of Object, RegExp instances also have Object.prototype in their prototype chain [1]:

    % Object.getPrototypeOf(Object.getPrototypeOf(/123/)) === Object.prototype
    true


Reference:
[1] http://www.2ality.com/2012/08/instanceof-object.html

Axel Rauschmayer

unread,
Sep 5, 2012, 2:04:29 AM9/5/12
to js-...@googlegroups.com
Right. That is one aspect that I had ignored: For a builtin Foo, JavaScript treats Foo.prototype as a kind of a primal instance. For example, String.prototype.match(regexp) checks the type of its argument via the internal property [[Class]]. That property cannot be directly accessed, but it is returned by Object.prototype.toString, wedged between the strings "[object " and "]":

    % Object.prototype.toString.call(/abc/)
    '[object RegExp]'

RegExp.prototype has a property [[Class]] whose value is "RegExp". That property is inherited by its instances and the reason for the previous result.

    % Object.prototype.toString.call(RegExp.prototype)
    '[object RegExp]'

Therefore, you have a paradoxical situation: On one hand, RegExp.prototype is not an instance of RegExp:

    % RegExp.prototype instanceof RegExp
    false

On the other hand, it is accepted as a valid regular expression by builtin methods. For example:

    % "xzy".match(RegExp.prototype)
    [ '', index: 0, input: 'xzy' ]

The primal regular expression is the empty regular expression, which matches everything:

    % RegExp.prototype.test("fdsadfsadfadfa")
    true
    % RegExp.prototype.test("")
    true

The only way to write the empty regular expression is via new RegExp(""), you can’t write it as the regular expression literal //, because that would be a comment. But the following regular expression is equivalent:

    /(?:)/

It contains an empty non-capturing group – a group that almost doesn’t exist, it has no effect other than grouping. That explains how RegExp.prototype is displayed on a JavaScript command line:

    % RegExp.prototype
    /(?:)/

Luke Moody

unread,
Sep 5, 2012, 5:30:44 PM9/5/12
to js-...@googlegroups.com
Absolutely fascinating. I'm still getting my head around the intricacies of why certain things are the way they are in JS (I'm self-taught with no academic background in compsci or anything like that), so thanks for helping.

Following that logic, I checked out the behaviour of Array, and that follows the same rules:
% Object.getPrototypeOf([])
[]

Using Object.getOwnPropertyNames shows what I was originally expecting RegExp.prototype to return (albeit as an array of strings rather than an object), but after reading all of the above, it's clear to me why it doesn't behave as I was expecting it to:
% Object.getOwnPropertyNames(RegExp.prototype)
["toString", "multiline", "exec", "constructor", "source", "ignoreCase", "compile", "global", "test", "lastIndex"]


I guess my main intrigue for all of this originally came from how JS's global objects appear "at source" (if that's the correct term?). For example, if I were to write out the Array object, I'd write something like the following (in its most basic form, anyway):

function Array() {} // The constructor (allowing for `new Array()` usage (IMO, not in-keeping with the rest of JS)).
Array.isArray = function() {}; // A "class" method.
Array.prototype.push = function() {}; // A prototype method.

I've left out arguments, the vast majority of the methods/properties, any inherited methods/properties, and haven't defined things as writable, enumerable, or configurable, but in my head that's how an Array would look. Is that a good way of thinking about it, or am I on completely the wrong track? I'm aware that global objects probably aren't presented like that (V8, at least, compiles to machine code before executing JS, so global objects might not even be described in Javascript), but behaviour-wise..?

Thanks, and I hope this isn't too simplistic!

Axel Rauschmayer

unread,
Sep 6, 2012, 10:12:15 PM9/6/12
to js-...@googlegroups.com
That is a good approximation of how arrays are implemented. The idea of prototype objects as primal instances leads one slightly astray, because it is only true for built-in types. I prefer to think of prototype objects as analogs to classes [1]: They contain properties that are shared by all instances (mainly methods). Properties that are instance-specific, on the other hand, are set up in the constructor.

[1] http://www.2ality.com/2011/06/prototypes-as-classes.html
Reply all
Reply to author
Forward
0 new messages