JavaScript: Class.method vs. Class.prototype.method


499

Qual é a diferença entre as duas declarações a seguir?

Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }

Tudo bem pensar na primeira declaração como uma declaração de um método estático e na segunda declaração como uma declaração de um método de instância?

Respostas:


696

Sim, a primeira função não tem relação com uma instância de objeto dessa função construtora , você pode considerá-la como um 'método estático' .

No JavaScript, as funções são objetos de primeira classe , o que significa que você pode tratá-las como qualquer objeto; nesse caso, você está apenas adicionando uma propriedade ao objeto de função .

A segunda função, à medida que você estende o protótipo da função construtora, estará disponível para todas as instâncias de objeto criadas com a newpalavra - chave e o contexto nessa função (a thispalavra - chave) se referirá à instância real do objeto em que você a chama.

Considere este exemplo:

// constructor function
function MyClass () {
  var privateVariable; // private member only available within the constructor fn

  this.privilegedMethod = function () { // it can access private members
    //..
  };
}

// A 'static method', it's just like a normal function 
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};

MyClass.prototype.publicMethod = function () {
  // the 'this' keyword refers to the object instance
  // you can access only 'privileged' and 'public' members
};

var myObj = new MyClass(); // new object instance

myObj.publicMethod();
MyClass.staticMethod();

1
Mas por que Function.prototype.method == Function.method?
Raghavendra

1
@Raghavendra não é
Zorgatone

1
@Menda Seu link está morto #
Eugen Sunic

19

Ao criar mais de uma instância do MyClass, você ainda terá apenas uma instância do publicMethod na memória, mas no caso do privilegedMethod, você acabará criando muitas instâncias e o staticMethod não terá relação com uma instância do objeto.

É por isso que os protótipos economizam memória.

Além disso, se você alterar as propriedades do objeto pai, se a propriedade correspondente da criança não tiver sido alterada, ela será atualizada.


15

Para aprendizes visuais, ao definir a função sem .prototype

ExampleClass = function(){};
ExampleClass.method = function(customString){
             console.log((customString !== undefined)? 
                          customString : 
                          "called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
    // >> error! `someInstance.method is not a function`  

Com o mesmo código, se .prototypefor adicionado,

ExampleClass.prototype.method = function(customString){
             console.log((customString !== undefined)? 
                          customString : 
                          "called from func def.");}
ExampleClass.method();  
      // > error! `ExampleClass.method is not a function.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
                 // > output: `Called from instance`

Para tornar mais claro,

ExampleClass = function(){};
ExampleClass.directM = function(){}  //M for method
ExampleClass.prototype.protoM = function(){}

var instanceOfExample = new ExampleClass();

ExampleClass.directM();      works
instanceOfExample.directM();   x Error!

ExampleClass.protoM();     x Error!
instanceOfExample.protoM();   works

**** Observe o exemplo acima, someInstance.method () não será executado, pois
ExampleClass.method () causa erro e a execução não pode continuar.
Mas, para fins ilustrativos e de fácil compreensão, mantive essa sequência. ****

Resultados gerados a partir de chrome developer console& Clique no link jsbin acima para percorrer o código. Alternar seção comentada com +JS Bin

ctrl/


15

Sim, o primeiro static methodtambém é chamado class method, enquanto o segundo é um instance method.

Considere os seguintes exemplos, para entendê-lo com mais detalhes.

No ES5

function Person(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
}

Person.isPerson = function(obj) {
   return obj.constructor === Person;
}

Person.prototype.sayHi = function() {
   return "Hi " + this.firstName;
}

No código acima, isPersoné um método estático, enquanto sayHié um método de instância de Person.

Abaixo, é como criar um objeto a partir do Personconstrutor.

var aminu = new Person("Aminu", "Abubakar");

Usando o método estático isPerson.

Person.isPerson(aminu); // will return true

Usando o método de instância sayHi.

aminu.sayHi(); // will return "Hi Aminu"

No ES6

class Person {
   constructor(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
   }

   static isPerson(obj) {
      return obj.constructor === Person;
   }

   sayHi() {
      return `Hi ${this.firstName}`;
   }
}

Veja como a staticpalavra-chave foi usada para declarar o método estático isPerson.

Para criar um objeto de Personclasse.

const aminu = new Person("Aminu", "Abubakar");

Usando o método estático isPerson.

Person.isPerson(aminu); // will return true

Usando o método de instância sayHi.

aminu.sayHi(); // will return "Hi Aminu"

NOTA: Ambos os exemplos são essencialmente os mesmos, o JavaScript permanece uma linguagem sem classe. O classintroduzido no ES6 é principalmente um açúcar sintático sobre o modelo de herança baseado em protótipo existente.


"No ES6", você descreve apenas um açúcar de sintaxe. Esta não é a maneira "ES2015" (todos parem de usar o ES6 e use o termo adequado ES2015). É simplesmente outra maneira de fazê-lo e, na minha opinião, o caminho incorreto.
K - A toxicidade no SO está crescendo.

2
@KarlMorrison Aminu não escreveu "maneira de fazê-lo", você acabou de escrever isso e fez uma exceção. Seu ponto de vista pode ser justo sobre o ES6 vs o ES2015, mas nas conversas as pessoas geralmente recorrem a uma convenção mais curta de eficiência, então acho que removê-la da escrita não é possível ou com certeza é aconselhável.
Wuliwong

Obrigado pela parte ES6 da sua resposta; isso esclarece muito, especialmente quando combinado com as 2 respostas "públicas + privilegiadas" acima. No entanto, estou completamente confuso por você obj.constructor === Personser trueexemplo ... O que? Como pode o construtor de uma instância de ===classe a própria classe ...? (Isso é como dizer que um subconjunto de um conjunto é o próprio conjunto, etc ...)
Andrew

Ohhh ... isso é tudo o que dizer então que, literalmente, o construtor é tudo o que realmente é uma classe JS no final do dia? Todo o resto é empilhado no construtor ou totalmente construtivo estático isolado da classe, exceto pelo nome / conceito (e como um "isto" implícito sendo disponibilizado obviamente)? (Portanto, o que eu pensei que era um subconjunto do conjunto não era realmente um subconjunto.)
Andrew
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.