Is there a way to catch an attempt to access a non existant property or method?

For instance this code:

function stuff() {
  this.onlyMethod = function () {
    return something;
  }
}

// some error is thrown
stuff().nonExistant();

Is there a way to do something like PHP's __call as a fallback from inside the object?

function stuff() {
  this.onlyMethod = function () {
    return something;
  }
  // "catcher" function
  this.__call__ = function (name, params) {
    alert(name + " can't be called.");
  }
}

// would then raise the alert "nonExistant can't be called".
stuff().nonExistant();

Maybe I'll explain a bit more what I'm doing.

The object contains another object, which has methods that should be accessible directly through this object. But those methods are different for each object, so I can't just route them, i need to be able to call them dynamically.

I know I could just make the object inside it a property of the main object stuff.obj.existant(), but I'm just wondering if I could avoid it, since the main object is sort of a wrapper that just adds some functionality temporarily (and makes it easier to access the object at the same time).

Answers:

Answer

There is a way to define a generic handler for calls on non-existant methods, but it is non-standard. Checkout the noSuchMethod for Firefox. Will let you route calls to undefined methods dynamically. Seems like v8 is also getting support for it.

To use it, define this method on any object:

var a = {};

a.__noSuchMethod__ = function(name, args) {
    console.log("method %s does not exist", name);
};

a.doSomething(); // logs "method doSomething does not exist"

However, if you want a cross-browser method, then simple try-catch blocks if the way to go:

try {
    a.doSomething();
}
catch(e) {
    // do something
}

If you don't want to write try-catch throughout the code, then you could add a wrapper to the main object through which all function calls are routed.

function main() {
    this.call = function(name, args) {
        if(this[name] && typeof this[name] == 'function') {
            this[name].call(args);
        }
        else {
            // handle non-existant method
        }
    },
    this.a = function() {
        alert("a");
    }
}

var object = new main();
object.call('a') // alerts "a"
object.call('garbage') // goes into error-handling code
Answer

Well, it seems that with harmony (ES6), there will be a way, and it's more complicated compared to the way other programing languages do it. Basically, it involves using the Proxy built-in object to create a wrapper on the object, and modify the way default behavior its implemented on it:

obj  = new Proxy({}, 
        { get : function(target, prop) 
            { 
                if(target[prop] === undefined) 
                    return function()  {
                        console.log('an otherwise undefined function!!');
                    };
                else 
                    return target[prop];
            }
        });
obj.f()        ///'an otherwise undefined function!!'
obj.l = function() {console.log(45);};
obj.l();       ///45

The Proxy will forward all methods not handled by handlers into the normal object. So it will be like if it wasn't there, and from proxy you can modify the target. There are also more handlers, even some to modify the prototype getting, and setters for any property access yes!.

As you would imagine, this isn't supported in all browsers right now, but in Firefox you can play with the Proxy interface quite easy, just go to the MDN docs

It would make me happier if the managed to add some syntactic sugar on this, but anyway, its nice to have this kind of power in an already powerful language. Have a nice day! :)

PD: I didn't copy rosettacode js entry, I updated it.

Answer

It seems that you know your way around JS. Unfortunately, I don't know of such feature in the language, and am pretty sure that it does not exist. Your best option, in my opinion is either using a uniform interface and extend it, or extend the prototypes from which your objects inherit (then you can use instanceof before going forward with the method call) or use the somewhat cumbersome '&&' operator in order to avoid the access of nonexistent properties/methods:

obj.methodName && obj.methodName(art1,arg2,...);

You can also extend the Object prototype with Anurag's suggestion ('call').

Answer

You can also check if the method exists.

if(a['your_method_that_doesnt_exist']===undefined){
//method doesn't exist
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.