11

Hi is there a way to overload the '.'(dot) and [] operator in javascript. ie if I say obj.Name or obj['Name'] it should call a common method in the obj class by passing Name as argument. The similar kind of functionality available in python using property method. But here I want the ".Name" to be passed as argument to the common method.

like this..

function Data(){
    this.getValue(name){
        return '...'
    }
}

data = new Data()
name = data.Name 
name = data['Name']
//both should call data.getValue()
1
  • FYI, things have changed since the question was asked. I've updated my answer. Commented Nov 11, 2013 at 16:09

5 Answers 5

17

You can do this with Proxy objects (MDN | spec). Proxies let you create objects that are true proxies (facades) for other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval:

const original = { "foo": "bar" };
const proxy = new Proxy(original, {
    get(target, name, receiver) {
        let rv = target[name];
        if (typeof rv === "string") {
            rv = rv.toUpperCase();
        }
        return rv;
    }
});

console.log(`original.foo = ${original.foo}`); // "bar"
console.log(`proxy.foo = ${proxy.foo}`);       // "BAR"

The output of the above is:

original.foo = bar
proxy.foo = BAR

Operations you don't override have their default behavior. In the above, all we override is get, but there's a whole list of operations you can hook into (getting a property, setting a property, looking up the list of existing properties, and others).

In the get handler function's arguments list:

  • target is the object being proxied (original, in our case).
  • name is (of course) the name of the property being retrieved.
  • receiver is either the proxy itself or something that inherits from it. In our case, receiver is === proxy, but if proxy were used as a prototype, receiver could be a descendant object, hence it being on the function signature (but at the end, so you can readily leave it off if, as with our example above, you don't actually use it).

This lets you create an object with the catch-all getter and setter feature you want:

const obj = new Proxy({}, {
    get(target, name) {
        if (!(name in target)) {
            console.log(`Getting non-existent property "${name}"`);
            return undefined;
        }
        return target[name];
    },
    set(target, name, value) {
        if (!(name in target)) {
            console.log(`Setting non-existent property '${name}', initial value: ${value}`);
        }
        target[name] = value;
    }
});

console.log("[before] obj.foo = " + obj.foo);
obj.foo = "bar";
console.log("[after] obj.foo = " + obj.foo);

The output of the above is:

Getting non-existent property 'foo'
[before] obj.foo = undefined
Setting non-existent property 'foo', initial value: bar
[after] obj.foo = bar

Note how we get the "non-existent" message when we try to retrieve foo when it doesn't yet exist, and again when we create it, but not subsequently.

Sign up to request clarification or add additional context in comments.

3 Comments

@wootscootinboogie: Thanks. FYI, I noticed the answer was out of date and have just updated it.
This guy. And his updates. I like this guy and his updates.
thank you very much @T.J.Crowder. your answer is helping me invoking methods in a worker without actually defining these methods in the client.
2

On recent JS versions, there is indeed something similar to Python's properties.

Also see question Javascript getters and setters for dummies?

It may be enough for you, although Python is still way more flexible and compact.

4 Comments

These aren't the same, they're getters and setters for a property, they don't operate on any property.
+1, but it's very implementation-specific, and it's been outdated by the ECMAScript5 specification, which allows doing that but in a completely different way. So support for that syntax (which was awful) never really got widespread, and now it certainly won't. But still, for use in a couple of browsers, it's a possibility.
@Nick: True, but the author could then defer to the central getValue function. But only for defined properties, no nice catch-all for undefined ones.
indeed. the question said "using property method" but it should probably be "using __ getattr __ method"
1

Nope, you can't overload or replace it in JavaScript. I wish I had a more comprehensive/informational answer for you...but it just isn't possible, I guess there was never a need seen to add the ability.

1 Comment

@T.J. - trust me, a lot more of my time will be taken up very soon ;)
0

As others have stated, this is impossible as operator overloading is impossible in ECMAscript.

If you really need to do this then I would use some preprocessing to convert the parts of code (and only those that really needs to be instrumented like this) into instrumented code.

This could easily be done using a little regexp like this

var a= "var a = b.c['foo']" // get this via xhr or something
var re = /(\[(.*)\])|(\.([\w\d]+)\b)/g;

var b = a.replace(re, function(m){
    return ".get('" +  m.replace(/[[\].'\"]/g,"") + "')";
});
b; // "var a = b.get('c').get('foo')"

eval(b);

Comments

0

You may invoke methods that don't exist in the original target object. I use this functionality to 'invoke' methods on a local object, that get actually posted to a worker, and to invoke methods on an object in the worker, to be actually posted to it's parent.

class Original {
  aa = (a, b) => {
    console.log(`aa invoked with ${a}, ${b}`);
  };
  defaultMethod = (name, ...args) => {
    console.log(
      `defaultMethod invoked as ${name} with ${
      args.map(JSON.stringify).join(', ')}`);
  };
};
let orig = new Original();
let proxy = new Proxy(orig, {
  get: (target, name) => {
    if (!(name in target)) {
        target[name] = (...args) => {
            target.defaultMethod(name, ...args);
        };
    }
    return target[name];
  }
});
proxy.aa(1, 2);
proxy.bb(1, 2, 3);

This answer is working now in 2024, and I didn't try to implement it for older browsers.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.