herança

Entendendo herança no JavaScript

O javascript é uma linguagem orientada a objeto porém diferente da maioria das linguagens orientada a objeto em que a herança é baseada em classes no javascript é baseado em prototypes. Você pode construir uma "emulação" de classes com JavaScript através de funções.
JavaScript não tem classe apenas objetos, isto é, você instacia novos objetos a partir de outros obejtos.

prototype faz referência a todas as propriedades e métodos herdados do objeto pai, incluindo seu prototype.

Ele pode ser pensado como uma lista ligada referenciando o elemento anterior na lista.Um dos principais aspectos da herança de prototype é que se você alterar uma propriedade herdada ou método de um objeto, 
essas mudanças vai refletir nos seus filhos mesmo depois de terem sido criados. Isso porque os filhos remetem as propriedades e métodos do pai.

Outro aspecto importante é que você pode mudar o prototype de qualquer um dos objetos globais. Porém é perigoso, e considerado uma má prática pois objetos globais são definidos por um padrão, por isso fazer que se eles comportarem de outra forma pode ser perigoso e perder seus beneficios.

Vamos há um simples exemplo:

 

ECMAScript 5 introduziu o novo método: Object.create(). Invocando este método podemos criar novos objetos.O prototype destes novos objetos é o primeiro argumento do método:

Este método utiliza um objeto como parâmetro e retorna um novo objeto, e elimina a necessidade de acessar diretamente a propriedade prototype.

var pai = { 
	a: 10,
	b: 50,
	//Object.prototype = {a:10, b:50}
	//pai ---> Object.prototype ---> null
	metodo: function(message) {
		console.log('Eu sou o '+ message);		
	},
};

var filho = Object.create(pai); 
//filho ---> pai ---> Object.prototype ---> null
var neto = Object.create(filho);
//neto ---> filho ---> pai ---> Object.prototype ---> null
console.log(neto.a);   	//vai imprimir 10
pai.a = 20;		//Atribui 20
console.log(neto.a);   	//vai imprimir 20
console.log(filho.a);  	//vai imprimir 20
filho.metodo('filho');  //"Eu sou o pai".
neto.metodo('neto');    //"Eu sou o neto"

 

Para familiarizarmos melhor, vamos fazer uma analogia:

 

var pai = { //Objeto
	a: 10,
	b: 50
};

var filho = Object.create(pai); //criando um objeto e herdando tudo do Objeto pai

 

Seria o mesmo que em Java da seguinte forma:

Class pai { //Classe
    a: 10,
    b: 50
}
Class filho extends pai { //Herdando tudo da classe pai.

}

 

Enquanto a propriedade prototype é usada pela linguagem para construir a cadeia prototype, ainda é possível atribuir qualquer valor a ela. No entanto, os primitivos serão simplemesmente ignorados quando atribuídos como prototype.

 

function Foo() {}
Foo.prototype = 1; // sem efeito
Foo.prototype      //1
Foo.prototype.1    //lança um erro.

 

Este é um exemplo trivial, mas vamos falar de performance.O tempo de pesquisa de propriedades em que estão no alto da cadeia de prototype pode ter um impacto negativo no desempenho, e isso pode ser significativo no seu código em que o desempenho é algo critico. Além disso, tentar acessar propriedades inexistentes irá sempre percorrer a cadeia prototype inteira.

Uma característica inconveniente que é frequentemente usada é estender Object.prototype ou um dos outros contruídos em prototype.
Essa técnica é chamada monkey patching e quebra o encapsulamento. Enquanto é usada por frameworks populares como o Prototype, ainda não há uma boa razão para sobrecarregar tipos internos (built-in types) com funcionalidades adicionais não padrão.
A única boa razão para estender um prototype interno é para transportar as funcionalidades para novos motores JavaScript; por exemplo, Array.forEach.


Agora vamos há um exemplo em que adicionamos propriedades ao objeto pai e disponibilizando aos filhos.

 

var pai = { 
	a: 10,
	b: 50
};

var filho = Object.create(pai);
var neto = Object.create(filho);

console.log(neto.a);   //vai imprimir 10
pai.c = 'Olá';	       //Adiciona a propriedade c e atribui o valor "Olá"
console.log(neto.c);   //vai imprimir 'Olá'
console.log(filho.c);  //vai imprimir 'Olá'

 

Como visto os filhos herdam as propriedades do pai mesmo já tendo sido criados antes das modificações.

 

Como a definição de ojeto global : Object.create() só foi definida apartir do ECMAScript 5, temos uma maneira de contornar a situação para navegadores que não suportam:

 

if (typeof Object.create !== 'function') {
	Object.create = function (o) {
		function F() {}
		F.prototype = o;
		return new F();
	};
}


Concluindo, é essencial entender o modelo de herança baseado em prototype antes de escrever códigos complexos que fazem uso dela. Além disso, esteja ciente do tamanho das cadeias prototype no seu código e quebre elas, se necessário, para evitar possíveis problemas de desempenho ou seja prudende e use Closures ao invés de prototypes. Outro ponto, os prototypes nativos nunca devem ser estendidos a menos que seja por uma questão de compatibilidade com novas funcionalidades JavaScript.