Object.prototype.__noSuchMethod__

  • Revision slug: JavaScript/Reference/Global_Objects/Object/noSuchMethod
  • Revision title: __noSuchMethod__
  • Revision id: 72090
  • Created:
  • Creator: evilpie
  • Is current revision? No
  • Comment 10 words added

Revision Content

{{ Non-standard_header() }}

Summary

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

Method of Object
Implemented in JavaScript ?
ECMAScript Edition None

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. Using __noSuchMethod__, you could replace the functionality of the old errorize  by substituting 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, " +
                         "wittyProjectName.LOGTYPE_ERROR) instead.",
                         this.LOGTYPE_LOG);
    // just act as a wrapper for the newer log method
    args.push(this.LOGTYPE_ERROR);
    this.log.apply(this, args);
  }
}

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 errorize method to call log directly. The key difference between that approach and this one is that in this one, the errorize method will remain completely hidden on the wittyProjectName object. It will 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__", "log", "LOGTYPE_ERROR", and
// "LOGTYPE_LOG" (if the three lattermost ones have 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>
<table class="standard-table"> <thead> <tr> <th class="header" colspan="2">Method of <a href="/en/JavaScript/Reference/Global_Objects/Object" title="en/JavaScript/Reference/Global_Objects/Object"><code>Object</code></a></th> </tr> </thead> <tbody> <tr> <td>Implemented in</td> <td>JavaScript ?</td> </tr> <tr> <td>ECMAScript Edition</td> <td>None</td> </tr> </tbody>
</table><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. Using <code>__noSuchMethod__, y</code>ou could replace the functionality of the old <code>errorize</code>  by substituting 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, " +
                         "wittyProjectName.LOGTYPE_ERROR) instead.",
                         this.LOGTYPE_LOG);
    // just act as a wrapper for the newer log method
    args.push(this.LOGTYPE_ERROR);
    this.log.apply(this, args);
  }
}
</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 <code>errorize</code> method to call <code>log</code> directly. The key difference between that approach and this one is that in this one, the <code>errorize</code> method will remain completely hidden on the <code>wittyProjectName</code> object. It will not be enumerable using <a href="/en/Core_JavaScript_1.5_Reference/Statements/for...in" title="en/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a>, 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__", "log", "LOGTYPE_ERROR", and
// "LOGTYPE_LOG" (if the three lattermost ones have 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