Please, Give Us Back __noSuchMethod__ !

For those who don't know what __noSuchMethod__ is here the quick summary: it was a bloody handy non-standard method able to provide a fallback whenever we invoked an object method that did not exist.

var o = {};
o.__noSuchMethod__(function (name, args) {
alert(name); // "iDoNotExist"
alert([].slice.call(args)); // 1,2,3
});
o.iDoNotExist(1, 2, 3); // will produce above alerts


A Bit Of Background

Well, if you are patient enough, you may consider to read this never-ending post in Mozilla mailing list.
The reason that post is called Proxies: get+fn vs. invoke is because Proxy supposes to be the new way to go able to bring us much more power than we probably ever need ... but hey, this is welcome, while what is not welcome, is that Proxy may not be implemented first, which means browsers vendors should have waited to remove __noSuchMethod__ 'cause right now we may not have a pseudo equivalent, and second, but surely not less important, , Proxy does not provide the same functionality.

The Minified Theory Against The Practice

The main argument from @BrendanEich is that JavaScript has properties only so that o.fn() is the equivalent of o.fn.apply(o).
While this is true with any normal object, this is totally different with __noSuchMethod__.
The equivalent of __noSuchMethod__ for that operation, and behind the scene, is:
  • is there a property in o or its __proto__ called fn ?
    • yes, proceed as usual as if it was o.fn.call(o) and throw error if that property was not callable
    • no, is there a __noSuchMethod__ callback to burn instead of throwing an error due undefined property?
      • yes, perform the current operation: nsm_callback.apply(o, arguments)
      • no, throw an error since property was undefined and obviously not callable
Got it? The equivalent of o.fn() in an environment where __noSuchMethod__ was supported is potentially different from o.fn.apply(o) ... I am 100% sure Brendan knows this before and better than me and this is the reason I don't really get his strongest point.
Once again, o.fn() may be the equivalent of nsm_callback.apply(o, arguments) and not o.fn.apply(o).

The Inexistent Theory Against The Practice

If above "reason" was not enough, I have read even worst in the same thread. I am sorry guys, but sometimes you must be realistic and understand that if a developer does, as example, this:

// de-context fn from o and invoke it
(o.fn)();

// exact equivalent of
var fn = o.fn; // GETTER, no invokation
// and after ...
fn()
// this is a problem? we have same with missing bind then ...

rather than this:

// invoke fn through o as default context
o.fn();
// can we see the difference?

it means that developer has much bigger problems than __noSuchMethod__ inexistent ambiguity, that developer does not even know that __noSuchMethod__ exist ... come on!
Going on and on, another point is that get should be all we need to simulate the __noSuchMethod__ behavior through proxies ... but this is completely misleading!

A Getter IS A Getter

Is that trivial ... if we access a generic object property we are doing nothing different from invoking a getter with such object as property context.

o.whatever;
// look for "whatever" property name in o
// if found returns the "whatever" associated value


o.whatever.call
// nothing change, THIS IS NOT AN INVOKE
// look for "whatever" property name in o
// if found returns the "whatever" associated value
// since the value was a function, the call method is usable
// if whatever was not defined, the call method won't exist

Nobody should ever even consider to use property accessor and expect a __noSuchMethod__ behavior ... that property did not exist, what kind of method would you expect to look for?
call is a property of the Function.prototype so following the accessor/getter logic, nothing is ambiguous here.
Accordingly, lattest example is simply an inexistent mistake that hopefully no developer would ever do ... but you know, shit happens, then we learn, then hopefully we don't repeat same shit.

Other Programming Languages

When it comes to PHP, they perfectly managed to make the behavior not ambiguous through the __call magic keyword in classes definition but no, we decided that in JavaScript we cannot even think to put an invoke to make the life easier and completely NOT ambiguous for all of us ... do we?
I still cannot understand where and what is the ambiguous part if we have an explicit invoke declaration ... maybe something a bit harder to solve behind the scene for these poor JS engines? It could be ... should we all limit JS because of this? I don't think so.

Think About Libraries APIs Migrations

I give you the most basic example, the most used JS library with a fake getter and setter behavior: jQuery.

// jQuery simulation of getters and setters behaviors

// the getter
$("body").html(); // return string with content

// the setter
$("body").html("
whatever
");
// set the string with content

If you want, specially for chainability reasons through the simulated setter, the fact html is a method is convenient for the library but this library is stuck forever behind these two methods.
jQuery, at current ECMAScript status, will never be able to switch gradually to real getters and setters ... why that? A simple example:

$("body").html; // returns the string in jQuery 3000

$("body").html(); // shows a "deprecated warning"
// ... and returns the "html" getter

// implementation example
function setInnerHTML(node) {
node.innerHTML = this;
}
Object.defineProperty($.fn, "html", {
get: function () {
return this[0].innerHTML;
},
set: function (html) {
this.each(setInnerHTML, html);
}
});

// the deprecation warning
$.fn.__noSuchMethod__(function (property, args) {
if (property in this) {
console.log("Warning: " + property + " is not a method anymore");
if (args.length) {
// invoke the setter
this[property] = args[0];
// preserve behavior
return this;
} else {
// invoke the getter
return this[property];
}
} else {
throw "Y U NO READ DOCUMENTATION";
}
});

That's it, we can migrate from two different APIs implementing getters and setters whenever we had a similar behavior and bringing gracefully users to the new usage ... no wa can't!

It Is Not About jQuery

I don't even use jQuery so don't get me wrong, this is not my battle here ... the point is that for another private project I am working on I would like to educate developers to use properties correctly but I understand developers may already got use to invoke methods as if it is normal, even when they are simply looking for a getter behavior.

var o = {
whatever:"cool bro",
__noSuchMethod__: function (property, args) {
console.log("we got a bro-blem here, " +
"don't invoke if you want a getter");
return this[property];
}
};

// so that
o.whatever === o.whatever();

Secially last line of code is apparently impossible to reproduce with Proxies, those that suppose to be the new and best way to go, those that give us control on things rarely needed until now, those that made JS.Next group decide that __noSuchMethod__ was evil and it had to be abandoned.
I really hope that JS.Next will not be non-developer expectations behaviors driven because guys, somebody tries to do cool things with this cool language, and if the reason you drop something is because we are all morons, as example misunderstanding the difference of a referenced property through parenthesis ... oh well ... good luck kkfuture JavaScript ...

How To Solve This

Please put an invoke or even better an invokeProperty, preserving invoke for when the object itself is used as if it was callable, in the current Proxy specifications so that who knows what is doing, can keep doing it and who never even bothered with this stuff, won't be affected at all.
Thank you for listening.

Comments

Popular posts from this blog

8 Things you should not be afraid of as a Developer

News

Why REST is so important