我可以决定调用不存在的对象方法时会发生什么吗?

时间:2012-06-21 18:34:34

标签: javascript

我的代码如下:

obj.foo(); // obj might, or might not have a `foo` method.

我想知道是否可以覆盖在我的代码中调用obj.foo时发生的情况,例如:

obj.foo = function(){ alert ("Hello"); });
obj.onCallNonExistentMethod = function(){ // I know this is imaginary syntax
    alert("World");
}
obj.foo(); // alerts "Hello"
delete obj.foo;
obj.foo(); // alerts "World" , would TypeError without the method missing handler.

根据我的理解,在Ruby中method_missingconst_missing或类似的东西。

我可以覆盖在JavaScript中调用不存在的对象方法时发生的事情吗?如果可以,我该怎么做?

目标是验证我提供给用户的API,以便他们可以安全地使用API​​,我可以更明确地警告错误。

4 个答案:

答案 0 :(得分:5)

两年前,本杰明,你不太了解行为打字的概念吗?

幸运的是,你很快就会在JavaScript中发现an interface is not structural自写这个问题,甚至wrote a library解决了你在这里提到的确切问题,但从不使用它。

所以,回答你的问题:

  • 是的,完全可以这样做。
  • 你可能不应该为你所提供的案件。

怎么做

如果您只需要通过Proxy API支持真正(非常真实)的新浏览器,这是完全可行的。

var realObject = { foo: function(){ alert("Bar"); }); // our fooable api
var api = new Proxy({}, {
    get: function(target, name){
        if(name in target){ // normal API call
            return target[name]; 
        }
        // return whatever you want instead of the method:
        return function(){ alert("Baz"); });
    }
});
api.foo(); //alerts Bar
api.bar(); //alerts Baz
api.IWillNotBuyThisRecordItIsScratched(); // alerts Baz

所以,虽然浏览器支持很简单,但

为什么你不应该

接口传达行为,在诸如拼写错误之类的情况下,可以(并且应该)通常由静态分析工具(如jshint或ternjs)捕获。

检查拼写错误的工具根本不足以传达JavaScript中的行为,类型检查被认为是反模式,通常 - 在JavaScript,Ruby和Python等语言中,您知道您将获得的类型。

答案 1 :(得分:2)

不幸的是,在任何广泛支持的方式都不可能。您可以获得的最接近的是对所有呼叫使用代理功能,即代替foo.bar(),您将使用foo.invoke('bar')之类的内容。然而,这非常丑陋,很可能接近你在第一个“不感兴趣”部分的意思。

在松散类型的语言中,期望开发人员通过兼容的对象是很常见的 - 即在传递意外事件时收到错误通常很好。


但是,如果你足够幸运地使用支持ECMAScript-harmony功能的JS引擎(显然IE不支持),你可以使用Proxy objects来实现这一点。基本上你将对象包装在一个代理中,它允许你在对象上捕获most operations,例如迭代它,检索属性,甚至创建属性。除了查看this answer to this question的两个链接可能值得一试。

答案 2 :(得分:1)

如果你正在使用严格定义的对象,你应该使用自定义对象而不是对象文字:

function Foo(bar, baz) {
  this.bar = bar;
  this.baz = baz;
}
Foo.prototype = {
  fizz: function () {
     alert(this.bar + ' ' + this.baz + '!');
  }
};

//elsewhere
f = new Foo('Hello', 'World');
f.fizz();

然后,您可以使用Foo运算符判断对象是否为instanceof个实例:

function Buzz(foo) {
  if (foo instanceof Foo) {
    foo.fizz();
  }
}
f = new Foo('Hello', 'World');
Buzz(f);

如果您只想查看对象是否包含特定参数的函数,您可以使用以下内容:

function hasFunction(obj, param) {
    return (typeof obj[param] === 'function');
}
o = {
    foo: function () {}
}
hasFunction(o, 'foo'); //true
hasFunction(o, 'bar'); //false

答案 3 :(得分:1)

这在今天并不是很有用(由于浏览器支持不稳定),但Proxy API将允许您编写拦截对象的属性访问的代码。换句话说,当你写:

obj.foo();
然后

代理的陷阱可以返回它想要的任何内容(被调用),无论该属性是否实际存在于原始对象上。