Есть ли способ поймать попытку получить доступ к несуществующему свойству или методу?

Например, этот код:

function stuff() {
  this.onlyMethod = function () {
    return something;
  }
}

// some error is thrown
stuff().nonExistant();

Есть ли способ сделать что-то вроде PHP __call как возврат изнутри объекта?

function stuff() {
  this.onlyMethod = function () {
    return something;
  }
  // "catcher" function
  this.__call__ = function (name, params) {
    alert(name + " can't be called.");
  }
}

// would then raise the alert "nonExistant can't be called".
stuff().nonExistant();

Возможно, я объясню немного больше, что я делаю.

Объект содержит другой объект, который имеет методы, которые должны быть доступны непосредственно через этот объект. Но эти методы разные для каждого объекта, поэтому я не могу их просто маршрутизировать, поэтому мне нужно их динамически называть.

Я знаю, что могу просто сделать объект внутри него свойством основного объекта stuff.obj.existant(), но мне просто интересно, могу ли я его избежать, поскольку основной объект - это своего рода оболочка, которая временно добавляет некоторые функции (и упрощает доступ к объекту в одно и то же время).

Ответы

Ответ 1

Существует способ определения общего обработчика вызовов для несуществующих методов, но он нестандартен. Оформить покупку noSuchMethod для Firefox. Позволяет динамически маршрутизировать вызовы undefined. Кажется, что v8 также получает поддержку для него.

Чтобы использовать его, определите этот метод для любого объекта:

var a = {};

a.__noSuchMethod__ = function(name, args) {
    console.log("method %s does not exist", name);
};

a.doSomething(); // logs "method doSomething does not exist"

Однако, если вы хотите использовать кросс-браузерный метод, тогда простые блоки try-catch, если вы хотите:

try {
    a.doSomething();
}
catch(e) {
    // do something
}

Если вы не хотите писать try-catch по всему коду, вы можете добавить оболочку к основному объекту, через который маршрутизируются все вызовы функций.

function main() {
    this.call = function(name, args) {
        if(this[name] && typeof this[name] == 'function') {
            this[name].call(args);
        }
        else {
            // handle non-existant method
        }
    },
    this.a = function() {
        alert("a");
    }
}

var object = new main();
object.call('a') // alerts "a"
object.call('garbage') // goes into error-handling code

Ответ 2

Ну, похоже, что с гармонией (ES6) будет какой-то способ, и это сложнее по сравнению с другими языками программирования сделай это. В основном, это предполагает использование встроенного объекта Proxy для создания оболочки для объекта и изменения способа его реализации по умолчанию:

obj  = new Proxy({}, 
        { get : function(target, prop) 
            { 
                if(target[prop] === undefined) 
                    return function()  {
                        console.log('an otherwise undefined function!!');
                    };
                else 
                    return target[prop];
            }
        });
obj.f()        ///'an otherwise undefined function!!'
obj.l = function() {console.log(45);};
obj.l();       ///45

Прокси пересылает все методы, которые не обрабатываются обработчиками в обычный объект. Так будет, если бы его там не было, и из прокси вы можете изменить цель. Есть также больше обработчиков, даже некоторые из них могут модифицировать получение прототипа и устанавливать для любого доступа к свойствам да!.

Как вы думаете, это не поддерживается во всех браузерах прямо сейчас, но в Firefox вы можете легко играть с прокси-интерфейсом, просто перейдите в Документы MDN

Это сделало бы меня счастливее, если бы удалось добавить на него какой-то синтаксический сахар, но, во всяком случае, приятно иметь такую ​​мощь на уже мощном языке. Хорошего дня!:)

PD: я не копировал запись jset rosettacode, я обновил ее.

Ответ 3

Вы также можете проверить, существует ли метод.

if(a['your_method_that_doesnt_exist']===undefined){
//method doesn't exist
}

Ответ 4

Кажется, вы знаете свой путь вокруг JS. К сожалению, я не знаю такой возможности на этом языке, и я уверен, что ее не существует. На мой взгляд, ваш лучший вариант - либо использовать единый интерфейс, либо расширять его, либо расширять прототипы, из которых наследуются ваши объекты (тогда вы можете использовать instanceof перед тем, как идти вперед с вызовом метода) или использовать несколько громоздкий "& & ' оператора, чтобы избежать доступа несуществующих свойств/методов:

obj.methodName && obj.methodName(art1,arg2,...);

Вы также можете расширить прототип объекта с помощью предложения Anurag ( "вызов" ).