introduzir
Neste artigo, consideramos vários aspectos da programação orientada a objetos no ECMAScript (embora esse tópico tenha sido discutido em muitos artigos antes). Veremos essas questões mais de uma perspectiva teórica. Em particular, consideraremos o algoritmo de criação do objeto, como a relação entre objetos (incluindo relacionamentos básicos - herança) também está disponível na discussão (espero que parte da ambiguidade conceitual da OOP em JavaScript seja removida).
Texto em inglês original: http://dmitrysohnikov.com/ecmascript/chapter-7-1-oop-general-torory/
Introdução, paradigma e pensamentos
Antes de realizar a análise técnica do OOP no ECMAScript, é necessário dominar algumas características básicas da OOP e esclarecer os principais conceitos da introdução.
O ECMAScript suporta vários métodos de programação, incluindo estruturados, orientados a objetos, funcionais, imperativos etc. e, em alguns casos, também suporta programação orientada a aspectos; Mas este artigo discute a programação orientada a objetos, por isso daremos a definição de programação orientada a objetos no ECMAScript:
O ECMAScript é uma linguagem de programação orientada a objetos com base na implementação do protótipo.
Existem muitas diferenças entre o OOP baseado em protótipo e os métodos estáticos baseados em classes. Vamos dar uma olhada em suas diferenças detalhadas diretas.
Baseado em classes e baseado em protótipo
Observe que o ponto importante na frase anterior já apontou - baseia -se inteiramente em classes estáticas. Com a palavra "estática", entendemos objetos estáticos e classes estáticas, fortemente digitadas (embora não sejam necessárias).
Em relação a essa situação, muitos documentos do fórum enfatizam que essa é a principal razão pela qual eles se opõem à comparação de "classes com protótipos" em JavaScript. Embora suas diferenças de implementação (como Python e Ruby baseadas em classes dinâmicas) não se opõem demais ao ponto (algumas condições são escritas, embora existam algumas diferenças nas idéias, o JavaScript não é tão alternativo), mas sua oposição são classes estáticas e protótipos dinâmicos (estatísticas + classes vs. dinâmicas + protótipes). Para ser preciso, uma classe estática (como C ++, Java) e seus subordinados e mecanismos de definição de método podem nos permitir ver a diferença exata entre ele e a implementação do protótipo.
Mas vamos listá -los um por um. Vamos considerar os princípios gerais e os principais conceitos desses paradigmas.
Baseado em classes estáticas
Em um modelo baseado em classe, existe um conceito sobre classes e instâncias. Instâncias de classes também são frequentemente nomeadas objetos ou paradigmas.
Classes e objetos
Uma classe representa uma abstração de uma instância (ou seja, um objeto). Isso é um pouco como matemática, mas chamamos de tipo ou classificação.
Por exemplo (os exemplos aqui e abaixo são pseudo-código):
A cópia do código é a seguinte:
C = classe {a, b, c} // classe C, incluindo recursos a, b, c
As características da instância são: atributos (descrição do objeto) e métodos (atividade do objeto). As próprias características também podem ser consideradas objetos: isto é, se o atributo é gravável, configurável, estabelecido (getter/setter), etc. Portanto, os objetos armazenam estado (ou seja, os valores específicos de todos os atributos descritos em uma classe) e as classes definem estruturas estritamente invariantes (propriedades) e os comportamentos estritamente invariáveis.
A cópia do código é a seguinte:
C = classe {a, b, c, método1, método2}
c1 = {a: 10, b: 20, c: 30} // classe C é uma instância: objeto с1
c2 = {a: 50, b: 60, c: 70} // classe C é uma instância: objeto с2, que tem seu próprio estado (ou seja, valor de atributo)
Herança hierárquica
Para melhorar a reutilização do código, as classes podem ser estendidas de uma para outra, adicionando informações adicionais. Esse mecanismo é chamado de herança (hierárquica).
A cópia do código é a seguinte:
D = classe estende c = {d, e} // {a, b, c, d, e}
d1 = {a: 10, b: 20, c: 30, d: 40, e: 50}
Ao ligar para a parte na instância da classe, a classe nativa geralmente procurará o método agora. Se não for encontrado, vá para a classe pai para pesquisar diretamente. Se não for encontrado, vá para a classe pai da classe pai para pesquisar (por exemplo, na cadeia de herança estrita). Se for encontrado que a parte superior da herança não foi encontrada, o resultado é: o objeto não tem comportamento semelhante e não pode obter o resultado.
A cópia do código é a seguinte:
d1.method1 () // d.method1 (não) -> c.method1 (sim)
d1.method5 () // d.method5 (no) -> c.method5 (não) -> sem resultado
Comparados aos métodos de herança que não são copiados para uma subclasse, as propriedades são sempre complicadas em subclasses. Podemos ver que a subclasse D herda da classe pai C: atributos A, B, C são copiados e a estrutura de D é {a, b, c, d, e}}. No entanto, o método {Method1, Method2} não copia o passado, mas herda o passado. Portanto, ou seja, se uma classe muito profunda possui algumas propriedades que os objetos não precisam, a subclasse também possui essas propriedades.
Conceitos -chave baseados em classes
Portanto, temos os seguintes conceitos -chave:
1. Antes de criar um objeto, a classe deve ser declarada. Primeiro, é necessário definir sua classe.
2. Portanto, o objeto será criado a partir de uma classe abstraída em sua própria "pictográfica e similaridade" (estrutura e comportamento)
3. O método é tratado através de uma cadeia de herança estrita, direta e imutável.
4. A subclasse contém todos os atributos na cadeia de herança (mesmo que alguns dos atributos não sejam exigidos pela subclasse);
5. Crie uma instância de classe. A classe não pode (devido a um modelo estático) para alterar as características (atributos ou métodos) de sua instância;
6. As instâncias (por causa de modelos estáticos estritas) não podem ter comportamentos ou atributos adicionais, exceto os comportamentos e atributos declarados na classe correspondente à instância.
Vamos ver como substituir o modelo OOP em JavaScript, que é o OOP baseado em protótipo que recomendamos.
Com base no protótipo
O conceito básico aqui são objetos mutáveis dinâmicos. A transformação (conversão completa, incluindo não apenas valores, mas também recursos) está diretamente relacionada à linguagem dinâmica. Objetos como o seguinte podem armazenar independentemente todas as suas propriedades (propriedades, métodos) sem classes.
A cópia do código é a seguinte:
objeto = {a: 10, b: 20, c: 30, método: fn};
objeto.a; // 10
object.c; // 30
object.Method ();
Além disso, devido à dinâmica, eles podem mudar facilmente (adicione, excluir, modificar) seus próprios recursos:
A cópia do código é a seguinte:
object.method5 = function () {...}; // Adicione um novo método
object.d = 40; // Adicione um novo atributo "D"
excluir object.c; // Excluir atributo "с"
object.a = 100; // modifica o atributo "а"
// O resultado é: objeto: {a: 100, b: 20, d: 40, método: fn, método5: fn};
Ou seja, ao atribuir, se algum recurso não existir, ele será criado e a atribuição será inicializada com ele e, se existir, será atualizada.
Nesse caso, a reutilização do código não é implementada estendendo a classe (observe que não dissemos que a classe não pode ser alterada porque não há conceito de classe aqui), mas é implementado por protótipos.
Um protótipo é um objeto usado como uma cópia primitiva de outros objetos, ou se alguns objetos não tiverem suas próprias características necessárias, o protótipo pode ser usado como delegado a esses objetos e atuar como um objeto auxiliar.
Com base na delegação
Qualquer objeto pode ser usado como um objeto de protótipo para outro objeto, porque o objeto pode alterar facilmente sua dinâmica de protótipo em tempo de execução.
Observe que estamos atualmente considerando uma introdução em vez de uma implementação concreta e, quando discutirmos a implementação concreta no ECMAScript, veremos algumas de suas próprias características.
Exemplo (pseudocode):
A cópia do código é a seguinte:
x = {a: 10, b: 20};
y = {a: 40, c: 50};
y. [[protótipo]] = x; // x é o protótipo de y
ya; // 40, suas próprias características
yc; // 50, também é sua própria característica
yb; // 20 Obtenha do protótipo: yb (não) -> y. [[Prototipo]]. B (sim): 20
excluir você; // exclua o seu próprio "а"
ya; // 10 Obtenha -o do protótipo
z = {a: 100, e: 50}
y. [[protótipo]] = z; // modifica o protótipo de y para z
ya; // 100 Obtenha do protótipo Z
ye // 50, também obtido do protótipo Z
zq = 200 // Adicione um novo atributo ao protótipo
yq // modificação também se aplica a y
Este exemplo mostra as funções e mecanismos importantes do protótipo como atributos de objeto auxiliar, assim como você precisa de seus próprios atributos. Comparados com seus próprios atributos, esses atributos são atributos delegados. Esse mecanismo é chamado de delegado, e o modelo de protótipo baseado nele é um protótipo delegado (ou um protótipo baseado em delegado). O mecanismo de referência é chamado de envio de informações para um objeto. Se o objeto não receber uma resposta, ele será delegado ao protótipo para encontrá -lo (exigindo que ele tente responder à mensagem).
A reutilização do código neste caso é chamada de herança baseada em delegado ou herança baseada em protótipo. Como qualquer objeto pode ser considerado um protótipo, ou seja, o protótipo também pode ter seu próprio protótipo. Esses protótipos são conectados para formar a chamada cadeia de protótipo. As cadeias também são hierárquicas em classes estáticas, mas podem ser facilmente reorganizadas, alterando hierarquias e estruturas.
A cópia do código é a seguinte:
x = {a: 10}
y = {b: 20}
y. [[protótipo]] = x
z = {c: 30}
z. [[protótipo]] = y
ZA // 10
// ZA Encontrado na cadeia de protótipos:
// za (não) ->
// z. [[prototype]]. A (não) ->
// z. [[prototype]]. [[prototype]]. A (sim): 10
Se um objeto e sua cadeia de protótipo não puderem responder ao envio de mensagens, o objeto poderá ativar o sinal do sistema correspondente, que pode ser processado por outros delegados na cadeia de protótipo.
Esse sinal do sistema está disponível em muitas implementações, incluindo sistemas baseados em classes dinâmicas: #DoesNoTundand em SmallTalk, Method_missing em Ruby; __getAtattr__ em python, __call em php; e __NosuchMethod__ implementação no ecmascript, etc.
Exemplo (implementação do ECMAScript do Spidermonkey):
A cópia do código é a seguinte:
var object = {
// Pegue o sinal do sistema que não pode responder às mensagens
__nosuchMethod__: function (nome, args) {
alerta ([nome, args]);
if (nome == 'teste') {
retornar '.test () método é tratado';
}
retornar delegado [nome] .Apply (isto, args);
}
};
var delegate = {
quadrado: função (a) {
retornar a * a;
}
};
alerta (object.square (10)); // 100
alerta (object.test ()); // .test () o método é tratado
Ou seja, com base na implementação de classes estáticas, quando a mensagem não pode ser respondida, a conclusão é que o objeto atual não possui as características necessárias, mas se você tentar obtê -lo da cadeia de protótipos, ainda poderá obter o resultado ou o objeto tiver a característica após uma série de mudanças.
Em relação ao ECMAScript, a implementação específica é: usando um protótipo baseado em delegado. No entanto, como veremos de especificações e implementações, eles também têm suas próprias características.
Modelo concatenativo
Honestamente, é necessário dizer algo sobre outra situação (não usada no ECMAScript o mais rápido possível): essa situação quando o protótipo é complexo de outros objetos à substituição original de objetos nativos. Nesse caso, a reutilização do código é uma cópia real (clone) de um objeto em vez de um delegado durante o estágio de criação de objetos. Este protótipo é chamado de protótipo concatenativo. Copiar as propriedades de todos os protótipos de um objeto pode alterar completamente suas propriedades e métodos e, como um protótipo, você também pode alterar a si mesmo (em um modelo baseado em delegado, essa alteração não alterará o comportamento dos objetos existentes, mas mudará suas características de protótipo). A vantagem desse método é que ele pode reduzir o tempo de agendamento e delegação, enquanto a desvantagem é que ele usa memória.
Tipo de pato
De volta ao objeto de mudança dinamicamente fraco, em comparação com o modelo estático baseado em classe, é necessário verificar se ele pode fazer essas coisas e que tipo (classe) o objeto possui, mas se pode estar relacionado à mensagem correspondente (ou seja, se ele tem a capacidade de fazê-lo após a verificação é necessária).
Por exemplo:
A cópia do código é a seguinte:
// em um modelo estático
if (instância do objeto de someclass) {
// Alguns comportamentos estão funcionando
}
// em implementação dinâmica
// não importa que tipo de objeto é neste momento
// porque mutações, tipos e características podem ser alterados livremente.
// se objetos importantes podem responder às mensagens de teste
if (isfunction (object.test)) // ecmascript
se object.Rand_To? (: teste) // ruby
Se hasattr (objeto, 'teste'): // python
Isso é chamado de tipo de doca. Ou seja, os objetos podem ser identificados por suas próprias características ao verificar, em vez da localização dos objetos na hierarquia ou pertencem a qualquer tipo específico.
Conceitos -chave baseados em protótipos
Vamos dar uma olhada nas principais características deste método:
1. O conceito básico é o objeto
2. Os objetos são completamente dinâmicos e mutáveis (em teoria, podem ser completamente transformados de um tipo para outro)
3. Os objetos não têm classes rigorosas que descrevem sua própria estrutura e comportamento, e os objetos não precisam de classes.
4. Os objetos não têm classes de aula, mas podem ter protótipos. Se eles não puderem responder a mensagens, poderão delegar a protótipos.
5. O protótipo do objeto pode ser alterado a qualquer momento durante o tempo de execução;
6. Em um modelo baseado em delegado, a alteração das características do protótipo afetará todos os objetos relacionados ao protótipo;
7. No modelo de protótipo concatenativo, o protótipo é a cópia original clonada de outros objetos e se torna uma cópia completamente independente. A transformação das características do protótipo não afetará o objeto clonado dele.
8. Se a mensagem não puder ser respondida, seu chamador poderá tomar medidas adicionais (por exemplo, alterar o agendamento)
9. A falha do objeto pode ser determinada não pelo seu nível e a qual classe eles pertencem, mas pelas características atuais.
No entanto, há outro modelo que devemos considerar também.
Com base em classes dinâmicas
Acreditamos que a diferença "Classe vs Prototype" mostrada no exemplo acima não é tão importante nesse modelo dinâmico baseado em classe (especialmente se a cadeia de protótipo não for alterada, ainda é necessário considerar uma classe estática para uma distinção mais precisa). Como exemplo, também pode ser usado em Python ou Ruby (ou outros idiomas semelhantes). Todos esses idiomas usam paradigmas dinâmicos baseados em classes. No entanto, em alguns aspectos, podemos ver certas funções implementadas com base em protótipos.
No exemplo a seguir, podemos ver que, apenas com base no protótipo delegado, podemos amplificar uma classe (protótipo), afetando assim todos os objetos relacionados a essa classe, também podemos alterar dinamicamente a classe desse objeto em tempo de execução (fornecendo um novo objeto para o delegado), etc.
A cópia do código é a seguinte:
# Python
Classe A (objeto):
def __init __ (eu, a):
self.a = a
Def Square (self):
retornar self.a * self.a
a = a (10) # Crie uma instância
Imprimir (AA) # 10
AB = 20 # Forneça uma nova propriedade para a classe
Imprima (AB) # 20 Você pode acessá -lo na instância "A"
a = 30 # Crie uma propriedade própria
Imprimir (AB) # 30
del ab # exclua seus próprios atributos
Imprimir (AB) # 20 - Retire -o da classe novamente (protótipo)
# Como um modelo baseado em protótipo
# Pode alterar o protótipo de um objeto em tempo de execução
Classe B (objeto): # vazio classe B
passar
b = b () # b instância
b .__ classe__ = uma classe de mudança dinamicamente (protótipo)
BA = 10 # Crie um novo atributo
Imprimir (B.Square ()) # 100 - O método da classe A está disponível no momento
# Pode exibir referências em classes excluídas
del a
del b
# Mas o objeto ainda tem referências implícitas, e esses métodos ainda estão disponíveis
Imprimir (B.Square ()) # 100
# Mas você não pode mudar a classe neste momento
# Esta é a característica da implementação
b .__ classe__ = Dict # Erro
A implementação no Ruby é semelhante: também usa classes completamente dinâmicas (a propósito, na versão atual do Python, em comparação com o rubi e o ecmascript, as classes amplificando (protótipos) não podem ser feitas), podemos alterar completamente as características de um objeto (ou classe) (adicionar métodos/atribuições para a classe e as alterações afetarão os objetos existentes), mas a classificação é que a classe é uma das alterações e as alterações dos objetos existentes.
No entanto, este artigo não é especificamente para Python e Ruby, por isso não diremos muito, vamos continuar discutindo o próprio Ecmascript.
Mas antes disso, temos que olhar para o "açúcar sintético" encontrado em alguns OOPS, porque muitos artigos anteriores sobre JavaScript geralmente cobrem esses problemas.
A única frase errada que precisa ser observada nesta seção é: "JavaScript não é uma classe, possui um protótipo e pode substituir uma classe". É muito necessário saber que nem todas as implementações baseadas em classe são completamente diferentes. Mesmo que possamos dizer "JavaScript é diferente", também é necessário considerar (além do conceito de "classes") que existem outros recursos relacionados.
Outros recursos de várias implementações de OOP
Nesta seção, introduzimos brevemente outros recursos e várias implementações de OOP sobre a reutilização do código, incluindo implementações de OOP no ECMAScript. O motivo é que a aparência anterior da implementação da OOP em JavaScript tem algumas restrições de pensamento habitual. O único requisito principal é que ele seja provado tecnicamente e ideologicamente. Não se pode dizer que você não descobriu a função de açúcar de sintaxe em outras implementações de OOP e não tem idéia de que o JavaScript não é uma linguagem Pura OOP, o que está errado.
Polimórfico
No ECMAScript, existem vários polimorfismos nos quais os objetos significam.
Por exemplo, uma função pode ser aplicada a diferentes objetos, assim como as propriedades de um objeto nativo (porque esse valor é determinado ao entrar no contexto de execução):
A cópia do código é a seguinte:
function test () {
alerta ([this.a, this.b]);
}
test.call ({a: 10, b: 20}); // 10, 20
test.call ({a: 100, b: 200}); // 100, 200
var a = 1;
var b = 2;
teste(); // 1, 2
No entanto, existem exceções: Date.prototype.gettime (), de acordo com o padrão, sempre deve haver um objeto de data, caso contrário, uma exceção será lançada.
A cópia do código é a seguinte:
alert (date.prototype.gettime.call (new date ())); // tempo
alert (date.prototype.gettime.call (new string ('' '))); // typeError
O chamado polimorfismo de parâmetros ao definir uma função é equivalente a todos os tipos de dados, mas aceita apenas parâmetros polimórficos (como o método de classificação .sort de uma matriz e seus parâmetros - a função de classificação do polimorfismo). A propósito, o exemplo acima também pode ser considerado como um polimorfismo de parâmetros.
O método no protótipo pode ser definido como vazio e todos os objetos criados devem ser redefinidos (implementados) o método (ou seja, "uma interface (assinatura), múltiplas implementações").
O polimorfismo está relacionado ao tipo de pato mencionado acima: ou seja, o tipo de objeto e sua posição na hierarquia não são tão importantes, mas podem ser facilmente aceitos se tiver todas as características necessárias (ou seja, a interface geral é importante e a implementação pode ser variada).
Pacote
Muitas vezes, existem visões erradas sobre o encapsulamento. Nesta seção, discutiremos alguns açúcares sintáticos nas implementações de OOP - ou seja, modificadores bem conhecidos: nesse caso, discutiremos algumas implementações de OOP "açúcares" convenientes - modificadores conhecidos: privados, protegidos e públicos (ou nível de acesso de objetos ou modificadores de acesso).
Aqui, gostaria de lembrá -lo sobre o principal objetivo do encapsulamento: o encapsulamento é uma adição abstrata, em vez de selecionar um "hacker malicioso" oculto que escreve algo diretamente em sua classe.
Este é um grande erro: use oculto para se esconder.
Os níveis de acesso (privados, protegidos e públicos), para a conveniência da programação, foram implementados em muitos objetos (realmente muito convenientes de açúcar de sintaxe) e descrevem e construíram o sistema de maneira mais abstrata.
Estes podem ser vistos em algumas implementações (como Python e Ruby já mencionadas). Por um lado (no Python), essas propriedades _privatadas _ Protegidas (através da especificação de nome do sublinhamento) não são acessíveis de fora. O Python, por outro lado, pode ser acessado de fora através de regras especiais (_className__field_name).
A cópia do código é a seguinte:
Classe A (objeto):
def __init __ (self):
self.public = 10
self .__ privado = 20
def get_private (self):
Retornar auto .__ privado
# fora:
a = a () # exemplo de um
Impressão (A.Public) # ok, 30
print (a.get_private ()) # ok, 20
Imprimir (A .__ privado) # falhou porque só pode estar disponível em um
# Mas em Python, regras especiais podem ser acessadas
Imprimir (A._A__Private) # ok, 20
Em Ruby: por um lado, ele tem a capacidade de definir as características de privado e protegido e, por outro lado, também existem métodos especiais (como Instance_variable_get, instance_variable_set, send, etc.) para obter dados encapsulados.
A cópia do código é a seguinte:
Classe A.
def Initialize
@A = 10
fim
def public_method
private_method (20)
fim
Privado
def private_method (b)
Retornar @A + B
fim
fim
a = a.new # nova instância
a.public_method # ok, 30
AA # falhou, @A - é uma variável de instância privada
# "private_method" é privado e só pode ser acessado na classe A
A.Private_method # Erro
# Mas há um nome de método de metadados especiais que pode obter os dados
a.send (: private_method, 20) # ok, 30
A.Instance_variable_get (:@a) # ok, 10
O principal motivo é o encapsulamento (observe que não uso dados "ocultos") que o programador deseja obter. Se esses dados forem alterados incorretamente de alguma forma ou houver algum erro, toda a responsabilidade é o programador, mas não simplesmente "espelando" ou "alterar certos campos casualmente". Mas se isso acontecer com frequência, é um mau hábito e estilo de programação, porque geralmente é uma API comum "conversar" com objetos.
Repita, o objetivo básico do encapsulamento é abstraí -lo do usuário de dados auxiliares, em vez de impedir que os hackers ocultem dados. Mais grave, o encapsulamento não é usar privado para modificar os dados para alcançar o objetivo da segurança do software.
Encapsulamento de objetos auxiliares (local), fornecemos viabilidade para alterações de comportamento das interfaces públicas com custo mínimo, localização e mudanças preditivas, que é exatamente o objetivo do encapsulamento.
Além disso, o objetivo importante do método do setter é abstrair cálculos complexos. Por exemplo, element.innerhtml setter - declaração de resumo - "O html dentro deste elemento agora é o seguinte", e a função do setter no atributo inerhtml será difícil de calcular e verificar. Nesse caso, o problema envolve principalmente a abstração, mas o encapsulamento pode acontecer.
O conceito de encapsulamento não está apenas relacionado ao OOP. Por exemplo, pode ser uma função simples que encapsula todos os tipos de cálculos para que seja abstrato (não é necessário que o usuário saiba, por exemplo, como a função Math.Round (......) é implementada, o usuário simplesmente chama). É um encapsulamento, observe que eu não disse que é "privado, protegido e público".
A versão atual da especificação ECMAScript não define modificadores privados, protegidos e públicos.
No entanto, na prática, é possível ver algo chamado "imitar o encapsulamento JS". Geralmente, o objetivo deste contexto deve ser usado (como regra, o próprio construtor). Infelizmente, muitas vezes a implementação dessa "imitação" pode ser feita, e os programadores podem produzir entidades pseudo-absolutamente absolutamente de abstrato para definir o "método getter/setter" (eu digo novamente, está errado):
A cópia do código é a seguinte:
função a () {
var _a; // "privado" a
this.geta = função _geta () {
retornar _a;
};
this.seta = função _seta (a) {
_a = a;
};
}
var a = novo a ();
a.seta (10);
alerta (A._A); // indefinido, "privado"
alerta (a.geta ()); // 10
Portanto, todo mundo entende que, para cada objeto criado, o método Geta/Seta também é criado, que também é o motivo do aumento da memória (em comparação com a definição de protótipo). Embora, em teoria, o objeto possa ser otimizado no primeiro caso.
Além disso, alguns artigos de JavaScript geralmente mencionam o conceito de "método privado". NOTA: O padrão ECMA-262-3 não define nenhum conceito de "método privado".
No entanto, em alguns casos, ele pode ser criado no construtor porque JS é uma linguagem ideológica - os objetos são completamente mutáveis e têm características únicas (sob certas condições no construtor, alguns objetos podem obter métodos adicionais, enquanto outros não podem).
Além disso, no JavaScript, se o encapsulamento ainda for mal interpretado como uma maneira de impedir que hackers maliciosos escrevam certos valores automaticamente no lugar do uso do método do setter, os chamados "ocultos" e "privados" não estão "escondidos". Algumas implementações podem obter valores na cadeia de escopo relevante (e todos os objetos variáveis correspondentes) chamando o contexto da função de avaliação (que pode ser testada no Spidermonkey 1.7).
A cópia do código é a seguinte:
Eval ('_ a = 100', A.Geta); // ou a.seta, porque "_a" métodos em [[escopo]]
A.Geta (); // 100
Como alternativa, na implementação, permita o acesso direto ao objeto ativo (como o rinoceronte), acessando as propriedades correspondentes do objeto, o valor da variável interna pode ser alterado:
A cópia do código é a seguinte:
// rinoceronte
var foo = (function () {
var x = 10; // "privado"
Return function () {
impressão (x);
};
}) ();
foo (); // 10
foo .__ pai __. x = 20;
foo (); // 20
Às vezes, em JavaScript, o objetivo dos dados "privados" e "protegidos" é alcançado por variáveis sublinhadas (mas comparadas ao Python, essa é apenas uma especificação de nomenclatura):
var _myPrivatedata = 'testString';
É frequentemente usado para incluir o contexto de execução com colchetes, mas para dados auxiliares reais, não possui associação direta com objetos e é apenas conveniente abstrair de APIs externas:
A cópia do código é a seguinte:
(function () {
// inicialize o contexto
}) ();
Herança múltipla
A multi-aprimoramento é um açúcar sintático muito conveniente para melhorias de reutilização de código (se podemos herdar uma classe por vez, por que não podemos herdar 10 por vez?). No entanto, devido a algumas deficiências em herança múltipla, ela não se tornou popular na implementação.
O ECMAScript não suporta herança múltipla (ou seja, apenas um objeto pode ser usado como um protótipo direto), embora os idiomas de autoprogramação de seus ancestrais tenham esses recursos. Mas em algumas implementações (como Spidermonkey) usando __nosuchmethod__ pode ser usado para gerenciar a programação e delegar para substituir as cadeias de protótipo.
Mixins
Mixins é uma maneira conveniente de reutilizar o código. Mixins foi recomendado como substituto para a herança múltipla. Todos esses elementos independentes podem ser misturados com qualquer objeto para estender sua funcionalidade (para que os objetos também possam misturar várias mixins). A especificação ECMA-262-3 não define o conceito de "mixins", mas de acordo com a definição e o ecmascript do Mixins possui objetos mutáveis dinâmicos, portanto, não há obstáculo para simplesmente expandir os recursos usando o Mixins.
Exemplos típicos:
A cópia do código é a seguinte:
// ajudante para aumento
Object.extend = function (destino, fonte) {
para (propriedade na fonte) if (fonte.HasownProperty (Propriedade)) {
destino [propriedade] = fonte [propriedade];
}
destino de retorno;
};
var x = {a: 10, b: 20};
var y = {c: 30, d: 40};
Object.Extend (x, y); // Misture y em x
alerta ([XA, XB, XC, XD]); 10, 20, 30, 40
Observe que peguei essas definições ("Mixin", "Mix") nas citações mencionadas na ECMA-262-3. Não existe esse conceito na especificação e não é uma mistura, mas uma maneira comumente usada de estender objetos por meio de novos recursos. (O conceito de mixins no rubi é oficialmente definido. Mixin cria uma referência ao módulo em vez de simplesmente copiar todos os atributos do módulo para outro módulo - de fato: criar um objeto adicional (protótipo) para o delegado).
Características
Traços e mixins têm conceitos semelhantes, mas possui muitas funções (por definição, porque os mixins podem ser aplicados para que não possam conter estados porque tem o potencial de causar conflitos de nomeação). De acordo com o ECMAScript, características e mixins seguem os mesmos princípios, portanto a especificação não define o conceito de "características".
interface
As interfaces implementadas em alguns Oops são semelhantes às Mixins e Traços. No entanto, em comparação com Mixins e Traits, uma interface aplica a classe de implementação para implementar seu comportamento de assinatura de método.
As interfaces podem ser consideradas classes abstratas. No entanto, em comparação com as classes abstratas (os métodos nas classes abstratas podem implementar apenas parte deles, e a outra parte ainda é definida como assinaturas), a herança pode ser apenas uma única classe base de herança, mas pode herdar várias interfaces. É por isso que as interfaces (múltiplas mistas) podem ser consideradas uma alternativa à herança múltipla.
O padrão ECMA-262-3 não define o conceito de "interface" nem o conceito de "classe abstrata". No entanto, como imitação, ele pode ser implementado por um objeto que o método "vazio" (ou uma exceção lançada em um método vazio, dizendo ao desenvolvedor que esse método precisa ser implementado).
Combinação de objetos
A combinação de objetos também é uma das técnicas de reutilização de código dinâmico. As combinações de objetos diferem da alta herança de flexibilidade, que implementa um delegado dinâmico e variável. E isso também é baseado no protótipo principal. Além dos protótipos mutáveis dinâmicos, o objeto pode ser um objeto agregado delegado (crie uma combinação como resultado - a agregação) e enviar mensagens ainda mais ao objeto e delegar ao delegado. Isso pode ser mais de dois delegados, porque sua natureza dinâmica determina que pode ser alterada em tempo de execução.
O exemplo __nosuchMethod__ mencionado é assim, mas também nos permite mostrar como usar os delegados explicitamente:
Por exemplo:
A cópia do código é a seguinte:
var _delegate = {
foo: function () {
alert ('_ delegate.foo');
}
};
var agregado = {
delegado: _delegate,
foo: function () {
Retorne this.delegate.foo.call (this);
}
};
agregate.foo (); // delegate.foo
agregate.delegate = {
foo: function () {
alerta ('foo de novo delegado');
}
};
agregate.foo (); // foo do novo delegado
Esse relacionamento de objeto é chamado "Has-A", enquanto a integração é o relacionamento de "IS-A".
Devido à falta de combinações de exibição (flexibilidade em comparação com a herança), também é bom adicionar código intermediário.
AOP apresenta
Como uma função orientada a aspectos, os decoradores de funções podem ser usados. A especificação ECMA-262-3 não possui um conceito claramente definido de "decoradores de funções" (em oposição ao Python, a palavra é oficialmente definida em Python). No entanto, as funções com parâmetros funcionais são decorativas e ativadas de certa forma (aplicando as chamadas sugestões):
O exemplo mais simples de um decorador:
A cópia do código é a seguinte:
função checkDecorator (OriginalFunction) {
Return function () {
if (foobar! = 'teste') {
alerta ('parâmetro errado');
retornar falso;
}
return originalfunction ();
};
}
function test () {
alerta ('função de teste');
}
var testwithCheck = checkDecorator (teste);
var foobar = false;
teste(); // 'Função de teste'
testWithCheck (); // 'parâmetro errado'
foobar = 'teste';
teste(); // 'Função de teste'
testWithCheck (); // 'Função de teste'
para concluir
Neste artigo, resolvemos a introdução ao OOP (espero que essas informações tenham sido úteis para você) e, no próximo capítulo, continuaremos a implementar o ECMAScript na programação orientada a objetos.