6

Probably not the correct word. But I want to create a new type in JavaScript. It would have the simple property that one could do this:

var inst = new SomeType();
inst.key1.key2 = 'something';
inst.key1.key1.key3 = 'something';

Basically you wouldn't have to declare an object literal to extend further. It would create one automatically.

This would allow me to build complex structures without having to worry about checking for the existence of a property to extend off of.

Instead of doing

inst.key1 = {};

inst.key1.key2 = 'data';

one could just do

inst.key1.key2 = 'data';

and the

inst.key1 = {};

would be automatic, i.e. would happen internally.

This does have a practical purpose. Particularly I have a registry pattern which I would use this new type to organize data using a more hierarchical approach.

Also, I see a pattern, common in libraries, that tests for the existence of an object literal and then creates one if it does not exist.

This is a common idiom it seems.

Shamim Hafiz - MSFT
  • 21,454
  • 43
  • 116
  • 176
  • 2
    FYI, this is known as autovivification in Perl. –  Jun 19 '13 at 17:23
  • 3
    It's the same number of syllables as your username :P – Paul Jun 19 '13 at 17:25
  • 2
    That's not possible (yet). You can't listen to reads of all possible properties. Not even with getters. – John Dvorak Jun 19 '13 at 17:25
  • Not possible without using some kind of method. However, I can imagine Coffeescript's existential operator being extended to allow such capability. (Come to think of it, that's a pretty good idea...) – GJK Jun 19 '13 at 17:28
  • I posted a super ugly answer using `._` ... below. –  Jun 19 '13 at 21:01
  • You changed your name, thereby rendering my comment obsolete :( – Paul Jun 20 '13 at 20:31

2 Answers2

7

What you'd like to get can easily be achieved with Harmony proxies (MDN). However, this API is not yet stable, so it's fine to use it in non-critical hobby code, but don't use it in production code (yet).

Here's an extremely simple example:

function getFreeChain(object) {
    var handler = {
        get: function(target, name) {
            if (name in target)
                return target[name];
            var newTarget = target[name] = {};
            return new Proxy(newTarget, handler);
        }
    };
    return new Proxy(object, handler);
}

// Usage:
var obj = {};
var magicalObj = getFreeChain(obj);
magicalObj.a.b.c.d.e.g = 1;
console.log(magicalObj.a.b.c.d.e.g); // 1
console.log(obj.a.b.c.d.e.g);        // 1
obj.x.y.z = 1;                       // TypeError: obj.x is undefined

Note: My example is an implementation of the latest Proxy specification, which is only supported by Firefox . Chrome only supports an old, significantly different version of the API, which can be enabled by turning on "Experimental JavaScript" at chrome://flags/. This old API is ugly, and implementing the previous would require significantly more lines of code, so I'll leave this as an exercise to the reader.

Oh, there's a library called DirectProxies.js (superseded by harmony-reflect) which brings the simple Proxy API to Chrome. After including the this library, the previous code will work in Firefox and Chrome (with experiments enabled): http://jsfiddle.net/PAhYL/

Rob W
  • 341,306
  • 83
  • 791
  • 678
0

Because javascript does not support operator overloading, one could create a really ugly fake operator called ._ as such. If someone wants to implement ._ please edit answer.

http://jsfiddle.net/586Sc/

var Obj = function () {
    this.hold = {};
};

Obj.prototype._ = function (key) {
    //implement here;
    return this;
};

var obj = new Obj();

obj._('key1')._('key2')._('key3') = 'barbarella';
console.log(test);