Object.prototype.__noSuchMethod__

Не стандартно
Эта возможность не является стандартной и стандартизировать её пока никто не собирается. Не используйте её на сайтах, смотрящих во внешний мир: она будет работать не у всех пользователей. Также могут присутствовать большие несовместимости между реализациями и её поведение может в будущем измениться.

Хотя свойство __noSuchMethod__ и нестандартно, спецификация ECMAScript Harmony (ES6) содержит объект Proxy, с помощью которого вы можете сделать всё тоже самое, что и при использовании этого свойством (и даже больше).

Сводка

Свойство __noSuchMethod__ ссылается на функцию, выполняющуюся каждый раз при вызове на объекте несуществующего метода.

Синтаксис

obj.__noSuchMethod__ = fun

Параметры

fun
Функция, имеющая вид
function(id, args) { . . . }
id
Имя вызванного несуществующего метода
args
Массив аргументов, переданный в метод

Описание

По умолчанию, при попытке вызвать не существующий в объекте метод, будет выброшено исключение TypeError. Это поведение можно обойти, определив функцию __noSuchMethod__ в качестве члена объекта. Функция принимает два аргумента, первый является именем метода, который попытались вызвать, а второй — массивом аргументов, которые были переданы в метод при его вызове. Второй аргумент является настойщим массивом (то есть, он наследуется через цепочку прототипов от Array.prototype), а не массивоподобным объектом arguments.

Если данный метод не может быть вызван, либо по причине того, что он установлен в undefined по умолчанию, либо удалён, либо вручную установлен в не-функцию, движок JavaScript вернётся к выбрасыванию исключения TypeError.

Примеры

Пример: простая проверка свойства __noSuchMethod__

var o = {
    __noSuchMethod__: function(id, args) { console.log(id, '(' + args.join(', ') + ')'); }
};

o.foo(1, 2, 3);
o.bar(4, 5);
o.baz();

// Вывод
// foo (1, 2, 3)
// bar (4, 5)
// baz ()

Пример: использование свойства __noSuchMethod__ для симуляции множественного наследования

Ниже показан пример кода, реализующего примитивную форму множественного наследования.

// Не работает с множественным наследованием объектов в качестве родителей
function noMethod(name, args) {
    var parents = this.__parents_;

    // Пройдёмся по всем родителям
    for (var i = 0; i < parents.length; i++) {
        // Если нашли функцию в родителе, вызовем её
        if (typeof parents[i][name] == 'function') {
            return parents[i][name].apply(this, args);
        }
    }

    // Если мы здесь, метод не был найден
    throw new TypeError;
}

// Используется для добавления родителя при множественном наследовании
function addParent(obj, parent) {
    // Если объект ещё не инициализирован, инициализируем его
    if (!obj.__parents_) {
        obj.__parents_ = [];
        obj.__noSuchMethod__ = noMethod;
    }

    // Добавляем родителя
    obj.__parents_.push(parent);
}

Ниже показан пример использования этой идеи.

// Пример первого базового класса

function NamedThing(name) {
    this.name = name;
}

NamedThing.prototype = {
    getName: function() { return this.name; },
    setName: function(newName) { this.name = newName; }
}

// Пример второго базового класса

function AgedThing(age){
    this.age = age;
}

AgedThing.prototype = {
    getAge: function() { return this.age; },
    setAge: function(age) { this.age = age; }
}

// Дочерний класс. Наследуется от NamedThing и AgedThing, а также определяет свойство address

function Person(name, age, address) {
    addParent(this, NamedThing.prototype);
    NamedThing.call(this, name);
    addParent(this, AgedThing.prototype);
    AgedThing.call(this, age);
    this.address = address;
}

Person.prototype = {
    getAddr: function() { return this.address; },
    setAddr: function(addr) { this.address = addr; }
}

var bob = new Person('Боб', 25, 'Нью-Йорк');

console.log('getAge лежит ' + (('getAge' in bob) ? 'в' : 'не в') + ' объекте bob');
console.log('возраст Боба: ' + bob.getAge());
console.log('getName лежит ' + (('getName' in bob) ? 'в' : 'не в') + ' объекте bob');
console.log('имя Боба: ' + bob.getName());
console.log('getAddr лежит ' + (('getAddr' in bob) ? 'в' : 'не в') + ' объекте bob');
console.log('адрес Боба: ' + bob.getAddr());

Вывод примера будет следующим:

getAge лежит не в объекте bob
возраст Боба: 25
getName лежит не в объекте bob
имя Боба: Боб
getAddr лежит в объекте bob
адрес Боба: Нью-Йорк

Спецификации

Не является частью какой-либо спецификации.

Совместимость с браузерами

Возможность Chrome Firefox (Gecko) Internet Explorer Opera Safari
Базовая поддержка Нет 1.0 (1.7 или ранее) Нет Нет Нет
Возможность Android Chrome для Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Базовая поддержка Нет Нет 1.0 (1.0) Нет Нет Нет

Метки документа и участники

 Внесли вклад в эту страницу: Mingun
 Обновлялась последний раз: Mingun,