11

I'm looking for a way to handle calls to undefined methods and properties in JavaScript.

These would be similar to the PHP magic methods __call, __callStatic, __get.

An example of the code using this might be:

var myObject = {};
myObject.__call = function (called, args) {
    alert(called);
    alert(args);
    return(true);
}

myObject.meow("kitty", "miau");

This would result in the first alert dialog displaying "meow" and the second to display "kitty, miau".

Alexander Trauzzi
  • 7,277
  • 13
  • 68
  • 112

5 Answers5

5

Proxy can do it! Proxy can do EVERYTHING! An answer is given here: Is there a javascript equivalent of python's __getattr__ method? . To rephrase in my own words:

var myObject = new Proxy({},{get(target,name) {
  return function() {
    alert(name)
    console.log(arguments) // alerts are bleh
    return true
  }
}})

myObject.meow("kitty", "miau") // alerts "meow", logs ["kitty","miau"] to the console, and returns true

Check out the MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

Works in chrome, firefox, and node.js. Downsides: doesn't work in IE - freakin IE. Soon.

Community
  • 1
  • 1
B T
  • 57,525
  • 34
  • 189
  • 207
0

If you just want something like PHP's features, read the following solution I came up with. I'm assuming that you'll be putting this functionality on your own objects. Well, as long as they're not functions, you can have the following convention for convenience, and it should work on all browsers:

instead of myobj.foo or myobj['foo'], just use myobj('foo'), and make your object callable when you define it. But you'll have to avoid the use of "new" because "new" can never return a function in Javascript.

var NewFlexible = function() {
  var custom = {};
  return function(prop) {
    if (!(prop in custom)) custom.prop = null;
    return custom.prop;
  };
};

And then you can create objects like so:

myObj = NewFlexible();

Using something similar to the Douglas Crockford pattern, you could create "classes" that extend this:

var NewDerived = function(options) {
  var result = {};
  NewFlexible.apply(result, arguments); // parent constructor
  // go on to do what you have to do
  return result;
};

Or, to forget about constructors, you can just make a wrapper around objects:

var MakeFlexible = function (obj) { 
 return function(prop) { 
   if ('prop' in obj) return obj.prop; 
   obj.prop = null; return obj.prop;
 };
}

You'll just have to publish this documentation for all users of your code. It's actually good to expose your functionality through this convention because you don't want to freak people out by using nonstandard javascript.

Magarshak
  • 151
  • 1
  • 4
  • But this *is* basically nonstandard Javascript. This is fine for quickie projects, but I wouldn't recommend starting a large code base using this pattern. – Ben Zotto Feb 26 '10 at 21:51
  • Just trying to be helpful. It clearly seemed the guy wanted similar functionality to PHP for some reason, i.e. a convenient notation for accessing properties that weren't there. So the easiest thing I could think of is to have objects that support foo('bar') instead of foo['bar'], which is pretty close to what the guy wanted, without using nonstandard javascript properties. This is using quite standard Javascript. It will work on every browser. The fact that it might be an unusual desire is not my fault -- it's the closest you can get (IMHO) to answering the question while using correct JS. – Magarshak Feb 28 '10 at 16:13
-1

There is a magic function in Javascript called __noSuchMethod__. Here's an example:

var foo = { __noSuchMethod__ : function(name,params) { alert('invalid function call'); } }
foo.bar();

EDIT: As @jldupont mentioned, this is actually only in Rhino and SpiderMonkey (Mozilla's JS engine); it is not supported in the ECMAScript standard. There are some requests that it be in ECMAScript 4.

Alex Beardsley
  • 20,988
  • 15
  • 52
  • 67
  • not even on V8: http://code.google.com/p/v8/issues/detail?id=264 – jldupont Feb 26 '10 at 20:05
  • OP never mentioned that as a requirement; for all i know he could be using Rhino. just saying, this is one way to do it. – Alex Beardsley Feb 26 '10 at 20:06
  • Ideally it would be a standard approach, yes. At the very least, webkit and mozilla browsers would be able to do it. – Alexander Trauzzi Feb 26 '10 at 20:14
  • 1
    This is the best answer I will be getting for now. Thank you for at least drawing my attention to the method name. I encourage readers to comment on and vote for the following issue: http://code.google.com/p/v8/issues/detail?id=264 It may be closed, but voicing support is the only way to raise awareness. – Alexander Trauzzi Feb 26 '10 at 20:36
  • @Omega there is no reason to raise awareness of an obsolete solution when this is precisely one of the use cases that proxies can solve. You should unaccept this incorrect answer, which does nothing more than describe a historical curiosity, and accept one which uses proxies. –  Mar 20 '16 at 10:00
-1

I should add for people still looking for a solution, there is this:

var myObject = {};
myObject['dynamicMethod'] = new function (parameters) {
    alert(parameters);
};

The only difference here is that you may have to iterate over what you intend on adding. This is pre-generating the methods, instead of simply dynamically handling them.

Alexander Trauzzi
  • 7,277
  • 13
  • 68
  • 112
-1

If you are looking specifically for a method, you will have to wait until ES7, because it looks they arent going to include it that way in harmony, anyway, there is a currently working-standard functionality on this, trought the built-in object Proxy, that is added in ES6, i wrote an answer, in this question, that takes the problem in a wider manner. Basically involves wrapping the object into a proxy, an loading a handler

get: function(obj, propname) {custom handling}

into the proxy trought it´s constructor, to handle, filter or implement all requests of properties into it.

And it adds even more functionality.... For the complete answer, go into the link.

Community
  • 1
  • 1
Nicolas NZ
  • 368
  • 3
  • 5