没有“这个类必须有这些功能”的概念(也就是说,本身没有接口),因为:
- JavaScript 继承基于对象,而不是类。在您意识到之前,这没什么大不了的:
- JavaScript 是一种非常动态类型的语言——您可以使用适当的方法创建一个对象,这将使其符合接口,然后取消定义所有构成它的东西符合。颠覆类型系统非常容易——即使是偶然的! -- 一开始就尝试制作类型系统是不值得的。
相反,JavaScript 使用所谓的duck typing。 (如果它像鸭子一样走路,像鸭子一样嘎嘎叫,就 JS 而言,它就是一只鸭子。)如果你的对象有 quack()、walk() 和 fly() 方法,代码可以在任何它期望的地方使用它一个可以行走、嘎嘎和飞行的对象,不需要实现一些“Duckable”接口。接口正是代码使用的一组函数(以及这些函数的返回值),并且通过鸭子类型,您可以免费获得。
现在,这并不是说如果您尝试调用 some_dog.quack(),您的代码不会在中途失败;你会得到一个 TypeError。坦率地说,如果你告诉狗嘎嘎,你的问题会稍微大一点。当您将所有鸭子排成一排时,鸭子打字效果最好,可以这么说,并且除非您将它们视为普通动物,否则不要让狗和鸭子混在一起。换句话说,即使界面是流动的,它仍然存在;将狗传递给期望它首先会嘎嘎飞的代码通常是错误的。
但是,如果您确定自己在做正确的事情,则可以通过在尝试使用特定方法之前测试其是否存在来解决嘎嘎狗问题。类似的东西
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
因此,您可以在使用之前检查所有可以使用的方法。不过,语法有点难看。还有一种更漂亮的方式:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
这是标准的 JavaScript,所以它应该可以在任何值得使用的 JS 解释器中工作。它具有像英语一样阅读的额外好处。
对于现代浏览器(即除了 IE 6-8 之外的几乎所有浏览器),甚至还有一种方法可以防止属性出现在 for...in:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
问题在于 IE7 对象根本没有.defineProperty,而在 IE8 中,据称它仅适用于宿主对象(即 DOM 元素等)。如果兼容性是一个问题,则不能使用.defineProperty。 (我什至不会提到 IE6,因为它在中国以外已经无关紧要了。)
另一个问题是,一些编码风格喜欢假设每个人都写不好的代码,并禁止修改Object.prototype,以防有人想盲目使用for...in。如果您关心这一点,或者正在使用(IMO broken)代码,请尝试稍微不同的版本:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}