Is there an equivalent of the __noSuchMethod__ feature for properties, or a way to implement it in JS?

Viewed 13319

There is a noSuchMethod feature in some javascript implementations (Rhino, SpiderMonkey)

proxy = {
    __noSuchMethod__: function(methodName, args){
        return "The " + methodName + " method isn't implemented yet. HINT: I accept cash and beer bribes" ;
    },

    realMethod: function(){
     return "implemented" ;   
    }
}

js> proxy.realMethod()
implemented
js> proxy.newIPod()
The newIPod method isn't implemented yet. HINT: I accept cash and beer bribes
js>

I was wondering, is there was a way to do something similar for properties? I'd like to write proxy classes that can dispatch on properties as well as methods.

6 Answers

You can use the Proxy class.

var myObj = {
    someAttr: 'foo'
};

var p = new Proxy(myObj, {
    get: function (target, propName) {
        // target is the first argument passed into new Proxy,
        // in this case target === myObj
        return 'myObj with someAttr:"' + target.someAttr 
               + '" had "' + propName 
               + '" called on it.';
    }
});
console.log(p.nonExsistantProperty);
// outputs: 
// myObj with someAttr:"foo" had "nonExsistantProperty" called on it

Although this is an old question I was looking into this today. I wanted to be able to seamlessly integrate code from another context, maybe a different web page or server.

Its the sort of thing that breaks in the long run, but I think its an interesting concept none the less. These things can be useful for mashing code together quickly, ( which then exists for years, buried somewhere ).

var mod = modproxy();

mod.callme.first.now('hello', 'world');

mod.hello.world.plot = 555;

var v = mod.peter.piper.lucky.john.valueOf;
console.log(v);

mod.hello.world = function(v) {
  alert(v);
  return 777;
};

var v = mod.hello.world('funky...');
console.log(v);

var v = mod.hello.world.plot.valueOf;
console.log(v);

mod.www.a(99);
mod.www.b(98);

function modproxy(__notfound__) {

  var mem = {};          
  return newproxy();

  function getter(target, name, receiver, lname) {

    if(name === 'valueOf') {
      lname=lname.slice(1);

      if(lname in mem) {
        var v = mem[lname];
        console.log(`rd : ${lname} - ${v}`);
        return v;
      }

      console.log(`rd (not found) : ${lname}`);
      return;
    }

    lname += '.'+name;
    return newproxy(() => {}, lname);

  } // getter

  function setter(obj, prop, newval, lname) {

    lname += '.' + prop;
    lname = lname.slice(1);
    console.log(`wt : ${lname} - ${newval}`);
    mem[lname] = newval;

  } // setter

  function applyer(target, thisArg, args, lname) {

    lname = lname.slice(1);

    if(lname in mem) {
      var v = mem[lname];

      if(typeof v === 'function') {
        console.log(`fn : ${lname} - [${args}]`);
        return v.apply(thisArg,args);
      }

      return v;

    }

    console.log(`fn (not found): ${lname} - [${args}]`);

  } // applyer

  function newproxy(target, lname) {

    target = target || {};
    lname = lname || '';

    return new Proxy(target, {
      get: (target, name, receiver) => {
        return getter(target, name, receiver, lname);
      },
      set: (target, name, newval) => {
        return setter(target, name, newval, lname);
      },
      apply: (target, thisArg, args) => {
        return applyer(target, thisArg, args, lname);
      }
    });

  } //proxy

} //modproxy

I implemented a valueOf step to read values, because it seems the property is 'get-ted' first.

I dont think its possible to tell at the time the property is 'get-ted' if its going to be invoked or read ( or required for further chaining ).

Its ok for single level properties. The property is either there or its not and either the required type or its not.

I'll work on it further, looking at promises for async/await routines, proxying existing objects and finer control of how properties are accessed when I am more familiar with how the code behaves and is best implemented.

I created a repository on GitHub: modproxy.js/README.md

CodePen: modproxy.js

My original question:

does javascript have an equivalent to the php magic class __call

Related