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

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


当前回答

像这样的抽象接口

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

其他回答

希望任何还在寻找答案的人都能从中找到帮助。

你可以尝试使用代理(这是自ECMAScript 2015以来的标准):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

latLngLiteral = new Proxy({},{
    set: function(obj, prop, val) {
        //only these two properties can be set
        if(['lng','lat'].indexOf(prop) == -1) {
            throw new ReferenceError('Key must be "lat" or "lng"!');
        }

        //the dec format only accepts numbers
        if(typeof val !== 'number') {
            throw new TypeError('Value must be numeric');
        }

        //latitude is in range between 0 and 90
        if(prop == 'lat'  && !(0 < val && val < 90)) {
            throw new RangeError('Position is out of range!');
        }
        //longitude is in range between 0 and 180
        else if(prop == 'lng' && !(0 < val && val < 180)) {
            throw new RangeError('Position is out of range!');
        }

        obj[prop] = val;

        return true;
    }
});

然后你就可以轻松地说:

myMap = {}
myMap.position = latLngLiteral;

如果你想通过instanceof (@Kamaffeather问)检查,你可以像这样把它包装在一个对象中:

class LatLngLiteral {
    constructor(props)
    {
        this.proxy = new Proxy(this, {
            set: function(obj, prop, val) {
                //only these two properties can be set
                if(['lng','lat'].indexOf(prop) == -1) {
                    throw new ReferenceError('Key must be "lat" or "lng"!');
                }

                //the dec format only accepts numbers
                if(typeof val !== 'number') {
                    throw new TypeError('Value must be numeric');
                }

                //latitude is in range between 0 and 90
                if(prop == 'lat'  && !(0 < val && val < 90)) {
                    throw new RangeError('Position is out of range!');
                }
                //longitude is in range between 0 and 180
                else if(prop == 'lng' && !(0 < val && val < 180)) {
                    throw new RangeError('Position is out of range!');
                }

                obj[prop] = val;

                return true;
            }
        })
        return this.proxy
    }
}

这可以在不使用Proxy的情况下完成,而是使用类getter和setter:

class LatLngLiteral {
    #latitude;
    #longitude;

    get lat()
    {
        return this.#latitude;
    }

    get lng()
    {
        return this.#longitude;
    }
    
    set lat(val)
    {
        //the dec format only accepts numbers
        if(typeof val !== 'number') {
            throw new TypeError('Value must be numeric');
        }

        //latitude is in range between 0 and 90
        if(!(0 < val && val < 90)) {
            throw new RangeError('Position is out of range!');
        }
        
        this.#latitude = val
    }
    
    set lng(val)
    {
        //the dec format only accepts numbers
        if(typeof val !== 'number') {
            throw new TypeError('Value must be numeric');
        }

        //longitude is in range between 0 and 180
        if(!(0 < val && val < 180)) {
            throw new RangeError('Position is out of range!');
        }
        
        this.#longitude = val
    }
}

没有,但是它有mixins。 您可以使用抽象子类或mixins作为替代https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#mix-ins

虽然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()

我知道这是一个老问题,但我最近发现自己越来越需要一个方便的API来根据接口检查对象。所以我写了这个:https://github.com/tomhicks/methodical

它也可以通过NPM: NPM install methodical获得

它基本上完成了上面建议的所有事情,并有一些更严格的选项,而且所有这些都不需要执行大量if (typeof x.method === 'function')样板文件。

希望有人觉得它有用。

这是旧的,但我实现了在ES6上使用的接口,没有转译器。

https://github.com/jkutianski/ES6-Interfaces