JavaScript hashes that inherit from a parent

For a JavaScript program I’m working on, I want to have hashes that can inherit values from a parent. Specifically, I have many widgets, each with a (usually small) hash of configuration parameters; when I access such a parameter and it’s not defined in the widget I want to get the value from the configuration of the team that the widget belongs to; and when that’s not defined I want it to get the value from the global configuration.

sushi-by-rob-owen-wahl-small

So I want this code:

var global_config = { foo: 99, bar: 99, baz: 3 };

var team_config = objectWithParent(global_config);
team_config.foo = 1;

var widget_config = objectWithParent(team_config);
widget_config.bar = 2;
widget_config.quux = 4;
team_config.thrick = 5;

print_all("global", global_config);
print_all("  team", team_config);
print_all("widget", widget_config);

to print the following:

global: foo=99, bar=99, baz=3, quux=undefined, thrick=undefined
  team: foo=1, bar=99, baz=3, quux=undefined, thrick=5
widget: foo=1, bar=2, baz=3, quux=4, thrick=5

You’d think that would be easy: just have the objectWithParent function make an empty hash, set its prototype to the value of the argument, and return that hash:

// WARNING: DOES NOT WORK
function objectWithParent(parent) {
    var res = {}
    res.prototype = null;
    return res;
}

But no. JavaScript’s notion of prototype is bizarre, complex and counter-intuitive — probably as a result of a misguided attempt to make its object-prototyping system look more like a class-based one. It turns out that you have to go around the houses, create the new object as a the result of invoking a function whose prototype is the parent. Here’s the code:

function objectWithParent(parent) {
    function thing() {}
    thing.prototype = parent;
    var res = new thing();
    thing.prototype = null;
    return res;
}

I’m not proud of this code — I don’t like or trust the temporary assignment of thing.prototype. But it seems to work, so I’m leaving it here for anyone else who finds it useful.

Feel free to chip in with improvements!

Update (a few hours later)

As Michael Williamson points out, the function I want is already included in modern JavaScript runtimes. It’s called Object.create. For more on this approach to inheritance, see Doug Crockford’s short peice from 2006, Prototypal Inheritance in JavaScript.

About these ads

9 responses to “JavaScript hashes that inherit from a parent

  1. Hi Mike :)

    First of all, in JS, similarly to traditional OO languages like Java, the prototype chain of an object cannot be modified after object’s construction. It’s hidden and protected by the runtime (hacks aside).

    Because of that, plain JS objects (typeof x == “object”) do not have “prototype” property. You can obviously set it to anything you like, but it’s no different from any other property, say “mikewashere”. It’s meaningless.

    Now, since the object prototype chain is protected, it can only be altered during the construction phase. In other words, the prototype chain can only be set when you use the “new” keyword.

    To construct an object with “new” you need a ‘constructor’, a plain JS function (which is also an object but of a special kind: typeof y == “function”). Before you invoke “new” on that function though, you can ask it to set the otherwise inaccessible prototype link (let’s call it __proto__ property) to something you like:

    var parent = {name: “parent”} //some plain JS object
    function Cloner() {} //constructor
    Cloner.prototype = parent; //ask the constructor to link constructed objects with ‘parent”
    var child = new Cloner(); //now child is linked to parent

    child.name //”parent”
    child.name = “child”
    child.name //”child”
    delete child.name
    child.name //parent

    Hope that helps.

  2. If I’ve understood correctly, then it seems like what you actually want is Object.create:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

    It’s supported by modern runtimes, and that page also includes a polyfill that should work for your case.

  3. … proving once again the great truism:

    “The best way to get information on Usenet isn’t to ask a question, but to post the wrong information” — aahz@netcom.com

    Thanks, Michael. I’ll modify my program to use Object.create drectly, and dump my objectWithParent workaround. And I’ll add an update to the post.

  4. Object#create does exactly the same as the “workaround” you provided, Mike. Take a look at the polyfill on that page.

  5. @Mike Taylor:
    I think part of the reason for that truism is that posting (wrong) information shows everyone else that you’re trying, and it gives them a sense of how much you already understand (or not) about the problem.

    By sharp contrast the question:
    “How do I get object inheritance to work in JavaScript?” *

    Would probably be harder to answer–even more so for anyone that didn’t already know how much *you* know or don’t know about JavaScript.

    * I realize the question I made up is subtly different from the problem you needed an answer to. But that’s just for the sake of creating a bad example on purpose.

  6. Thanks, Jakub, it’s what I’m using already — see Michael Williamson’s comment. It’s a shame that the second argument to Object.create doesn’t work sanely across platforms, but not the end of the world.

    Wyrdwyrd, I like your charitable interpretation of why posting a suboptimal solution is a good way to get the right answer. But I fear in reality this is really just a corollary of SIWOTI syndrome.

  7. I recommend the book Javascript: The Good Parts (2008) by Douglas Crockford, as a good source of clarification of, and strategies against, the weirdnesses of Javascript. For instance, he explains how the prototype is attached to the constructor function, and he defines Object.create() as a function.

    That said, this thread gave some good information that wasn’t in that book. Thanks everyone!

    I guess you mean wyrdwyrd’s explanation is “charitable” in the sense that it’s a little optimistic to believe that that dynamic will work all the time. But, it’s not something wrong on the internet. It’s the problem of anyone with a question to figure out how to make it easy for others to answer (which you did).

  8. it’s new to me that properties/members/keys of objects/hashmaps would be called “hashes”, even in javascript.

  9. I don’t think the properties/members/keys of objects are ever called hashes. The objects themselves might be when, as in this case, their only role is to hold a key->value mapping. The terminology is from Perl, where of course it’s taken from “hash table” which is a particular implementation of such a mapping.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s