As an exercise, I wrote a simple function that composes a prototype chain from a list of objects by appending to the prototype chain of the first object, the rest of the objects. This is the code (example usage and tests in this gist):
var assign = Object.assign || require('object.assign');
// Non-enumerable properties are omitted. Property attributes are not respected
// and getters and setters aren't copied.
function appendToProto(obj, proto)
{
if (obj === null)
throw new Error(
'Prototype chains are assumed to be delimited by Object.prototype.'
);
else if (obj === Object.prototype)
return proto;
else
return assign(
Object.create(
appendToProto(Object.getPrototypeOf(obj), proto)
),
obj
);
}
// Takes a list of objects. The first object will be the farthest away from root
// (Object.prototype).
function composeProtoChain(prototypes)
{
return prototypes.reduce(appendToProto);
}
As noted above, I can identify three issues with this code:
- Non-enumerable properties are omitted from the prototype chain.
- Property attributes are not respected and getters and setters aren't copied.
- The assumption that
Object.prototype
is the last object beforenull
in the prototype chain of objects.
I don't think there is a solution for the first issue, but workarounds could be implemented for the second and third ones. I preferred to keep things simple here, so I avoided doing that.
(Exceeding the maximum recursion depth in appendToProto
shouldn't be a problem.)
I have two questions:
- Is throwing an error in
appendToProto
the right way to enforce the assumption from the third issue? - Are there any other caveats that I haven't considered?