Skip to content

Instantly share code, notes, and snippets.

@agostbiro
Last active August 29, 2015 14:24
Show Gist options
  • Save agostbiro/035b8aaf676f20ebd295 to your computer and use it in GitHub Desktop.
Save agostbiro/035b8aaf676f20ebd295 to your computer and use it in GitHub Desktop.
Composing a prototype chain in JavaScript
// A demonstration of composing a prototype chain from a list of objects by
// appending to the prototype chain of the first object, the rest of the
// objects.
'use strict';
var assign = Object.assign || require('object.assign');
// Non-enumerable properties are omitted. Property attributes are not respected
// and getters and setters aren't copied. Use 'Object.setPrototypeOf' if these
// are important.
function appendToProto(obj, proto)
{
if (Object.prototype.isPrototypeOf(obj) &&
!Object.prototype.isPrototypeOf(proto))
throw new Error(
'Object.prototype will be missing from the resulting proto chain.'
);
return (function inner(obj, proto)
{
if (obj === Object.prototype || obj === null)
return proto;
else
return assign(
Object.create(
inner(Object.getPrototypeOf(obj), proto)
),
obj
);
})(obj, proto);
}
// Takes a list of objects. The first object will be the farthest away from root
// (Object.prototype).
function composeProtoChain(prototypes)
{
return prototypes.reduce(appendToProto);
}
// Tests
function createA()
{
var obj = Object.create({
foo: function foo()
{
console.log('foo');
}
});
obj.a = 'a';
return obj;
}
function createB()
{
var obj = Object.create({
bar: function bar()
{
console.log('bar');
}
});
obj.b = 'b';
return obj;
}
function createC()
{
var obj = Object.create({
baz: function baz()
{
console.log('baz');
}
});
obj.c = 'c';
return obj;
}
function createBA()
{
return Object.create(composeProtoChain([createB(), createA()]));
}
function createCBA()
{
// Since 'createBA' returns an empty object, there will be an empty object
// in the prototype chain.
return Object.create(composeProtoChain([createC(), createBA()]));
}
function createCBAOverwrite()
{
var
a = createA(),
b = createB(),
c = createC();
a.c = 'this should have no effect';
b.foo = function foo()
{
console.log('a.foo overwritten by b');
}
c.a = 'a.a overwritten by c';
return Object.create(composeProtoChain([c, b, a]));
}
function createCBALong()
{
return Object.create(
composeProtoChain([
createC(),
createB(),
createA(),
createCBA(),
createCBAOverwrite()
])
);
}
function test(testee)
{
var tester = testee();
console.log(tester.a, tester.b, tester.c);
tester.foo && tester.foo();
tester.bar && tester.bar();
tester.baz && tester.baz();
console.log("\n");
}
test(createA);
/*
a undefined undefined
foo
*/
test(createB);
/*
undefined 'b' undefined
bar
*/
test(createC);
/*
undefined undefined 'c'
baz
*/
test(createBA);
/*
a b undefined
foo
bar
*/
test(createCBA);
/*
a b c
foo
bar
baz
*/
test(createCBAOverwrite);
/*
a.a overwritten by c b c
a.foo overwritten by b
bar
baz
*/
test(createCBALong);
/*
a b c
foo
bar
baz
*/
appendToProto({}, {});
appendToProto(Object.create(null), Object.create(null));
appendToProto(Object.create(null), {});
try
{
appendToProto({}, Object.create(null));
}
catch (e)
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment