JavaScript é uma linguagem orientada a objetos. Há um ditado muito clássico em JavaScript: tudo é um objeto. Como é orientado a objetos, existem três características principais da orientação a objetos: encapsulamento, herança e polimorfismo. Aqui falamos sobre a herança do JavaScript, e falaremos sobre os outros dois mais tarde.
A herança do JavaScript não é a mesma do que de C ++. A herança de C ++ é baseada na classe, enquanto a herança de JavaScript é baseada em protótipos.
Agora o problema está aqui.
Qual é o protótipo? Para protótipos, podemos nos referir às classes em C ++ e salvar as propriedades e métodos do objeto. Por exemplo, vamos escrever um objeto simples
A cópia do código é a seguinte:
função animal (nome) {
this.name = nome;
}
Animal.prototype.setName = function (nome) {
this.name = nome;
}
var animal = novo animal ("Wangwang");
Podemos ver que este é um animal de objeto, que possui um nome de atributo e um conjunto de métodos. Observe que, uma vez modificado o protótipo, como adicionar um método, todas as instâncias do objeto compartilharão esse método. Por exemplo
A cópia do código é a seguinte:
função animal (nome) {
this.name = nome;
}
var animal = novo animal ("Wangwang");
Neste momento, o animal só tem o atributo de nome. Se adicionarmos uma frase,
A cópia do código é a seguinte:
Animal.prototype.setName = function (nome) {
this.name = nome;
}
No momento, o Animal também terá um método SetName.
Cópia da herança - começando de um objeto vazio, sabemos que, entre os tipos básicos de JS, existe um tipo chamado objeto, e sua instância mais básica é um objeto vazio, ou seja, a instância gerada chamando o novo objeto () diretamente ou declarado com o {} literal. Um objeto vazio é um "objeto limpo", com apenas propriedades e métodos predefinidos, enquanto todos os outros objetos são herdados de objetos vazios; portanto, todos os objetos têm essas propriedades e métodos predefinidos. O protótipo é na verdade uma instância de objeto. O significado de um protótipo é: se o construtor tiver um protótipo Objeto A, as instâncias criadas pelo construtor devem ser copiadas de A. Como a instância é copiada do objeto A, a instância deve herdar todas as propriedades, métodos e outras propriedades de A. Então, como a replicação é implementada? Método 1: Construa a cópia que cada instância construída é copiada do protótipo, e a nova instância ocupa o mesmo espaço de memória que o protótipo. Embora isso torne o Obj1 e o Obj2 "completamente consistente" com seus protótipos, também é muito não econômico - o consumo de espaço de memória aumentará rapidamente. Como mostrado na imagem:
Método 2: Copie a redação desta estratégia vem da tecnologia de um sistema de decepção consistente: copie a redação. Um exemplo típico desse tipo de fraude é a Dynamic Link Library (DDL) no sistema operacional, cuja área de memória é sempre copiada na gravação. Como mostrado na imagem:
Só precisamos especificar no sistema que o OBJ1 e o OBJ2 são equivalentes aos seus protótipos; portanto, ao ler, precisamos seguir as instruções para ler o protótipo. Quando precisamos escrever as propriedades de um objeto (como OBJ2), copiamos uma imagem de protótipo e fazemos com que as operações subsequentes apontem para a imagem. Como mostrado na imagem:
A vantagem desse método é que não precisamos de muita sobrecarga de memória ao criar instâncias e ler atributos. Usamos apenas algum código para alocar memória ao escrever pela primeira vez e trazer algum código e sobrecarga de memória. Mas não haverá essa sobrecarga desde então, porque a eficiência de acessar imagens e acessar protótipos é consistente. No entanto, para sistemas que geralmente escrevem operações, esse método não é econômico que o método anterior. Método 3: Reading Traversal Este método gira a granularidade da cópia do protótipo para o membro. Este método é caracterizado por copiar as informações do membro apenas na imagem da instância ao escrever um membro de uma instância. Ao escrever propriedades do objeto, por exemplo (obj2.Value = 10), um valor de atributo nomeado valor será gerado e colocado na lista de membros do objeto Obj2. Olhe para a foto:
Pode -se descobrir que o OBJ2 ainda é uma referência ao protótipo e nenhuma instância de objetos do mesmo tamanho que o protótipo foi criada durante a operação. Dessa forma, as operações de gravação não levam a uma grande quantidade de alocação de memória; portanto, o uso da memória parece econômico. A diferença é que o Obj2 (e todas as instâncias de objetos) precisa manter uma lista de membros. Esta lista de membros segue duas regras: é garantido que seja acessado primeiro quando lido. Se nenhum atributo for especificado no objeto, tente atravessar toda a cadeia de protótipo do objeto até que o protótipo esteja vazio ou a propriedade seja encontrada. A cadeia de protótipo será discutida mais tarde. Obviamente, entre os três métodos, o Read Traversal é o melhor desempenho. Portanto, a herança do protótipo de JavaScript é lida de travessia. As pessoas construtoras familiarizadas com o C ++ serão definitivamente confusas depois de ler o código do objeto superior. É fácil de entender sem a palavra -chave da classe, afinal, existem palavras -chave de função, mas as palavras -chave são diferentes. Mas e o construtor? De fato, o JavaScript também possui construtores semelhantes, mas eles são chamados de construtores. Ao usar o novo operador, o construtor foi chamado e isso é vinculado como um objeto. Por exemplo, usamos o seguinte código
A cópia do código é a seguinte:
var animal = animal ("Wangwang");
Animal será indefinido. Algumas pessoas dirão que nenhum valor de retorno é obviamente indefinido. Então, se você alterar a definição de objeto animal:
A cópia do código é a seguinte:
função animal (nome) {
this.name = nome;
devolver isso;
}
Adivinha que animal é agora?
Nesse momento, o animal se tornou janela. A diferença é que ela expande a janela, para que a janela tenha um atributo de nome. Isso ocorre porque isso padrão é a janela, ou seja, a variável de nível superior sem especificá-la. Somente chamando a nova palavra -chave pode o construtor pode ser chamado corretamente. Então, como evitar que a nova palavra -chave seja perdida pela pessoa que a usa? Podemos fazer algumas pequenas alterações:
A cópia do código é a seguinte:
função animal (nome) {
if (! (esta instância do animal)) {
retornar novo animal (nome);
}
this.name = nome;
}
Isso será infalível. O construtor também tem outro uso, indicando a qual objeto a instância pertence. Podemos usar a instância do para julgar, mas a instância de retornará verdadeira a objetos ancestrais e objetos reais ao herdar, para que não seja muito adequado. Quando o construtor é chamado de novo, ele aponta para o objeto atual por padrão.
A cópia do código é a seguinte:
console.log (animal.prototype.Constructor === Animal); // verdadeiro
Podemos pensar de maneira diferente: o protótipo não tem valor no início da função, e a implementação pode ser a seguinte lógica
// SET __Proto__ é um membro interno da função, get_protoTyoe () é o seu método
A cópia do código é a seguinte:
var __proto__ = nulo;
function get_prototype () {
if (! __ proto__) {
__proto__ = new Object ();
__proto __. Construtor = this;
}
retornar __proto__;
}
Esse benefício é que evita criar uma instância de objeto para cada função declarada, economizando sobrecarga. O construtor pode ser modificado, que será discutido posteriormente. Acredito que todo mundo sabe o que a herança é baseada em protótipos, para que eles não mostram seu limite inferior ao QI.
Existem vários tipos de herança de JS, aqui estão dois tipos
1. Método 1 Este método é mais comumente usado e tem melhor segurança. Vamos definir dois objetos primeiro
A cópia do código é a seguinte:
função animal (nome) {
this.name = nome;
}
cão função (idade) {
this.age = idade;
}
var cão = novo cachorro (2);
É muito simples de construir herança, aponte o protótipo do objeto infantil para a instância do objeto pai (observe que é uma instância, não um objeto)
A cópia do código é a seguinte:
Dog.Prototype = New Animal ("Wangwang");
Neste momento, o cachorro terá dois atributos, nome e idade. E se a instância do operador for usada para cachorro
A cópia do código é a seguinte:
console.log (Instância de cães de animal); // verdadeiro
console.log (Instância de cães de cachorro); // false
Isso alcança a herança, mas há um pequeno problema
A cópia do código é a seguinte:
console.log (Dog.prototype.Constructor === Animal); // verdadeiro
console.log (Dog.prototype.Constructor === Dog); // false
Você pode ver que o objeto apontado pelo construtor mudou, o que não atende ao nosso objetivo. Não podemos determinar a quem a instância a que nova pertence. Portanto, podemos adicionar uma frase:
A cópia do código é a seguinte:
Cachorro.prototype.Constructor = DOG;
Vamos dar uma olhada novamente:
A cópia do código é a seguinte:
console.log (Instância de cães de animal); // false
console.log (Instância de cães de cachorro); // verdadeiro
feito. Este método é um link na manutenção da cadeia de protótipos, que será explicada em detalhes abaixo. 2. Método 2 Esse método tem seus benefícios e desvantagens, mas as desvantagens superam os benefícios. Veja o código primeiro
A cópia do código é a seguinte:
<pre nome = "code"> função animal (nome) {
this.name = nome;
}
Animal.prototype.setName = function (nome) {
this.name = nome;
}
cão função (idade) {
this.age = idade;
}
Dog.Prototype = Animal.prototype;
Isso permite a cópia do protótipo.
A vantagem desse método é que ele não requer instanciação de objetos (em comparação com o método 1), economizando recursos. As desvantagens também são óbvias. Além do mesmo problema que acima, ou seja, o construtor aponta para o objeto pai, ele pode copiar apenas as propriedades e métodos declarados pelo objeto pai com protótipo. Ou seja, no código acima, o atributo de nome do objeto animal não pode ser copiado, mas o método SetName pode ser copiado. O mais fatal é que qualquer modificação no protótipo do objeto filho afetará o protótipo do objeto pai, ou seja, as instâncias declaradas por ambos os objetos serão afetadas. Portanto, esse método não é recomendado.
Cadeia de protótipo
Qualquer pessoa que tenha escrito sobre herança sabe que a herança pode ser herdada de vários níveis. E em JS, isso forma a cadeia de protótipos. O artigo acima também mencionou a cadeia de protótipo muitas vezes, então, qual é a cadeia de protótipo? Uma instância deve pelo menos ter um atributo proto apontando para o protótipo, que é a base do sistema de objetos no JavaScript. No entanto, essa propriedade é invisível e a chamamos de "cadeia de protótipos internos" para distingui -la da "cadeia de protótipo do construtor" composta pelo protótipo do construtor (ou seja, o que geralmente chamamos de "cadeia de protótipos"). Vamos primeiro construir uma relação de herança simples de acordo com o código acima:
A cópia do código é a seguinte:
função animal (nome) {
this.name = nome;
}
cão função (idade) {
this.age = idade;
}
var animal = novo animal ("Wangwang");
Cachorro.prototype = animal;
var cão = novo cachorro (2);
Como lembrete, como mencionado anteriormente, todos os objetos herdam objetos vazios. Então, construímos uma cadeia de protótipo:
Podemos ver que o protótipo do objeto filho aponta para a instância do objeto pai, formando a cadeia do protótipo do construtor. O objeto proto interno da instância infantil também é uma instância apontando para o objeto pai, formando uma cadeia de protótipos internos. Quando precisamos encontrar uma propriedade, o código é semelhante a
A cópia do código é a seguinte:
função getattrfromobj (att, obj) {
if (typeof (obj) === "objeto") {
var proto = obj;
while (proto) {
if (proto.hasownProperty (att)) {
retornar proto [att];
}
proto = proto .__ proto__;
}
}
retornar indefinido;
}
Neste exemplo, se procurarmos o atributo de nome em Dog, ele será pesquisado na lista de membros em Dog. Obviamente, não será encontrado, porque agora a lista de membros do cachorro é apenas a idade do item. Em seguida, continuará pesquisando ao longo da cadeia de protótipos, ou seja, a instância apontada por .proto, ou seja, em Animal, encontre o atributo de nome e retorne -o. Se a pesquisa for uma propriedade que não existe, quando não puder ser encontrada em animais, continuará pesquisando com .proto, encontre um objeto vazio e depois continuará pesquisando com .proto e o .proto do objeto vazio aponta para nulo, procurando por saída.
Manutenção de cadeias de protótipo, levantamos uma questão quando conversamos sobre herança de protótipo agora. Ao usar o método 10 para construir a herança, o construtor da instância do objeto filho aponta para o objeto pai. A vantagem disso é que podemos acessar a cadeia de protótipos através do atributo construtor, e as desvantagens também são óbvias. Um objeto cuja instância deve apontar para si mesma, ou seja,
A cópia do código é a seguinte:
(novo obj ()). prototype.constructor === obj;
Então, depois de reescrever as propriedades do protótipo, o construtor da instância gerado pelo objeto filho não aponta para si mesmo! Isso vai contra a intenção original do construtor. Nós mencionamos uma solução acima:
A cópia do código é a seguinte:
Dog.Prototype = New Animal ("Wangwang");
Cachorro.prototype.Constructor = DOG;
Parece que nada está errado. Mas, de fato, isso traz um novo problema, porque descobriremos que não podemos voltar à cadeia de protótipo, porque não podemos encontrar o objeto pai e o atributo .proto da cadeia de protótipo interno é inacessível. Portanto, o Spidermonkey fornece uma melhoria: adicione uma propriedade chamada __proto__ a qualquer objeto criado, que sempre aponta para o protótipo usado pelo construtor. Dessa maneira, qualquer modificação do construtor não afetará o valor de __proto__, tornando conveniente manter o construtor.
No entanto, existem mais dois problemas:
__proto__ é reescrito, o que significa que ainda existem riscos ao usá -lo
__proto__ é um processamento especial de Spidermonkey e não pode ser usado em outros motores (como o JScript).
Há outra maneira de manter as propriedades do construtor do protótipo e inicializar as propriedades do construtor da instância na função do construtor da subclasse.
O código é o seguinte: reescrever o objeto infantil
A cópia do código é a seguinte:
cão função (idade) {
this.Constructor = argumentos.callee;
this.age = idade;
}
Dog.Prototype = New Animal ("Wangwang");
Dessa maneira, o construtor de todos os casos de objetos filhos aponta corretamente para o objeto, enquanto o construtor do protótipo aponta para o objeto pai. Embora esse método seja relativamente ineficiente porque o atributo construtor deve ser reescrito toda vez que a instância for construída, não há dúvida de que esse método pode resolver efetivamente as contradições anteriores. O ES5 leva isso em consideração e resolve completamente este problema: object.getProTypeOf () pode ser usado a qualquer momento para obter o protótipo real de um objeto sem acessar o construtor ou manter a cadeia de protótipo externa. Portanto, como mencionado na seção anterior, podemos reescrever a seguinte:
A cópia do código é a seguinte:
função getattrfromobj (att, obj) {
if (typeof (obj) === "objeto") {
fazer {
var proto = object.getPrototypeof (cão);
if (proto [att]) {
retornar proto [att];
}
}
enquanto (proto);
}
retornar indefinido;
}
Obviamente, esse método só pode ser usado em navegadores que suportam ES5. Para ser compatibilidade com versões anteriores, ainda precisamos considerar o método anterior. Um método mais adequado é integrar e encapsular esses dois métodos. Acredito que os leitores são muito bons nisso, então não serei feiúra aqui.