No artigo anterior, o conceito de protótipo foi introduzido e a relação entre os três bons amigos do construtor, objeto de protótipo e instância em JavaScript: cada construtor possui um "guardião" - o objeto de protótipo, e também existe uma "posição" do construtor no coração do protótipo. Os dois estão apaixonados, mas o exemplo está "secretamente apaixonado" pelo objeto do protótipo, e ela também mantém a posição do objeto de protótipo em seu coração.
O Javascript em si não é um idioma orientado a objetos, mas um idioma baseado em objetos. Para aqueles que estão acostumados a outros idiomas OO, é um pouco desconfortável a princípio, porque não existe um conceito de "classe" aqui, ou não há distinção entre "classe" e "instância", muito menos a diferença entre "classe pai" e "subclasse". Então, como essas pilhas de objetos no JavaScript são vinculadas dessa maneira?
Felizmente, o JavaScript forneceu um método de implementação de "herança" no início de seu design. Antes de entender a "herança", agora entenderemos o conceito de cadeias de protótipo.
Cadeia de protótipo
Sabemos que os protótipos têm um ponteiro para o construtor. E se fizermos com que o protótipo subclasse objeto igual a outra instância da nova superclass ()? Neste momento, o objeto de protótipo da subclasse contém um ponteiro para o protótipo da superclasse, e o protótipo de superclasse também contém um ponteiro para o construtor de superclasse. . . Dessa maneira, uma cadeia de protótipos é formada.
O código específico é o seguinte:
function superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma garota!"; } função subclass () {this.subname = "Sua irmã"; } Subclass.prototype = new superclass (); Subclass.prototype.subSaywhat = function () {return this.subname + ": i`ma linda garota"; } var sub = new Subclass (); console.log (sub.Saywhat ()); // Mulheres: I'ma Girl!Use a cadeia de protótipos para obter herança
A partir do código acima, podemos ver que a subclasse herda as propriedades e métodos da superclasse. Essa implementação herdada está atribuindo a instância da superclasse ao objeto protótipo da subclasse. Dessa maneira, o protótipo objeto da subclasse é substituído por uma instância de superclasse, com todas as suas propriedades e métodos e também com um ponteiro para o objeto de protótipo da superclasse.
Há algumas coisas que precisam receber atenção ao implementar a herança usando cadeias de protótipo:
Preste atenção às mudanças no construtor após a herança. Aqui, o construtor de sub -superclass porque o protótipo de subclasse aponta para a Superclass. Ao entender a cadeia do protótipo, não ignore o objeto padrão no final, e é por isso que podemos usar métodos internos, como a toque em todos os objetos.
Ao implementar a herança através da cadeia de protótipos, você não pode usar a definição literal do método do protótipo, porque isso reescreverá o objeto Prototype (também introduzido no artigo anterior):
function superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma garota!"; } função subclass () {this.subname = "Sua irmã"; } Subclass.prototype = new superclass (); Subclass.prototype = {// O objeto de protótipo é substituído aqui porque os atributos e métodos da superclasse não podem ser herdados subssaywhat: function () {return this.subname + ": i`ma linda garota"; }} var sub = new Subclass (); console.log (sub.SayWhat ()); // typeError: indefinido não é uma funçãoUm problema com o compartilhamento de instância. Ao explicar o protótipo e o construtor anteriormente, introduzimos uma vez que os protótipos contendo atributos do tipo referência serão compartilhados por todas as instâncias. Da mesma forma, as propriedades do tipo de referência no protótipo de "classe pai" também serão compartilhadas no protótipo. Quando modificamos os atributos do tipo de referência da "classe pai" através da herança do protótipo, todas as outras instâncias herdadas do protótipo serão afetadas. Isso não apenas desperdiça recursos, mas também um fenômeno que não queremos ver:
function superclass () {this.name = "mulheres"; this.bra = ["a", "b"]; } função subclass () {this.subname = "Sua irmã"; } Subclass.prototype = new superclass (); var sub1 = new Subclass (); sub1.name = "man"; sub1.bra.push ("c"); console.log (sub1.name); // man Console.log (sub1.bra); // ["a", "b", "c"] var sub2 = new subclass (); console.log (sub1.name); // woman console.log (sub2.bra); // ["a", "b", "c"]Nota: Adicione um elemento à matriz aqui, todas as instâncias herdadas da Superclass serão afetadas, mas se você modificar o atributo de nome, ela não afetará outras instâncias, porque a matriz é um tipo de referência e o nome é um tipo primitivo.
Como resolver o problema do compartilhamento de instância? Vamos continuar olhando para baixo ...
Herança clássica (roubo de construtor)
Como introduzimos que raramente usamos protótipos para definir objetos sozinhos, no desenvolvimento real, raramente usamos as cadeias de protótipo. Para resolver o problema de compartilhar tipos de referência, os desenvolvedores de JavaScript introduziram o padrão de herança clássica (algumas pessoas chamam a herança do construtor emprestado). Sua implementação é muito simples de chamar construtores de supertipo em construtores de subtipo. Precisamos usar a função Call () ou Aplicar () fornecida por JavaScript, vamos dar uma olhada no exemplo:
function superclass () {this.name = "mulheres"; this.bra = ["a", "b"];} função subclass () {this.subname = "sua irmã"; // atribui o escopo da superclasse ao construtor atual para implementar a herança de superclass.call (this);} var sub1 = new Subclass (); sub1.bra.push ("c"); console.log (sub1.bra); // ["a", "b", "c"] var2 = novo Subclass (); console.log (sub2.bra); // ["a", "b"]Superclass.call (this); Essa frase significa que o trabalho de inicialização do construtor de superclasse é chamado no ambiente da subclasse (contexto), para que cada instância tenha sua própria cópia do atributo BRA, que não terá efeito um no outro.
No entanto, esse método de implementação ainda não é perfeito. Como o construtor é introduzido, também enfrentamos o problema com o construtor mencionado no artigo anterior: se houver uma definição de método no construtor, haverá uma referência de função separada para nenhuma das instâncias. Nosso objetivo é compartilhar esse método e os métodos que definimos no protótipo do supertipo não podem ser chamados na instância do subtipo:
function superclass () {this.name = "mulheres"; this.bra = ["a", "b"]; } Superclass.prototype.saywhat = function () {console.log ("hello"); } função subclass () {this.subname = "Sua irmã"; Superclass.call (this); } var sub1 = new Subclass (); console.log (sub1.saywhat ()); // typeError: indefinido não é uma funçãoSe você leu o artigo anterior sobre objetos e construtores de protótipo, já deve saber a resposta para resolver esse problema, ou seja, siga a rotina do artigo anterior e use "combinação de soco"!
Herança combinada
A herança combinada é uma maneira de combinar as vantagens da cadeia de protótipos e do construtor e combiná -las para alcançar a herança. Simplificando, é usar a cadeia de protótipo para herdar atributos e métodos e usar construtores emprestados para implementar a herança de atributos de instância. Isso não apenas resolve o problema do compartilhamento de atributos de instância, mas também permite que os atributos e métodos do super-tipo sejam herdados:
function superclass () {this.name = "mulheres"; this.bra = ["a", "b"]; } Superclass.prototype.saywhat = function () {console.log ("hello"); } função subclass () {this.subname = "Sua irmã"; Superclass.call (this); // a segunda chamada para superclass} subclass.prototype = new superclass (); // a primeira chamada para superclass var sub1 = new subclass (); console.log (sub1.SayWhat ()); //oláO método de herança de combinação também é a maneira mais usada para implementar a herança no desenvolvimento real. Neste ponto, ele pode atender às suas necessidades reais de desenvolvimento, mas a busca pela perfeição das pessoas é infinita, então inevitavelmente haverá alguém "encontrar" sobre esse padrão: seu padrão chamou o construtor do Super-Type duas vezes! Duas vezes. . . Você fez isso? Esta amplificação é 100 vezes a perda de desempenho?
A refutação mais poderosa é encontrar uma solução, mas felizmente o desenvolvedor encontrou a melhor solução para esse problema:
Herança de combinação parasita
Antes de introduzir esse método de herança, primeiro entendemos o conceito de construtor parasitário. O construtor parasitário é semelhante ao padrão de fábrica mencionado acima. Sua idéia é definir uma função comum. Esta função é usada especificamente para lidar com a criação de um objeto. Após a conclusão da criação, ela retorna este objeto. Esta função é muito semelhante a um construtor, mas o construtor não retorna um valor:
função gf (nome, sutiã) {var obj = new Object (); obj.name = nome; obj.bra = sutiã; obj.saywhat = function () {console.log (this.name); } retornar obj;} var gf1 = new gf ("bingbing", "c ++"); console.log (gf1.saywhat ()); // bingbingA implementação da herança parasitária é semelhante ao construtor parasitário. Ele cria uma função de "fábrica" que não depende de tipos específicos, lida especificamente com o processo de herança do objeto e, em seguida, retorna a instância do objeto herdado. Felizmente, isso não exige que implementemos nós mesmos. Dao Ge (Douglas) há muito nos forneceu um método de implementação:
função objeto (obj) {função f () {} f.prototype = obj; retornar novo f ();} var superclass = {name: "bingbing", sutiã: "c ++"} var subclass = objeto (superclass); console.log (subclass.name); // bingbingUm construtor simples é fornecido em uma função pública e, em seguida, uma instância do objeto passada é atribuída ao protótipo objeto do construtor e, finalmente, retornar uma instância do construtor é muito simples, mas a eficácia é muito boa, não é? Este método é posteriormente chamado de "herança do protótipo", e a herança parasitária é alcançada com base no protótipo, aprimorando as propriedades personalizadas do objeto:
function BuildObj (obj) {var o = objeto (obj); o.saywhat = function () {console.log ("hello"); } retornar o;} var superclass = {name: "bingbing", sutiã: "c ++"} var gf = buildObj (superclass); gf.saywhat (); // OláA herança parasitária também enfrenta o problema da reutilização de funções em protótipos, então as pessoas começaram a montar os blocos de construção novamente, e nasceu a herança da combinação parasitária, com o objetivo de resolver o problema de chamar o construtor do tipo pai ao especificar um protótipo de subtipo e ao mesmo tempo, para maximizar o reuso da função. Com base nos métodos de implementação básica acima, são os seguintes:
// Os parâmetros são dois construtores função HeritoBJ (sub, sup) {// Implementar a herança da instância e obter uma cópia do supertype var proto = object (sup.prototype); // Respecificar o atributo construtor da instância proto proto.constructor = sub; // atribui o objeto criado ao protótipo do subtipo sub.prototype = proto;} função superclass () {this.name = "women"; this.bra = ["a", "b"];} superclass.prototype.saywhat = function () {console.log ("hello");} função subclass () {this.name = "women"; this.bra = ["a", "b"];} superclass.prototype.saywhat = function () {console.log ("hello");} função subclass () {this.subname = "Sua irmã"; Superclass.call (this);} heroTobj (subclasse, superclass); var sub1 = new subclass (); console.log (sub1.saywhat ()); //oláEssa implementação evita duas chamadas para supertipo e também salva propriedades desnecessárias no subclass.prototype e também mantém a cadeia de protótipo. Nesse ponto, a jornada de herança acabou realmente acabada, e essa implementação também se tornou o método de implementação de herança mais ideal! A controvérsia das pessoas sobre a herança de JavaScript ainda continua. Alguns advogados OO, e outros se opõem a fazer esforços desnecessários em JavaScript para realizar as características do OO. E se for, pelo menos eu tenho um entendimento mais profundo!