0

I have been looking through some shim/polyfill libraries and see that some of them have a shim for Object.getPrototypeOf. On fail to exist they fall through to using __proto__ and if that doesn't exist then to object.constructor.prototype.

I understand that __proto__ is "non-standard" and while slightly different to the Object.getPrototypeOf method, they can be pretty interchangeable.

I also understand that in principle the externally accessible object.constructor.prototype would suffice in many situations where neither of the other two existed (provided the prototype has not been reassigned).

Where I have a problem is with the following examples:

    function findPropertyOwner(object, property) {
        var count = 0;

        do {
            if (object.hasOwnProperty(property)) {
                return [object, count];
            }

            object = Object.getPrototypeOf(object);
            count += 1;
        } while (object);

        return undefined;
    }

Or

    function instanceOf(object, constructor) {
        while (object) {
            if (object === constructor.prototype) {
                return true;
            }

            object = Object.getPrototypeOf(object);
        }

        return false;
    }

With such examples as above where we "walk the chain" so to speak, if the shim falls back to object.constructor.prototype then we now end up in the horrible situation of an infinite loop.

My question: Is there any way to achieve the above code in an environment where Object.getPrototypeOf and __proto__ do not exist?

My feeling is that there is not, but I just want to check in case there is some information out there that I have not come across.

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • I still don't get it why people call `__proto__` deprecated. It may have been deprecated, but AFAIK it is currently in a normative section of the ES6 draft. http://people.mozilla.org/~jorendorff/es6-draft.html#sec-B.3.1 – Fabrício Matté May 29 '13 at 23:20
  • 1
    Best you could probably do would be to keep a list of prototype objects encountered, and check the list for circular references. If one is found, either jump to `Object.prototype`, or just `return false`. –  May 29 '13 at 23:25
  • And this: `while (object !== undefined && object !== null) {` ...is unnecessarily long. Just do this: `while (object != null) {` ...and it'll test both `null` and `undefined`. –  May 29 '13 at 23:29
  • can you provide some more information ? maybe on jsfiddle... – Givi May 29 '13 at 23:35
  • @Fabrício Matté I was aware of the discussion, I will change the wording to ´non-standard´, like on the MDN page. – Xotic750 May 29 '13 at 23:38
  • @squint I guess a reference of encountered prototypes would be the only solution and the line could be shortened to ´while (object) {´ I suppose :) – Xotic750 May 29 '13 at 23:40
  • @Givi I don't think you want me to point you to an infinite loop jsfiddle? – Xotic750 May 29 '13 at 23:40
  • Yeah, true since a prototype can't be a primitive, except for `null`. For a minute I was thinking that you might have the same issue with `__proto__`, but I did a quick test, and it seems that cyclical prototype chains are caught for you... which is nice! –  May 29 '13 at 23:42
  • @squint I tried keeping the references and jumping to ´Object.prototype´ and while this solves the circular prototype chains problem, the results are only correct for roughly 90% of the cases that I tried. An example fail would be ´instanceOf(new TypeError(), Error);´ gives false. And as much as I have tried, I can only conclude that the above examples will only work with access to the internal prototype i.e. ´Object.getPrototypeOf´ or ´__proto__´ – Xotic750 May 30 '13 at 12:34

1 Answers1

0

Based on the comments by @squint I will present the solution that I tried. I reworked the general shim for Object.getPrototypeOf that seems to be common among the libraries, based on a blog by John Resig as far as I can tell.

Cross-Browser Implementation

The obvious question now becomes: How do we begin using Object.getPrototypeOf today if most browsers don’t have it implemented yet? In the meantime we can use something like the following code for some form of compatibility:

if (typeof Object.getPrototypeOf !== "function") {
    if (typeof "test".__proto__ === "object") {
        Object.getPrototypeOf = function (object) {
            return object.__proto__;
        };
    } else {
        Object.getPrototypeOf = function (object) {
            // May break if the constructor has been tampered with
            return object.constructor.prototype;
        };
    }
}

While it’s not 100% spot-on (since the .constructor property is mutable on any object – it’s fully possible that it could’ve been manipulated by the user at some point) the above code should serve as a “good enough” solution to tide you over until browsers have good ECMAScript 3.1 compatibility.

And this is what I came up with

if (typeof Object.getPrototypeOf !== "function") {
    if (Object.prototype.__proto__ === null) {
        Object.getPrototypeOf = function getPrototypeOf(object) {
            return object[proto];
        };
    } else {
        Object.getPrototypeOf = function getPrototypeOf(object) {
            if (object === Object.prototype) {
                return null;
            }

            if (object === object.constructor.prototype) {
                return Object.prototype;
            }

            return object.constructor.prototype;
        };
    }
}

As I mentioned in my comments, this solves the circular prototype chain problem that I experienced with the John Resig version.

The problem that I do have with it (other than it may break if the constructor has been tampered with) is that during my tests I found that I get false result on one particular test (there may be more, but not that I have yet found). The test/s concern the Error objects

instanceof

new TypeError() instanceof Error; // --> true

function instanceOf with ES5 Object.getPrototypeOf or _proto_

instanceOf(new TypeError(), Error); // --> true

function instanceOf with object.constructor.prototype fallthrough

instanceOf(new TypeError(), Error); // --> false

All my other testing gave me matching results, but as I said, there may be more inconsistencies out there (where the constructor has not been tampered with). This is the best that I have managed to come up with, but at least I no longer have the possibility of infinite loops on functions similar to those given in my question.

Here is a jsfiddle that demonstrates the problem. (tested on Chromium v25, FireFox v20 and Opera 12.14: they are all that I have available)

Xotic750
  • 22,914
  • 8
  • 57
  • 79