【问题标题】:Is there a way to catch an attempt to access a non existant property or method?有没有办法捕捉访问不存在的属性或方法的尝试?
【发布时间】:2011-02-09 15:07:58
【问题描述】:

例如这段代码:

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() 的属性,但我只是想知道是否可以避免它,因为主对象是一种包装器,只是临时添加了一些功能(同时更容易访问对象)。

【问题讨论】:

  • 不能用try catch吗?还是我错过了什么?
  • 它必须发生在函数内部。
  • 我会补充一点,在最新版本的 JS/ES 中,实际上有 getter 和 setter 元方法。

标签: javascript


【解决方案1】:

嗯,似乎有了 Harmony(ES6),就会有办法,而且比其他 programing languages 做的方式更复杂。基本上,它涉及使用 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 docs

如果设法在此基础上添加一些语法糖,我会更高兴,但无论如何,在已经强大的语言中拥有这种力量真是太好了。祝你今天过得愉快! :)

PD:我没有复制rosettacode js条目,我更新了它。

【讨论】:

  • 但这没用,因为它不适用于目标对象本身,而是适用于全新的对象。换句话说,你不能用它来捕获Array的属性访问。
  • @Pacerier 仅仅因为它不适用于您的用例并不意味着它毫无用处。事实上,这对于手头的问题很有用。
  • @Pacerier 您的用例可能是有效的,但我怀疑,如果您正在编写一个可能是有效的库,那么给消费者一个新的“MyArray”对象,而不是修改内置的,这增加了可读性并保持系统解耦。此外,可能通过降低内置的可预测性而导致性能问题。
【解决方案2】:

有一种方法可以为不存在的方法的调用定义通用处理程序,但它是非标准的。查看 Firefox 的 noSuchMethod。将让您动态地将调用路由到未定义的方法。似乎 v8 也是 getting support 的。

要使用它,请在任何对象上定义此方法:

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

【讨论】:

  • trycatch 不起作用,因为它切断了用户代码的流程(“您将如何恢复操作?”),其次,无法从 catch 处理程序访问 obj。
  • This feature is obsolete... Try to avoid using it.
【解决方案3】:

看来你对 JS 很熟悉。 不幸的是,我不知道该语言中有这样的功能,并且很确定它不存在。在我看来,你最好的选择是使用统一接口并扩展它,或者扩展你的对象继承的原型(然后你可以在继续方法调用之前使用 instanceof)或者使用有点麻烦的 '&&' 运算符为了避免访问不存在的属性/方法:

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

您还可以根据 Anurag 的建议(“调用”)扩展 Object 原型。

【讨论】:

    【解决方案4】:

    您也可以检查该方法是否存在。

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

    【讨论】:

    • 这不是重点。现实情况可能是您想捕获这些未定义的方法并做一些事情而不是转储错误。例如,改为返回一些默认值
    • 不管怎样,最好还是做if( methodName in a )..检查方法是否存在
    猜你喜欢
    • 2013-04-18
    • 1970-01-01
    • 1970-01-01
    • 2022-08-02
    • 2022-08-23
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 2010-09-27
    相关资源
    最近更新 更多