Quando usar a programação prototípica em JavaScript

15

Passei um bom tempo desenvolvendo widgets simples para projetos da seguinte maneira:

var project = project || {};

(function() {

  project.elements = {
    prop1: val1,
    prop2: val2
  }

  project.method1 = function(val) {
    // Do this
  }

  project.method2 = function(val) {
    // Do that
  }

  project.init = function() {
    project.method1(project.elements.prop1)
    project.method2(project.elements.prop2)
  }
})()

project.init();

Mas comecei a mudar meu formato para o seguinte:

function Project() {
  this.elements = {
    prop1: val1,
    prop2: val2
  }

  this.method_one(this.elements.prop1);
  this.method_two(this.elements.prop2);
}

Project.prototype.method_one = function (val) {
  // 
};

Project.prototype.method_two = function (val) {
  //
};

new Project();

É verdade que estes são exemplos tolos, por isso não se enrole no eixo. Mas qual é a diferença funcional e quando devo escolher um ou outro?

JDillon522
fonte
Acho que seu primeiro exemplo de código não funcionará, pois o projeto não sai do escopo. Para usar herança em JS, use prototipagem. O primeiro exemplo não pretende estender o projeto, portanto a reutilização pode ser limitada.
Aitch
Sim, acidentalmente coloquei a projectdeclaração dentro da função. Atualizada.
JDillon522
o mesmo aqui. o primeiro exemplo é estático, o segundo é orientado a objetos.
Aitch
1
Se você estiver usando apenas UMA instância do objeto, não haverá motivo para usar a definição prototípica. E o que você parece com a necessidade - é um formato de módulo - não há muitas opções para isso, veja: addyosmani.com/writing-modular-js
C69
1
Primeiro um para padrão Singleton O segundo uma para não-Singleton (quando uma classe pode ter muitos objectos)
Kokizzu

Respostas:

6

A primeira diferença pode ser resumida como: thisrefere-se à Instância da classe. prototyperefere-se à definição .

Digamos que temos a seguinte classe:

var Flight = function ( number ) { this.number = number; };

Então, aqui estamos nos anexando this.numbera todas as instâncias da classe, e faz sentido, porque todas Flightdevem ter seu próprio número de voo.

var flightOne = new Flight( "ABC" );
var flightTwo = new Flight( "XYZ" );

Por outro lado, prototypedefine uma única propriedade que pode ser acessada por todas as instâncias.

Agora, se quisermos obter o número do voo, podemos simplesmente escrever o seguinte trecho e todas as nossas instâncias receberão uma referência a esse objeto recém-protegido.

Flight.prototype.getNumber = function () { return this.number; };

A segunda diferença é sobre a maneira como o JavaScript procura uma propriedade de um objeto. Quando você está procurando Object.whatever, o JavaScript vai até o objeto Objeto principal (o objeto que todo o resto herdou) e, assim que encontrar uma correspondência, ele retornará ou o chamará.

Mas isso só acontece para as propriedades prototipadas. Portanto, se você tiver algum lugar nas camadas mais altas this.whatever, o JavaScript não o considerará uma correspondência e continuará a pesquisa.

Vamos ver como isso acontece na realidade.

Primeira nota que [quase] tudo são objetos em JavaScript. Tente o seguinte:

typeof null

Agora vamos ver o que está dentro de um Object(observe Maiúsculas Oe .no final). Nas Ferramentas do desenvolvedor do Google Chrome, quando você digita, .você obterá uma lista de propriedades disponíveis dentro desse objeto específico.

Object.

Agora faça o mesmo para Function:

Function.

Você pode perceber o namemétodo. Vá em frente e vamos ver o que acontece:

Object.name
Function.name

Agora vamos criar uma função:

var myFunc = function () {};

E vamos ver se temos o namemétodo aqui também:

myFunc.name

Você deve ter uma string vazia, mas tudo bem. Você não deve receber um erro ou exceção.

Agora, vamos adicionar algo a esse deus Objecte ver se o encontramos em outros lugares também?

Object.prototype.test = "Okay!";

E lá vai você:

Object.prototype.test
Function.prototype.test
myFunc.prototype.test

Em todos os casos, você deve ver "Okay!".

Com relação aos prós e contras de cada método, você pode considerar a prototipagem como uma maneira "mais eficiente" de fazer as coisas, pois mantém uma referência em todas as instâncias, em vez de copiar toda a propriedade em cada objeto. Por outro lado, é um exemplo de acoplamento apertado, que é um grande não-não até que você possa realmente justificar o motivo. thisé bem mais complicado, pois é relevante para o contexto. Você pode encontrar muitos bons recursos gratuitamente na internet.

Dito isso, ambas as formas são apenas ferramentas de linguagem e realmente depende de você e do problema que você está tentando resolver para escolher o que melhor se encaixa.

Se você precisar ter uma propriedade relevante para todas as instâncias de uma classe, use this. Se você precisar ter uma propriedade para funcionar da mesma maneira em todas as instâncias, use prototype.

Atualizar

Em relação aos snippets de amostra, o primeiro é um exemplo de Singleton , portanto, faz sentido usar thisno corpo do objeto. Você também pode melhorar seu exemplo, tornando-o modular assim (e nem sempre é necessário usá-lo this).

/* Assuming it will run in a web browser */
(function (window) {
    window.myApp = {
        ...
    }
})( window );

/* And in other pages ... */
(function (myApp) {
    myApp.Module = {
        ...
    }
})( myApp );

/* And if you prefer Encapsulation */
(function (myApp) {
    myApp.Module = {
         "foo": "Foo",
         "bar": function ( string ) {
             return string;
         },
         return {
             "foor": foo,
             "bar": bar
         }
    }
})( myApp );

Seu segundo snippet não faz muito sentido, porque primeiro você está usando thise depois está tentando hackear prototype, o que não funciona porque thistem prioridade prototype. Não sei ao certo quais eram suas expectativas com relação a esse pedaço de código e como ele estava funcionando, mas eu recomendo que você o refatorasse.

Atualizar

Para elaborar a thisprecedência prototype, posso mostrar um exemplo e explicar como ele pode ser explicado, mas não tenho nenhum recurso externo para fazer backup.

O exemplo é muito simples:

var myClass = function () { this.foo = "Foo"; };
myClass.prototype.foo = "nice try!";
myClass.prototype.bar = "Bar";

var obj = new myClass;
obj.foo;     // Still contains "Foo" ...
obj.bar;     // Contains "Bar" as expected

A explicação é, como sabemos, thisé relevante para o contexto. Portanto, ele não existirá até que o contexto esteja pronto. Quando o contexto está pronto? Quando a nova instância está sendo criada! Você deve adivinhar o resto agora! Isso significa que, embora exista uma prototypedefinição, mas thisfaz mais sentido ter precedência, porque é tudo sobre a nova instância sendo criada naquele momento.

53777A
fonte
Esta é uma resposta parcialmente épica impressionante! Você pode editá-lo e entrar em mais detalhes comparando e contrastando os dois métodos? Seus dois primeiros parágrafos me confundem sobre a qual método de design você está se referindo. Seu exercício é excelente! Nunca pensei muito no poder da prototipagem assim. Você também pode dar um exemplo de uso de caso semi detalhado de quando usar cada método? Obrigado novamente, isso é ótimo.
JDillon522
@ JDillon522 Fico feliz em ouvir isso. Vou tentar atualizá-lo nas próximas horas!
53777A 17/02/2015
@ JDillon522 Acabei de fazer uma atualização rápida. Espero que fique mais claro desta vez.
53777A 17/02/2015
@ JDillon522 Um pouco mais sobre seus trechos amostra ...
53777A
Belo exemplo singleton. Então, usei thisno exemplo de protótipo bobo porque thisse refere a suas próprias propriedades, incluindo seus métodos. Eu não aprendo o melhor lendo sobre código, mas mais olhando para código. ( Parte da MDN , Object Playground (incrível) e algumas outras). Você pode apontar para algo que explica o que você quer dizer com " thistem prioridade sobre prototype"? Eu gostaria de investigar mais.
JDillon522