Есть ли у JavaScript тип интерфейса (например, интерфейс Java)?
Я изучаю как сделать ООП с помощью JavaScript. Имеет ли он концепцию интерфейса (например, Java interface
)?
Итак, я смог бы создать слушателя...
Ответы
Ответ 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"
}
Что вы не можете сделать:
Ответ 9
Javascript не имеет интерфейсов. Но это может быть утиная, пример можно найти здесь:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
Ответ 10
в JavaScript нет нативных интерфейсов,
Есть несколько способов симулировать интерфейс. я написал пакет, который делает это
вы можете увидеть имплантацию здесь here
Ответ 11
Я знаю, что это старый, но мне недавно потребовалось все больше и больше иметь удобный API для проверки объектов против интерфейсов. Поэтому я написал следующее: https://github.com/tomhicks/methodical
Он также доступен через NPM: npm install methodical
В основном это все, что было предложено выше, с некоторыми вариантами для более строгих и без необходимости выполнять нагрузки шаблона if (typeof x.method === 'function')
.
Надеюсь, кто-то сочтет это полезным.
Ответ 12
Там также jQuery.isFunction(method)
, если вы предпочитаете, чем код cHao.