JavaScript estende vs mixin

5

Depois de ler artigo Fluent JavaScript de Eric Elliott Eu estava e ainda me lembro sobre a maneira de brincar com protótipos de instância.

De um lado, você tem a herança extendendo ...

var B = function() {} ;
B.prototype = new A() ;

Ou a nova maneira de fazer isso ...

B.prototype = Object.create(A.prototype) ;

E do outro lado, você tem o mixin , criando uma mistura de vários protótipos que serão herdados em instâncias.

var B = function() {} ;
// Basic mixin from A
for (var m in A.prototype) B.prototype[m] = A.prototype[m] ;
  • Em seu artigo, Eric Elliott nos encoraja a evitar a extensão de tipos, mas a compor (mixin) tipos de componentes objetos / protótipos (veja também Stampit no github ). Eu concordo totalmente com isso.

  • No entanto, parece quebrar uma característica principal (e filosofia) em JavaScript: Modularidade . O que quero dizer é o fato de que a alteração de protótipos estará automaticamente disponível para todas as instâncias por referências.

Aqui está um exemplo básico da herança do protótipo:

var A = function() {} ;
A.prototype.fn = function() { return 1 ; } ;

var a = new A() ;
a.fn() ; // 1

A.prototype.fn = function() { return 2 ; } ;
a.fn() ; // 2

E agora com um protótipo estendido:

var B = function() {} ;
B.prototype = Object.create(A.prototype) ;

var b = new B() ;
b.fn() ; // 2, inherited from A.prototype

Ok, isso está funcionando bem, mas não podemos herdar vários protótipos; mais herança clássica é geralmente evitada.

A outra solução seria mixin então:

var D = function() {} ;
// Let's imagine a mixin function mixing D with some prototypes...
mixin(D, A.prototype, C.prototype, R.prototype) ;

var d = new D() ;
d.fn() ; // 2, inherited from A.prototype ? Let's see...

A.prototype.fn = function() { return 3 ; } ;
d.fn() ; // 2, what a shame...

De fato, o mixin apenas copia todas as funções de um protótipo para outro. Isso significa que não há mais referências entre esses tipos e, portanto, a modularidade parece quebrada aqui (também testada com Stampit).

Agora estou pensando em uma solução para manter referências aos objetos protótipos originais e não apenas às funções internas.

E as heranças em cascata?

  • Não é como D <- C <- B <- A , pois altera B e C e parece restritivo.

  • Mas como D <- (((p <- A) <- B) <- C) , onde p é um protótipo reservado apenas para D . Nem B nem C serão alterados, somente p será uma herança em cascata direta. D terá todos os métodos dos protótipos listados, e se quisermos herdar de outro, basta reiniciar o processo a partir de zero, como D <- ((((p <- A) <- B) <- C) <- R) .

O único inconveniente que vejo seria em torno de performances. Cada vez que adicionamos uma nova herança, teremos que reiniciar todo o processo: De fato, depois de herdarmos, adicionamos novas funções ao protótipo (ou sobrescrevemos algumas), e isso deve ser sempre no final do processo, assim após R herança:

  • Antes: D <- ((((p <- A) <- B) <- C) <- {}) , em que {} é o conjunto de funções novas / prioritárias.

  • Depois: D <- (((((p <- A) <- B) <- C) <- R) <- {}) e não ... <- {}) <- R) .

Seria interessante ter uma pequena biblioteca para isso? Eu estaria pronto para fazer isso, permitindo várias heranças de protótipos.

Então ... o que você acha disso tudo?

    
por Tot 18.09.2015 / 00:33
fonte

1 resposta

2

Com Stampit, não há intencionalmente nenhum link entre protótipos mixins e a fonte original, e isso é por design, porque alterar o protótipo de qualquer instância poderia então alterar o protótipo original.

No seu post, você parece pensar que isso é ruim, mas, na prática, a mutação de protótipos após a instanciação de objetos tem uma infinidade de efeitos colaterais que podem afetar significativamente a robustez do aplicativo (é muito mais difícil de entender quais alterações afetarão quais objetos, ou seja, você acha que está consertando um bug em suas animações de sprites, e de repente seu mecanismo de renderização de partículas quebra, porque eles compartilham um protótipo comum) e desempenho (alterar protótipos faz com que os mecanismos JS joguem fora todos suas otimizações de propriedades de objetos em cache, incluindo otimizações que afetam o desempenho de cada parte do código que usa qualquer objeto descendente .

Em outras palavras, o que você está tentando fazer é amplamente considerado um anti-padrão na comunidade JavaScript.

Outro ponto interessante: a nova especificação do carimbo permite que você passe uma instância para o carimbo. Se você fizer isso, o selo não alterará o protótipo original, e o protótipo original realmente aparecerá na cadeia de protótipos, o que significa que o que você está sugerindo será possível na próxima versão do Stampit - mas meu aviso é que alterar os protótipos após a instanciação é um anti-padrão ainda permanece.

    
por 22.09.2015 / 22:15
fonte