Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
playing around with an `Object.make()` helper
// `Object.make(..)` is a helper/wrapper for `Object.create(..)`. Both create a new
// object, and optionally link that new object's `[[Prototype]]` chain to another object.
//
// But `Object.make(..)` makes sure the new object always has a `__proto__` property
// (even a null one) and delegation to a `isPrototypeOf(..)` method, both of which are
// missing from the bare object (aka "Dictionary") created by `Object.create(null)`.
//
// `isPrototypeOf()` is put on a extra object that your created object can delegate to,
// if any only if you create an empty object (by not passing a `linkTo`) that otherwise
// wouldn't have access to `isPrototypeOf()`.
//
// `__proto__` is only imparted if you're in a non-`__proto__` engine (IE<=10, etc), OR
// if the object in question doesn't eventually delegate to the `Object.prototype` (and
// thus doesn't have access to the special `Object.prototype.__proto__`), like for instance
// a "Dictionary" created by `Object.create(null)` (whose `[[Prototype]]` is null).
//
// The particular reason for making sure at least a read-only (not settable) `__proto__`
// is always present (or delegatable to) is so that an effective/simple non-ES5 polyfill
// for `Object.getProtoypeOf()` can be made and used.
if (!Object.make) {
Object.make = function(linkTo) {
// if no `linkTo` provided, make a substitute delegate ancestor
// that only has `isPrototypeOf()` in it.
if (!linkTo) {
linkTo = Object.create(null);
linkTo.isPrototypeOf = Object.isPrototypeOf;
}
var obj = Object.create(linkTo);
// impart a `__proto__` property, if one doesn't exist (or can't be
// delegated to) already on the new object
if (!("__proto__" in obj) || Object.make.__proto_needed__) {
// can we ES5'ify a non-enumerable property for `__proto__`?
if (Object.defineProperty) {
Object.defineProperty(obj,"__proto__",{
enumerable: false,
writable: false,
value: linkTo
});
}
// oh well, just add it directly
else {
obj.__proto__ = linkTo;
}
}
return obj;
};
// FT for non __proto__ engines
Object.make.__proto_needed__ = !("__proto__" in {});
}
// non-ES5 polyfill for `Object.getPrototypeOf(..)`
// NOTE: relies on `__proto__` being present, which `Object.make(..)` above ensures
if (!Object.getPrototypeOf) {
Object.getPrototypeOf = function(obj) {
return obj.__proto__ || null;
};
}
// examples:
var Foo = Object.make();
Foo.me = "Foo";
Foo.identify = function() {
console.log("Me: " + this.me);
};
var Bar = Object.make(Foo);
Bar.me = "Bar";
var bar1 = Object.make(Bar);
bar1.me = "bar1";
var bar2 = Object.make(Bar);
bar2.me = "bar2";
Foo.identify(); // "Me: Foo"
Bar.identify(); // "Me: Bar"
bar1.identify(); // "Me: bar1"
bar2.identify(); // "Me: bar2"
Foo.isPrototypeOf(Bar); // true
Bar.isPrototypeOf(bar1); // true
Bar.isPrototypeOf(bar2); // true
Foo.isPrototypeOf(bar1); // true
Foo.isPrototypeOf(bar2); // true
Object.getPrototypeOf(Bar) === Foo; // true
Object.getPrototypeOf(bar1) === Bar; // true
Object.getPrototypeOf(bar2) === Bar; // true
Owner

getify commented Mar 26, 2013

A good question was asked: why is __proto__ being added here if not being used in this code example. I don't show it here, but I also prefer to have a non-ES5 polyfill for Object.getPrototypeOf(..).

if (!Object.getPrototypeOf) {
   Object.getPrototypeOf = function(obj) {
      return obj.__proto__ || null;
   };
}

edit: I've now added examples of __proto__ being useful for polyfill'ing Object.getPrototypeOf(..) and how that is useful in inspecting the [[Prototype]] link chain.

lsmith commented Apr 2, 2013

Shouldn't __proto__ be defined with Object.defineProperty? You don't want it enumerable, do you? And setting it should have side effects, no?

Owner

getify commented May 1, 2013

@lsmith I have updated the snippet to use a different technique for both __proto__ and isPrototypeOf(), after playing with it more.

However, I have not opted to try and actually polyfill the magical behavior (the set'ability) of __proto__ in this case. Others have created a set setter for it, I believe, but I think there are many caveats to it, not the least of which is you can't actually mimic that in browsers that don't already have __proto__, so making it seem like you support that when you can't is _worse_, IMHO, than just not supporting it outright.

Basically, I'm making sure __proto__ is added only so that there's an easy polyfill for Object.getPrototypeOf().

wilmoore commented May 7, 2013

...making sure proto is added only so that there's an easy polyfill for Object.getPrototypeOf().

For those that may happen across this and wonder what the __proto__ part of the conversation is about, see my original question, which asks:

Unless I am missing something simple, don’t we have a minor x-browser issue? The Foo object has no .constructor property and __proto__ is not available everywhere. This makes it difficult to properly polyfill Object.getPrototypeOf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment