3

我的代码如下所示:

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

4 回答 4

5

两年前本杰明,你不太了解行为类型的概念是吗?

幸运的是,您很快就会意识到,自从编写该问题以来,JavaScript 中的接口不是结构化的,甚至编写了一个库来解决您在这里所说的确切问题,只是从不使用它。

所以,回答你的问题:

  • 是的,完全有可能做到这一点。
  • 对于您给出的案例,您可能不应该这样做。

怎么做

如果您只需要通过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 等语言中,你知道你会得到哪些类型。

于 2014-05-25T22:15:40.440 回答
2

不幸的是,这是不可能以任何广泛支持的方式实现的。您可以获得的最接近的方法是对所有调用使用代理功能,即foo.bar()您可以使用类似foo.invoke('bar'). 但是,这很丑陋,很可能与您在第一个“不感兴趣”部分中的意思很接近。

不过,在松散类型的语言中,期望开发人员传递兼容的对象是很常见的——即,在传递意外的东西时出错通常是可以的。


但是,如果您有幸使用了支持 ECMAScript-harmony 功能的 JS 引擎(显然 IE 不支持),您可以使用Proxy 对象来实现这一点。基本上,您将对象包装在 Proxy 中,它可以让您捕获对象上的大多数操作,例如迭代它、检索属性,甚至创建属性。除了查看这个问题的答案的两个链接之外,可能值得一试。

于 2012-06-21T18:36:32.910 回答
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
于 2012-06-21T18:40:04.587 回答
1

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

obj.foo();

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

于 2012-06-21T19:21:22.980 回答