Object.prototype.__noSuchMethod__

  • Revision slug: JavaScript/Reference/Global_Objects/Object/noSuchMethod
  • Revision title: __noSuchMethod__
  • Revision id: 72087
  • Created:
  • Creator: Sevenspade
  • Is current revision? No
  • Comment expand my note about a more interesting example, and explain how method "dispatching" through __noSuchMethod__ differs from the simpler, obvious approach; 197 words added, 38 words removed

Revision Content

{{ Non-standard_header() }}

Summary

Executes a function when a non-existent method is called on an object.

Syntax

obj.__noSuchMethod__ = fun

Parameters

fun
a function that takes the form
function (id, args) { . . . }
id
the name of the non-existent method that was called
args
an array of the arguments passed to the method

Description

By default, an attempt to call a method that doesn't exist on an object results in a TypeError being thrown. This behavior can be circumvented by defining a function at that object's __noSuchMethod__ member. The function takes two arguments, the first is the name of the method attempted and the second is an array of the arguments that were passed in the method call. The second argument is an actual array (that is, it inherits through the Array prototype chain) and not the array-like arguments object.

If this method cannot be called, either as if undefined by default, if deleted, or if manually set to a non-function, the JavaScript engine will revert to throwing TypeErrors.

Example

{{ note("If you have a better example, (like a shim that mediates method calls, to simulate vanilla prototypal inheritance, but with conditions that can affect the return type or auxiliary behavior of the method), please feel free to replace this one."); }}

Suppose you have a project which has deprecated the use of an errorize method , both because errorize is poorly-named and because your new log method handles the severity of the occurrence, such as an error versus a simple warning. You could replace the functionality of errorize using __noSuchMethod__ to substitute a call to log:

wittyProjectName.__noSuchMethod__ = function __noSuchMethod__ (id, args) {
  if (id == 'errorize') {
    wittyProjectName.log("wittyProjectName.errorize has been deprecated.\n" +
                         "Use wittyProjectName.log(message, 0) instead.", 1);
    wittyProjectName.log(args[0], 0); // log the message to the error log
  }
}

Do note, however, that this is merely a demonstration of __noSuchMethod__, and there are simpler ways to achieve the effect here, like changing the function body of the old log method to do everything inside the if statement. The key difference between that approach and this one is that in this one, the errorize method would remain completely hidden on the wittyProjectName object. It would not be enumerable using for...in, and any references to it besides those calling it as a function would get undefined, as if it's not there—because it really isn't:

wittyProjectName.errorize; // returns undefined
wittyProjectName.hasOwnProperty("errorize"); // returns false
"errorize" in wittyProjectName; // returns false
// this will print "__noSuchMethod__", and "log" (if log has been defined)
for (propname in wittyProjectName) {
  println(propname);
}

{{ languages( { "fr": "fr/R\u00e9f\u00e9rence_de_JavaScript_1.5_Core/Objets_globaux/Object/noSuchMethod", "ja": "ja/Core_JavaScript_1.5_Reference/Global_Objects/Object/noSuchMethod" } ) }}

Revision Source

<p>{{ Non-standard_header() }}</p>
<h2 name="Summary">Summary</h2>
<p>Executes a function when a non-existent method is called on an object.</p>
<h2 name="Syntax">Syntax</h2>
<p><code><em>obj</em>.__noSuchMethod__ = <em>fun</em></code></p>
<h2 name="Parameters">Parameters</h2>
<dl> <dt><code>fun</code></dt> <dd>a function that takes the form</dd> <dd><code>function (<em>id</em>, <em>args</em>) { . . . }</code> <dl> <dt><code>id</code></dt> <dd>the name of the non-existent method that was called</dd> <dt><code>args</code></dt> <dd>an array of the arguments passed to the method</dd> </dl> </dd>
</dl>
<h2 name="Description">Description</h2>
<p>By default, an attempt to call a method that doesn't exist on an object results in a <a href="/en/Core_JavaScript_1.5_Reference/Global_Objects/TypeError" title="en/Core_JavaScript_1.5_Reference/Global_Objects/TypeError">TypeError</a> being thrown. This behavior can be circumvented by defining a function at that object's <code>__noSuchMethod__</code> member. The function takes two arguments, the first is the name of the method attempted and the second is an array of the arguments that were passed in the method call. The second argument is an actual array (that is, it inherits through the <a href="/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/prototype" title="en/Core_JavaScript_1.5_Reference/Global_Objects/Array/prototype">Array prototype</a> chain) and not the array-like <a href="/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments" title="en/Core_JavaScript_1.5_Reference/Functions/arguments">arguments object</a>.</p>
<p>If this method cannot be called, either as if <code>undefined</code> by default, if deleted, or if manually set to a non-function, the JavaScript engine will revert to throwing <code>TypeError</code>s.</p>
<h2 name="Example">Example</h2>
<p>{{ note("If you have a better example, (like a shim that mediates method calls, to simulate vanilla prototypal inheritance, but with conditions that can affect the return type or auxiliary behavior of the method), please feel free to replace this one."); }}</p>
<p>Suppose you have a project which has deprecated the use of an <code>errorize</code> method , both because <code>errorize</code> is poorly-named and because your new <code>log</code> method handles the severity of the occurrence, such as an error versus a simple warning. You could replace the functionality of <code>errorize</code> using <code>__noSuchMethod__</code> to substitute a call to <code>log</code>:</p>
<pre class="eval">wittyProjectName.__noSuchMethod__ = function __noSuchMethod__ (id, args) {
  if (id == 'errorize') {
    wittyProjectName.log("wittyProjectName.errorize has been deprecated.\n" +
                         "Use wittyProjectName.log(message, 0) instead.", 1);
    wittyProjectName.log(args[0], 0); // log the message to the error log
  }
}
</pre>
<p>Do note, however, that this is merely a demonstration of <code>__noSuchMethod__</code>, and there are simpler ways to achieve the effect here, like changing the function body of the old log method to do everything inside the if statement. The key difference between that approach and this one is that in this one, the <code>errorize</code> method would remain completely hidden on the <code>wittyProjectName</code> object. It would not be enumerable using for...in, and any references to it besides those calling it as a function would get <code>undefined</code>, as if it's not there—because it really isn't:</p>
<pre class="eval">wittyProjectName.errorize; // returns undefined
wittyProjectName.hasOwnProperty("errorize"); // returns false
"errorize" in wittyProjectName; // returns false
// this will print "__noSuchMethod__", and "log" (if log has been defined)
for (propname in wittyProjectName) {
  println(propname);
}
</pre>
<p>{{ languages( { "fr": "fr/R\u00e9f\u00e9rence_de_JavaScript_1.5_Core/Objets_globaux/Object/noSuchMethod", "ja": "ja/Core_JavaScript_1.5_Reference/Global_Objects/Object/noSuchMethod" } ) }}</p>
Revert to this revision