views:

161

answers:

2

How do you determine the difference between new Object and new fn under these conditions?

  • var fn = function(){};
  • fn.prototype = {};
  • You may not rely on __proto__ or Object.getPrototypeOf existing, as they are not in IE or Opera.
  • The solution may not statically use fn, such as instanceof fn.
  • Implementing your own Object.getPrototypeOf is fine.

Not required: It would be nice if your solution works with objects from other frames and doesn't use function serialization.

Here's some example base code to start off with:

var fn = function(){};
fn.prototype = {};

var x = new fn,
y = new Object;

fn = null; // prohibit static reference to fn

// define your detection function here
function isNewObject(obj) {

};

alert(isNewObject(x) !== isNewObject(y) ? "pass" : "fail");
+3  A: 

As far as I know your problem is impossible to solve with ES3: instanceof and isPrototypeOf() can't be used to solve it because the prototype object of fn is inaccessible, and only ES5 allows access to the internal [[Prototype]] property via Object.getPrototypeOf().

Btw, an even harder problem to solve would be if you assign Object.prototype to fn.prototype, eg

var x = (function() {
    function Foo() {}
    Foo.prototype = Object.prototype;
    return new Foo;
})();

As the prototype chain of Foo and Object instances is identical, there shouldn't be any way to distinguish them.

Christoph
+2  A: 

I'm pretty sure you can't do this in ES3 with only standard support, and here's why:

Look at x and y creation.

var fn = function(){};
fn.prototype = {};

var x = new fn,
    y = new Object;

When x is instantiated, its internal [[Prototype]] is being set to an object referenced by fn.prototype (just an Object object that you assigned to fn.prototype). Object's constructor - fn - is then being called in a context of that newly created object, but since it doesn't mutate ab object in any way, we can consider that step irrelevant.

When y is instantiated, its internal [[Prototype]] is being set to Object.prototype, which is an Object object too. Its constructor (Object) is then being called in a context of this newly created object as well, but nothing happens there either.

So now you end up with 2 Object objects which only differ in that their internal [[Prototype]]'s reference different objects - x's one references whatever you assigned to fn.prototype and y's references Object.prototype. Their internal [[Class]] properties are identical too (i.e. equal to "Object")

You can't get direct access to [[Prototype]] in ES3. You can infer something about it by using instanceof, but since fn is nulled in your example intanceof-based inference is out of the picture.

Now, you can augment Object constructor in such way that it would somehow mutate instantiated object. You can then inspect an object and detect this augmentation. For example:

Object = function() {
  return ({ __: void 0 });
}

and then:

function isNewObject(object) {
  return '__' in object;
}

but this will, of course, only work when object is being created with new Object and not via, say, an object literal - { }. It's also rather obtrusive :)

Perhaps there are other ways, but I can't see them right now.

HTH

kangax
Your solution would only work if someone doesn't explicitly define `__', but it's seems the only possible way (other than bruteforcing by iterating over properties and using !obj.hasOwnProperty(..) and !Object.prototype.hasOwnProperty(..))
Eli Grey
You can always change "__" to a more unique token, of course, but the problem would still persist. Any script would be able to spoof Object object by adding/deleting this unique token.
kangax