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

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


当前回答

找到一个尽可能低影响的模拟接口的解决方案也困扰着我。

一种解决方案是制作一个工具:

/**
@parameter {Array|object} required : method name list or members types by their name
@constructor
*/
let Interface=function(required){
    this.obj=0;
    if(required instanceof Array){
        this.obj={};
        required.forEach(r=>this.obj[r]='function');
    }else if(typeof(required)==='object'){
        this.obj=required;
    }else {
        throw('Interface invalid parameter required = '+required);
    }
};
/** check constructor instance
@parameter {object} scope : instance to check.
@parameter {boolean} [strict] : if true -> throw an error if errors ar found.
@constructor
*/
Interface.prototype.check=function(scope,strict){
    let err=[],type,res={};
    for(let k in this.obj){
        type=typeof(scope[k]);
        if(type!==this.obj[k]){
            err.push({
                key:k,
                type:this.obj[k],
                inputType:type,
                msg:type==='undefined'?'missing element':'bad element type "'+type+'"'
            });
        }
    }
    res.success=!err.length;
    if(err.length){
        res.msg='Class bad structure :';
        res.errors=err;
        if(strict){
            let stk = new Error().stack.split('\n');
            stk.shift();
            throw(['',res.msg,
                res.errors.map(e=>'- {'+e.type+'} '+e.key+' : '+e.msg).join('\n'),
                '','at :\n\t'+stk.join('\n\t')
            ].join('\n'));

        }
    }
    return res;
};

使用实例:

// create interface tool
let dataInterface=new Interface(['toData','fromData']);
// abstract constructor
let AbstractData=function(){
    dataInterface.check(this,1);// check extended element
};
// extended constructor
let DataXY=function(){
    AbstractData.apply(this,[]);
    this.xy=[0,0];
};
DataXY.prototype.toData=function(){
    return [this.xy[0],this.xy[1]];
};

// should throw an error because 'fromData' is missing
let dx=new DataXY();

与类

class AbstractData{
    constructor(){
        dataInterface.check(this,1);
    }
}
class DataXY extends AbstractData{
    constructor(){
        super();
        this.xy=[0,0];
    }
    toData(){
        return [this.xy[0],this.xy[1]];
    }
}

它仍然有一点性能的提高,并且需要依赖于Interface类,但是可以用于调试或开放api。

其他回答

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;
}

拿起Dustin Diaz的“JavaScript设计模式”。有几章专门介绍通过Duck Typing实现JavaScript接口。这也是一本好书。但是,没有接口的语言本机实现,你必须使用Duck Type。

// example duck typing method
var hasMethods = function(obj /*, method list as strings */){
    var i = 1, methodName;
    while((methodName = arguments[i++])){
        if(typeof obj[methodName] != 'function') {
            return false;
        }
    }
    return true;
}

// in your code
if(hasMethods(obj, 'quak', 'flapWings','waggle')) {
    //  IT'S A DUCK, do your duck thang
}

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

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();
}

像这样的抽象接口

const MyInterface = {
  serialize: () => {throw "must implement serialize for MyInterface types"},
  print: () => console.log(this.serialize())
}

创建一个实例:

function MyType() {
  this.serialize = () => "serialized "
}
MyType.prototype = MyInterface

并使用它

let x = new MyType()
x.print()

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