Update for 2013 and 2015 (see initial response from 2011 below):
This has changed as an ES2015 specification (aka ES6): JavaScript now has a proxy . Proxies allow you to create objects that are true proxy servers for (facades) of other objects. Here is a simple example that turns any property values that are strings for all caps upon retrieval:
var original = { "foo": "bar" }; var proxy = new Proxy(original, { get: function(target, name, receiver) { var rv = target[name]; if (typeof rv === "string") { rv = rv.toUpperCase(); } return rv; } }); console.log("original.foo = " + original.foo);
"use strict"; (function() { if (typeof Proxy == "undefined") { console.log("This browser doesn't support Proxy"); return; } var original = { "foo": "bar" }; var proxy = new Proxy(original, { get: function(target, name, receiver) { var rv = target[name]; if (typeof rv === "string") { rv = rv.toUpperCase(); } return rv; } }); console.log("original.foo = " + original.foo);
Operations that you do not override have default behavior. In the above example, we all redefine get , but there is a whole list of operations that you can connect to.
In the argument list of the get handler function:
target is an object that is proxied ( original , in our case).name is (of course) the name of the returned property.receiver - either the proxy itself, or something that inherits from it. In our case, receiver is === proxy , but if proxy used as a prototype, receiver could be a descendant, therefore, it was on the function signature (but at the end, so you can easily refuse it if, as in our example above, you are not actually using it).
This allows you to create an object with the required getter and setter function:
var obj = new Proxy({}, { get: function(target, name) { if (!(name in target)) { console.log("Getting non-existant property '" + name + "'"); return undefined; } return target[name]; }, set: function(target, name, value) { if (!(name in target)) { console.log("Setting non-existant property '" + name + "', initial value: " + value); } target[name] = value; return true; } }); console.log("[before] obj.foo = " + obj.foo); obj.foo = "bar"; console.log("[after] obj.foo = " + obj.foo);
"use strict"; (function() { if (typeof Proxy == "undefined") { console.log("This browser doesn't support Proxy"); return; } var obj = new Proxy({}, { get: function(target, name) { if (!(name in target)) { console.log("Getting non-existant property '" + name + "'"); return undefined; } return target[name]; }, set: function(target, name, value) { if (!(name in target)) { console.log("Setting non-existant property '" + name + "', initial value: " + value); } target[name] = value; return true; } }); console.log("[before] obj.foo = " + obj.foo); obj.foo = "bar"; console.log("[after] obj.foo = " + obj.foo); })();
(Notice how I left the receiver functions, since we are not using it. receiver is an optional fourth argument to arg on set .)
The conclusion above:
Getting non-existant property 'foo'
[before] obj.foo = undefined
Setting non-existant property 'foo', initial value: bar
[after] obj.foo = bar
Pay attention to how we get the message "nonexistent" when we try to extract foo when it does not exist yet, and again when we create it, but not afterwards.
Reply from 2011 (see above for updates of 2013 and 2015):
No, JavaScript does not have full access. The accessor syntax used is discussed in Section 11.1.5 of the specification and does not offer any template or something like that.
You could, of course, implement a function for this, but I assume that you probably do not want to use f = obj.prop("foo"); , not f = obj.foo; and obj.prop("foo", value); , not obj.foo = value; (which would be necessary for a function to handle unknown properties).
FWIW, the getter function (I was not worried about the setter logic) would look something like this:
MyObject.prototype.prop = function(propName) { if (propName in this) { // This object or its prototype already has this property, // return the existing value. return this[propName]; } // ...Catch-all, deal with undefined property here... };
But then again, I can’t imagine that you really want to do this, because of how it changes the way you use the object.