Estou aprendendo a fazer OOP com JavaScript . Possui o conceito de interface (como o Java interface
)?
Então, eu seria capaz de criar um ouvinte ...
Estou aprendendo a fazer OOP com JavaScript . Possui o conceito de interface (como o Java interface
)?
Então, eu seria capaz de criar um ouvinte ...
Respostas:
Não há noção de "essa classe deve ter essas funções" (ou seja, nenhuma interface em si), porque:
Em vez disso, o JavaScript usa o que é chamado de digitação de pato . (Se ele anda como um pato e grasna como um pato, na medida em que JS se importa, é um pato.) Se o seu objeto possui métodos quack (), walk () e fly (), o código pode usá-lo onde quer que ele espere um objeto que pode andar, grasnar e voar, sem exigir a implementação de alguma interface "Duckable". A interface é exatamente o conjunto de funções que o código usa (e os valores de retorno dessas funções) e, com a digitação do duck, você obtém isso de graça.
Agora, isso não significa que seu código não falhará no meio, se você tentar ligar some_dog.quack()
; você receberá um TypeError. Francamente, se você está dizendo para os cães gritarem, você tem problemas um pouco maiores; digitar patos funciona melhor quando você mantém todos os seus patos em ordem, por assim dizer, e não deixa cães e patos se misturarem, a menos que os trate como animais genéricos. Em outras palavras, mesmo que a interface seja fluida, ela ainda está lá; geralmente é um erro passar um cachorro para o código que espera gritar e voar em primeiro lugar.
Mas se você tem certeza de que está fazendo a coisa certa, pode solucionar o problema dos cães quacking testando a existência de um método específico antes de tentar usá-lo. Algo como
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
Portanto, você pode verificar todos os métodos que pode usar antes de usá-los. A sintaxe é meio feia, no entanto. Há uma maneira um pouco mais bonita:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
Como o JavaScript é padrão, deve funcionar em qualquer interpretador JS que valha a pena usar. Tem o benefício adicional de ler como o inglês.
Para navegadores modernos (ou seja, praticamente qualquer navegador que não seja o IE 6-8), existe até uma maneira de impedir que a propriedade apareça em for...in
:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
O problema é que os objetos do IE7 não possuem .defineProperty
, e no IE8, ele supostamente funciona apenas em objetos host (ou seja, elementos DOM e outros). Se a compatibilidade for um problema, você não poderá usar .defineProperty
. (Eu nem vou mencionar o IE6, porque é mais irrelevante fora da China.)
Outra questão é que alguns estilos de codificação gostam de assumir que todos escrevem códigos incorretos e proíbem a modificação Object.prototype
caso alguém queira usar cegamente for...in
. Se você se importa com isso, ou está usando um código (IMO quebrado ), tente uma versão ligeiramente diferente:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
for...in
é - e sempre foi - repleto de tais perigos, e qualquer pessoa que faça isso sem pelo menos considerar que alguém adicionado a Object.prototype
(uma técnica não incomum, pela própria admissão desse artigo) verá seu código quebrar nas mãos de outra pessoa.
for...in
problema. developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
for...in
problema" ainda existirá até certo ponto, porque sempre haverá código desleixado ... bem, isso e Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3});
é um pouco mais trabalhoso do que apenas obj.a = 3;
. Eu posso entender totalmente as pessoas que não tentam fazer isso com mais frequência. : P
Pegue uma cópia dos ' JavaScript design patterns ' de Dustin Diaz . Existem alguns capítulos dedicados à implementação de interfaces JavaScript por meio do Duck Typing. É uma boa leitura também. Mas não, não há implementação nativa de linguagem de uma interface, você precisa do 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 (ECMAScript edição 3) tem uma implements
palavra reservada salva para uso futuro . Eu acho que isso é planejado exatamente para esse propósito, no entanto, com a pressa de divulgar as especificações, eles não tiveram tempo de definir o que fazer com elas; portanto, atualmente, os navegadores não fazem nada além deixe-o ali e, ocasionalmente, reclame se tentar usá-lo para alguma coisa.
É possível e de fato fácil criar seu próprio Object.implement(Interface)
método com uma lógica que falha sempre que um conjunto específico de propriedades / funções não é implementado em um determinado objeto.
Escrevi um artigo sobre orientação a objetos em que use minha própria notação da seguinte maneira :
// 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);
Existem várias maneiras de tratar desse gato em particular, mas essa é a lógica que usei para minha própria implementação de interface. Acho que prefiro essa abordagem e é fácil de ler e usar (como você pode ver acima). Significa adicionar um método 'implementar' ao Function.prototype
qual algumas pessoas podem ter problemas, mas acho que funciona lindamente.
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;
}
var interf = arguments[i]; for (prop in interf) { if (this.prototype[prop] === undefined) { throw 'Member [' + prop + '] missing from class definition.'; }}
. Veja a parte inferior do link do artigo para um exemplo mais elaborado.
Embora o JavaScript não tenha esse interface
tipo, muitas vezes é necessário. Por razões relacionadas à natureza dinâmica do JavaScript e ao uso de herança prototípica, é difícil garantir interfaces consistentes entre as classes - no entanto, é possível fazê-lo; e frequentemente emulado.
Neste ponto, existem várias maneiras específicas de emular interfaces em JavaScript; a variação nas abordagens geralmente satisfaz algumas necessidades, enquanto outras não são atendidas. Muitas vezes, a abordagem mais robusta é excessivamente complicada e atrapalha o implementador (desenvolvedor).
Aqui está uma abordagem para interfaces / classes abstratas que não é muito complicada, é explicativa, mantém as implementações dentro de abstrações em um nível mínimo e deixa espaço suficiente para metodologias dinâmicas ou personalizadas:
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
};
Resolvedor de Preceitos
A resolvePrecept
função é uma função utilitário e auxiliar para usar dentro da sua Classe Abstrata . Seu trabalho é permitir a manipulação de implementação personalizada de Preceitos encapsulados (dados e comportamento) . Ele pode gerar erros ou avisar - E - atribuir um valor padrão à classe Implementor.
iAbstractClass
O iAbstractClass
define a interface a ser usada. Sua abordagem envolve um acordo tácito com sua classe Implementor. Essa interface atribui cada preceito ao mesmo espaço de nome exato de preceito - OU - para o que a função Resolver do Preceito retornar. No entanto, o acordo tácito resolve para um contexto - uma provisão do Implementador.
Implementor
O Implementor simplesmente 'concorda' com uma Interface ( iAbstractClass neste caso) e aplica-lo pelo uso de Construtor-Hijacking : iAbstractClass.apply(this)
. Ao definir os dados e o comportamento acima e, em seguida, seqüestrar o construtor da Interface - passando o contexto do implementador para o construtor da interface - podemos garantir que as substituições do implementador sejam adicionadas e que a interface explique avisos e valores padrão.
Essa é uma abordagem muito incômoda, que serviu muito bem à minha equipe e ao longo do tempo e a diferentes projetos. No entanto, ele tem algumas ressalvas e desvantagens.
Desvantagens
Embora isso ajude a implementar consistentemente todo o software em um grau significativo, ele não implementa interfaces verdadeiras - mas simula-as. Embora as definições, os padrões e os avisos ou erros sejam explicados, a explicação do uso é imposta e afirmada pelo desenvolvedor (como ocorre com grande parte do desenvolvimento do JavaScript).
Essa é aparentemente a melhor abordagem para "Interfaces em JavaScript" , no entanto, eu adoraria ver o seguinte resolvido:
delete
açõesDito isto, espero que isso ajude você tanto quanto a minha equipe e a mim.
Você precisa de interfaces em Java, pois ele é digitado estaticamente e o contrato entre as classes deve ser conhecido durante a compilação. Em JavaScript é diferente. JavaScript é digitado dinamicamente; significa que, quando você obtém o objeto, pode apenas verificar se ele possui um método específico e chamá-lo.
yourMethod
a entrada nº 5 na Superclass
tabela de tabelas e, para cada subclasse que possui sua própria yourMethod
, simplesmente aponta a entrada nº 5 da subclasse. na implementação apropriada.
Implementation
que implementa SomeInterface
não apenas diz que implementa toda a interface. Possui informações que dizem "eu implemento SomeInterface.yourMethod
" e aponta para a definição de método para Implementation.yourMethod
. Quando a JVM chama SomeInterface.yourMethod
, ela procura na classe informações sobre implementações do método dessa interface e acha que precisa chamar Implementation.yourMethod
.
Espero que qualquer pessoa que ainda esteja procurando uma resposta seja útil.
Você pode experimentar usando um Proxy (é padrão desde 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;
}
});
Então você pode facilmente dizer:
myMap = {}
myMap.position = latLngLiteral;
Quando você deseja usar um transcompilador, tente o TypeScript. Ele suporta recursos de rascunho do ECMA (na proposta, as interfaces são chamadas de " protocolos ") semelhantes às linguagens como coffeescript ou babel.
No TypeScript, sua interface pode se parecer com:
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"
}
O que você não pode fazer:
não há interfaces nativas no JavaScript, existem várias maneiras de simular uma interface. eu escrevi um pacote que faz isso
você pode ver a implantação aqui
Javascript não possui interfaces. Mas pode ser do tipo pato, um exemplo pode ser encontrado aqui:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
Sei que é antigo, mas recentemente me vi precisando cada vez mais de uma API útil para verificar objetos em relação a interfaces. Então eu escrevi isso: https://github.com/tomhicks/methodical
Também está disponível via NPM: npm install methodical
Basicamente, faz tudo o que foi sugerido acima, com algumas opções para ser um pouco mais rigoroso e tudo sem ter que fazer um monte de if (typeof x.method === 'function')
clichê.
Espero que alguém ache útil.
Esta é uma pergunta antiga, mas esse tópico nunca deixa de me incomodar.
Como muitas das respostas aqui e em toda a Web se concentram em "impor" a interface, eu gostaria de sugerir uma visão alternativa:
Sinto a falta de interfaces quando estou usando várias classes que se comportam de maneira semelhante (por exemplo, implementar uma interface ).
Por exemplo, eu tenho um Gerador de E - mail que espera receber Fábricas de Seções de E-mail , que "sabem" como gerar o conteúdo e o HTML das seções. Assim, eles toda a necessidade de ter algum tipo de getContent(id)
e getHtml(content)
métodos.
O padrão mais próximo das interfaces (embora ainda seja uma solução alternativa) que eu poderia pensar é usar uma classe que terá 2 argumentos, que definirão os 2 métodos de interface.
O principal desafio desse padrão é que os métodos precisam ser static
, ou obter como argumento a própria instância, para acessar suas propriedades. No entanto, há casos em que considero essa troca compensadora.
class Filterable {
constructor(data, { filter, toString }) {
this.data = data;
this.filter = filter;
this.toString = toString;
// You can also enforce here an Iterable interface, for example,
// which feels much more natural than having an external check
}
}
const evenNumbersList = new Filterable(
[1, 2, 3, 4, 5, 6], {
filter: (lst) => {
const evenElements = lst.data.filter(x => x % 2 === 0);
lst.data = evenElements;
},
toString: lst => `< ${lst.data.toString()} >`,
}
);
console.log('The whole list: ', evenNumbersList.toString(evenNumbersList));
evenNumbersList.filter(evenNumbersList);
console.log('The filtered list: ', evenNumbersList.toString(evenNumbersList));
interface abstrata como esta
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
crie uma instância:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
e use
let x = new MyType()
x.print()