我正在学习如何用JavaScript制作面向对象编程。是否有接口概念(如Java的接口)?

这样我就可以创建一个监听器。


当前回答

JavaScript (ECMAScript edition 3)有一个保留字以备将来使用。我认为这正是为了这个目的而设计的,然而,在匆忙发布规范的过程中,他们没有时间定义如何使用它,所以,目前,浏览器除了让它呆在那里,偶尔会抱怨你试图用它来做什么之外,什么也不做。

创建自己的object. implementation (Interface)方法是可能的,而且非常简单,只要在给定的对象中没有实现特定的属性/函数集,该方法的逻辑就会中断。

我写了一篇关于面向对象的文章,其中使用了我自己的符号如下:

// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
    constructor: function(name) {
        Dog.superClass.call(this, name);
    },
    bark: function() {
        alert('woof');
    }
}).implement(Mammal);

有很多方法来处理这个特殊的问题,但这是我在自己的接口实现中使用的逻辑。我发现我更喜欢这种方法,而且它易于阅读和使用(如上所述)。这意味着在Function中添加一个“实现”方法。原型,有些人可能有一个问题,但我发现它工作得很漂亮。

Function.prototype.implement = function() {
    // Loop through each interface passed in and then check 
    // that its members are implemented in the context object (this).
    for(var i = 0; i < arguments.length; i++) {
       // .. Check member's logic ..
    }
    // Remember to return the class being tested
    return this;
}

其他回答

在Java中需要接口,因为它是静态类型的,并且在编译期间应该知道类之间的契约。在JavaScript中则不同。JavaScript是动态类型的;这意味着当你获得对象时,你可以检查它是否有特定的方法并调用它。

JavaScript (ECMAScript edition 3)有一个保留字以备将来使用。我认为这正是为了这个目的而设计的,然而,在匆忙发布规范的过程中,他们没有时间定义如何使用它,所以,目前,浏览器除了让它呆在那里,偶尔会抱怨你试图用它来做什么之外,什么也不做。

创建自己的object. implementation (Interface)方法是可能的,而且非常简单,只要在给定的对象中没有实现特定的属性/函数集,该方法的逻辑就会中断。

我写了一篇关于面向对象的文章,其中使用了我自己的符号如下:

// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
    constructor: function(name) {
        Dog.superClass.call(this, name);
    },
    bark: function() {
        alert('woof');
    }
}).implement(Mammal);

有很多方法来处理这个特殊的问题,但这是我在自己的接口实现中使用的逻辑。我发现我更喜欢这种方法,而且它易于阅读和使用(如上所述)。这意味着在Function中添加一个“实现”方法。原型,有些人可能有一个问题,但我发现它工作得很漂亮。

Function.prototype.implement = function() {
    // Loop through each interface passed in and then check 
    // that its members are implemented in the context object (this).
    for(var i = 0; i < arguments.length; i++) {
       // .. Check member's logic ..
    }
    // Remember to return the class being tested
    return this;
}

虽然javaScript中不像Java中那样有接口,但你可以用这条消息下的代码来模仿这种行为。因为接口基本上是一个强制契约,你可以自己构建它。

下面的代码来自3个类:接口、父类和子类。

接口中有检查方法和属性是否存在的方法。 父类用于使用Interface类在子类中强制执行所需的方法和属性。 孩子是父母规则强制执行的类别。

在您正确地设置它之后,如果子程序中缺少方法或属性,那么您将在控制台中看到一个错误,如果子程序正确地实现了契约,则什么也没有。

class Interface {  
    checkRequiredMethods(methodNames) {
        setTimeout( () => {
            const loopLength = methodNames.length;
            let i = 0
            for (i; i<loopLength; i++) {
                if (typeof this[methodNames[i]] === "undefined") {
                    this.throwMissingMethod(methodNames[i]);
                }

                else if (typeof this[methodNames[i]] !== "function") {
                    this.throwNotAMethod(methodNames[i]);
                }
            }
        }, 0);
    }

    checkRequiredProperties(propNames) {
        setTimeout( () => {
            const loopLength = propNames.length;
            let i = 0
            for (i; i<loopLength; i++) {
                if (typeof this[propNames[i]] === "undefined") {
                    this.throwMissingProperty(propNames[i]);
                }

                else if (typeof this[propNames[i]] === "function") {
                    this.throwPropertyIsMethod(propNames[i]);
                }
            }
        }, 0);     
    }

    throwMissingMethod(methodName) {
        throw new Error(`error method ${methodName} is undefined`);
    }

    throwNotAMethod(methodName) {
        throw new Error(`error method ${methodName} is not a method`);
    }

    throwMissingProperty(propName) {
        throw new Error(`error property ${propName} is not defined`);
    }

    throwPropertyIsMethod(propName) {
        throw new Error(`error property ${propName} is a method`);
    }
}
class Parent extends Interface {
    constructor() {
        super()

        this.checkRequiredProperties([
            "p1",
            "p2",
            "p3",
            "p4",
            "p5"
        ]);

        this.checkRequiredMethods([ 
            "m1",
            "m2",
            "m3",
            "m4"
        ]);
    }
}
class Child extends Parent {
    p1 = 0;
    p2 = "";
    p3 = false;
    p4 = [];
    p5 = {};

    constructor() {
        super();
    }

    m1() {}
    m2() {}
    m3() {}
    m4() {}
}
new Child()

没有“这个类必须有这些函数”的概念(也就是说,本身没有接口),因为:

JavaScript继承基于对象,而不是类。这没什么大不了的,直到你意识到: JavaScript是一种非常动态类型的语言——您可以使用适当的方法创建一个对象,这将使它符合接口,然后取消定义所有使它符合接口的东西。颠覆类型系统是如此容易——甚至是意外地!一开始就尝试创建一个类型系统是不值得的。

相反,JavaScript使用所谓的鸭子类型。(如果它像鸭子一样走路,像鸭子一样嘎嘎叫,在JS看来,它就是一只鸭子。)如果您的对象具有quack()、walk()和fly()方法,那么代码可以在任何期望对象能够walk、quack和fly的地方使用它,而不需要实现某种“Duckable”接口。接口正是代码使用的函数集(以及这些函数的返回值),通过duck键入,您可以免费获得这些功能。

Now, that's not to say your code won't fail halfway through, if you try to call some_dog.quack(); you'll get a TypeError. Frankly, if you're telling dogs to quack, you have slightly bigger problems; duck typing works best when you keep all your ducks in a row, so to speak, and aren't letting dogs and ducks mingle together unless you're treating them as generic animals. In other words, even though the interface is fluid, it's still there; it's often an error to pass a dog to code that expects it to quack and fly in the first place.

但是,如果您确信您所做的事情是正确的,那么您可以在尝试使用特定方法之前通过测试它的存在来解决嘎嘎狗问题。类似的

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。原型,以防有人盲目地使用for…in。如果你关心这个,或者正在使用(IMO坏了)的代码,尝试一个稍微不同的版本:

function can(obj, methodName)
{
     return ((typeof obj[methodName]) == "function");
}

if (can(someObject, "quack"))
{
    someObject.quack();
}

JavaScript中没有本机接口, 有几种方法可以模拟接口。我已经写了一个包来做这件事

你可以看到这里的植入