Подтвердить что ты не робот

Есть ли у JavaScript тип интерфейса (например, интерфейс Java)?

Я изучаю как сделать ООП с помощью JavaScript. Имеет ли он концепцию интерфейса (например, Java interface)?

Итак, я смог бы создать слушателя...

4b9b3361

Ответ 1

Нет понятия "этот класс должен иметь эти функции" (то есть никаких интерфейсов как таковых), потому что:

  • Наследование JavaScript основано на объектах, а не на классах. Это не большая проблема, пока вы не осознаете:
  • JavaScript - это чрезвычайно динамически типизированный язык - вы можете создать объект с надлежащими методами, который бы соответствовал интерфейсу, а затем деинсталлировал все, что заставило его соответствовать. Было бы так легко подорвать систему типов - даже случайно! - что не стоит пытаться создавать систему типов в первую очередь.

Вместо этого JavaScript использует то, что называется утиная печать. (Если он ходит как утка, а quacks - как утка, насколько это касается JS, это утка.) Если ваш объект имеет методы quack(), walk() и fly(), код может использовать его везде, где он ожидает объект, который может ходить, взлетать и летать, не требуя реализации некоторого "Duckable" интерфейса. Интерфейс - это именно тот набор функций, который использует код (и возвращаемые значения из этих функций), и с утиным набором текста вы получаете это бесплатно.

Теперь, чтобы не сказать, что ваш код не сработает на полпути, если вы попытаетесь позвонить some_dog.quack(); вы получите TypeError. Честно говоря, если вы говорите собакам о шарлатане, у вас есть несколько большие проблемы; утиная печать работает лучше всего, когда вы держите всех своих уток подряд, так сказать, и не позволяете собакам и уткам сливаться вместе, если вы не относитесь к ним как к родовым животным. Другими словами, хотя интерфейс является жидким, он все еще там; часто бывает ошибкой передавать собаку коду, который ожидает, что он будет крякать и лететь в первую очередь.

Но если вы уверены, что поступаете правильно, вы можете обойти проблему quacking-dog, проверив наличие определенного метода, прежде чем пытаться его использовать. Что-то вроде

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

Ответ 2

Возьмите копию " Шаблоны дизайна JavaScript " Дастина Диаса. Там есть несколько глав, посвященных реализации интерфейсов JavaScript через Duck Typing. Это тоже приятно читать. Но нет, нет языковой реализации интерфейса, вы должны 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 A DUCK, do your duck thang
}

Ответ 3

JavaScript (ECMAScript edition 3) имеет implements зарезервированное слово сохраненное для будущего использования. Я думаю, что это предназначено именно для этой цели, однако, в спешке, чтобы получить спецификацию у двери, у них не было времени определить, что с ней делать, поэтому в настоящее время браузеры не делают ничего, кроме пусть он сидит и иногда жалуется, если вы попытаетесь использовать его для чего-то.

Возможно и действительно достаточно просто создать свой собственный метод Object.implement(Interface) с логикой, которая будет блокироваться всякий раз, когда определенный набор свойств/функций не реализуется в заданном объекте.

Я написал статью о object-orientation где используется мои собственные обозначения следующим образом:

// 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.prototype, с которым у некоторых может возникнуть проблема, но я нахожу, что он работает красиво.

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 logic ..
    }
    // Remember to return the class being tested
    return this;
}

Ответ 4

Интерфейсы JavaScript:

Хотя JavaScript не имеет типа interface, часто требуется время. По причинам, связанным с динамической природой JavaScript и использованием Prototypical-Inheritance, трудно обеспечить согласованные интерфейсы между классами, однако это можно сделать; и часто эмулируются.

В этот момент есть несколько конкретных способов эмуляции интерфейсов в JavaScript; дисперсия на подходах обычно удовлетворяет некоторым потребностям, в то время как другие остаются без внимания. Часто, самый надежный подход слишком громоздкий и препятствует разработчику (разработчику).

Вот подход к интерфейсам/абстрактным классам, который не очень громоздкий, объясняет, сохраняет реализации внутри абстракций до минимума и оставляет достаточно места для динамических или пользовательских методологий:

function resolvePrecept(interfaceName) {
    var interfaceName = interfaceName;
    return function curry(value) {
        /*      throw new Error(interfaceName + ' requires an implementation for ...');     */
        console.warn('%s requires an implementation for ...', interfaceName);
        return value;
    };
}

var iAbstractClass = function AbstractClass() {
    var defaultTo = resolvePrecept('iAbstractClass');

    this.datum1 = this.datum1 || defaultTo(new Number());
    this.datum2 = this.datum2 || defaultTo(new String());

    this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
    this.method2 = this.method2 || defaultTo(new Function('return new Object();'));

};

var ConcreteImplementation = function ConcreteImplementation() {

    this.datum1 = 1;
    this.datum2 = 'str';

    this.method1 = function method1() {
        return true;
    };
    this.method2 = function method2() {
        return {};
    };

    //Applies Interface (Implement iAbstractClass Interface)
    iAbstractClass.apply(this);  // .call / .apply after precept definitions
};

Участники

Предустановочный резольвер

Функция resolvePrecept - это утилита и вспомогательная функция для использования внутри вашего абстрактного класса. Его задача состоит в том, чтобы разрешить индивидуальную обработку внедрения инкапсулированных Заповедей (данных и поведения). Он может вызывать ошибки или предупреждать - И - присваивать значение по умолчанию классу Implementor.

iAbstractClass

iAbstractClass определяет используемый интерфейс. Его подход подразумевает молчаливое согласие со своим классом-исполнителем. Этот интерфейс присваивает каждому предписанию одно и то же точное пространство имен предписаний - ИЛИ - к тому, что возвращает функция Resolver Prescept. Однако молчаливое соглашение разрешает контекст - положение исполнителя.

Implementor

Исполнитель просто "соглашается" с интерфейсом (iAbstractClass в этом случае) и применяет его с помощью Constructor-Hijacking: iAbstractClass.apply(this). Определив данные и поведение выше, а затем захватив конструктор интерфейса - передав контекст реализации в конструктор интерфейса - мы можем гарантировать, что переопределения исполнителей будут добавлены и этот интерфейс будет раскрывать предупреждения и значения по умолчанию.

Это очень не громоздкий подход, который очень хорошо служил моей команде, и я очень хорошо следил за временем и разными проектами. Однако у него есть некоторые оговорки и недостатки.

Недостатки

Хотя это помогает в значительной степени реализовать согласованность в вашем программном обеспечении, не реализует истинные интерфейсы, но имитирует их. Хотя определения, значения по умолчанию и предупреждения или ошибки раскрыты, экспликация использования принудительно применяется и заявляется разработчиком (как и в случае с большей частью разработки JavaScript).

Это, по-видимому, лучший подход к "Интерфейсам в JavaScript", однако мне бы хотелось увидеть следующее:

  • Утверждения типов возврата
  • Утверждения подписей
  • Отменить объекты из delete действий
  • Утверждения о чем-либо еще распространенном или необходимом в специфике сообщества JavaScript.

Тем не менее, я надеюсь, что это поможет вам в той мере, в какой у меня есть моя команда и я.

Ответ 5

Вам нужны интерфейсы на Java, поскольку они статически типизированы, и контракт между классами должен быть известен во время компиляции. В JavaScript все по-другому. JavaScript динамически типизирован; это означает, что когда вы получаете объект, вы можете просто проверить, есть ли у него конкретный метод и вызвать его.

Ответ 6

Надеюсь, что любой, кто все еще ищет ответ, найдет его полезным.

Вы можете попробовать использовать Proxy (стандартно с 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;

Ответ 7

bob.js поддерживает какие-то интерфейсы.

1. Проверьте, реализует ли объект интерфейс:

var iFace = { say: function () { }, write: function () { } };  
var obj1 = { say: function() { }, write: function () { }, read: function () { } }; 
var obj2 = { say: function () { }, read: function () { } }; 
console.log('1: ' + bob.obj.canExtractInterface(obj1, iFace)); 
console.log('2: ' + bob.obj.canExtractInterface(obj2, iFace)); 
// Output: 
// 1: true 
// 2: false

2. Извлечь интерфейс из объекта:

var obj = {  
    msgCount: 0, 
    say: function (msg) { console.log(++this.msgCount + ': ' + msg); }, 
    sum: function (a, b) { console.log(a + b); } 
}; 
var iFace = { say: function () { } }; 
obj = bob.obj.extractInterface(obj, iFace); 
obj.say('Hello!'); 
obj.say('How is your day?'); 
obj.say('Good bye!'); 
// Output: 
// 1: Hello! 
// 2: How is your day? 
// 3: Good bye! 

Ответ 8

Если вы хотите использовать транскомпилятор, вы можете попробовать TypeScript. Он поддерживает черновые функции ECMA, аналогичные языкам, таким как coffeescript или babel.

В TypeScript ваш интерфейс может выглядеть следующим образом:

interface IMyInterface {
    id: number; // TypeScript types are lowercase
    name: string;
    callback: (key: string; value: any; array: string[]) => void;
    type: "test" | "notATest"; // so called "union type"
}

Что вы не можете сделать:

Ответ 10

в JavaScript нет нативных интерфейсов, Есть несколько способов симулировать интерфейс. я написал пакет, который делает это

вы можете увидеть имплантацию здесь here

Ответ 11

Я знаю, что это старый, но мне недавно потребовалось все больше и больше иметь удобный API для проверки объектов против интерфейсов. Поэтому я написал следующее: https://github.com/tomhicks/methodical

Он также доступен через NPM: npm install methodical

В основном это все, что было предложено выше, с некоторыми вариантами для более строгих и без необходимости выполнять нагрузки шаблона if (typeof x.method === 'function').

Надеюсь, кто-то сочтет это полезным.

Ответ 12

Там также jQuery.isFunction(method), если вы предпочитаете, чем код cHao.