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

499

¿Cuál es la diferencia entre las siguientes dos declaraciones?

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

¿Está bien pensar en la primera declaración como una declaración de un método estático, y la segunda declaración como una declaración de un método de instancia?

postracional
fuente

Respuestas:

696

Sí, la primera función no tiene relación con una instancia de objeto de esa función constructora , puede considerarla como un 'método estático' .

En JavaScript, las funciones son objetos de primera clase , lo que significa que puede tratarlos como cualquier objeto, en este caso, solo está agregando una propiedad al objeto de función .

La segunda función, a medida que amplíe el prototipo de la función de constructor, estará disponible para todas las instancias de objeto creadas con la newpalabra clave, y el contexto dentro de esa función (la thispalabra clave) se referirá a la instancia de objeto real donde la llame.

Considere este ejemplo:

// 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();
CMS
fuente
1
Pero, ¿por qué Function.prototype.method == Function.method?
Raghavendra
1
@Raghavendra no lo es
Zorgatone
1
@Menda tu enlace está muerto
Eugen Sunic
19

Cuando crea más de una instancia de MyClass, solo tendrá una sola instancia de publicMethod en la memoria, pero en caso de privilegedMethod terminará creando muchas instancias y staticMethod no tiene relación con una instancia de objeto.

Es por eso que los prototipos ahorran memoria.

Además, si cambia las propiedades del objeto padre, si la propiedad correspondiente del niño no se ha cambiado, se actualizará.

usuario2440156
fuente
15

Para estudiantes visuales, al definir la función sin .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`  

Con el mismo código, si .prototypese agrega,

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 hacerlo más 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

**** Nota para el ejemplo anterior, someInstance.method () no se ejecutará, ya que
ExampleClass.method () causa un error y la ejecución no puede continuar.
Pero en aras de la ilustración y la fácil comprensión, he mantenido esta secuencia. ****

Resultados generados desde chrome developer console& Haga clic en el enlace jsbin de arriba para recorrer el código. Alternar la sección comentada con +JS Bin

ctrl/

SAm
fuente
15

Sí, el primero static methodtambién se llama class method, mientras que el segundo es un instance method.

Considere los siguientes ejemplos para comprenderlo con más detalle.

En 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;
}

En el código anterior, isPersones un método estático, mientras que sayHies un método de instancia de Person.

A continuación, se muestra cómo crear un objeto desde el Personconstructor.

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

Usando el método estático isPerson.

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

Usando el método de instancia sayHi.

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

En ES6

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

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

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

Mire cómo staticse utilizó la palabra clave para declarar el método estático isPerson.

Para crear un objeto de Personclase.

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

Usando el método estático isPerson.

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

Usando el método de instancia sayHi.

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

NOTA: Ambos ejemplos son esencialmente los mismos, JavaScript sigue siendo un lenguaje sin clases. El classintroducido en ES6 es principalmente un azúcar sintáctico sobre el modelo de herencia basado en prototipos existente.

Aminu Kano
fuente
"En ES6" usted describe una sintaxis de azúcar solamente. Esta no es la forma de hacerlo "ES2015" (por favor, todos dejen de usar ES6, use el término apropiado ES2015). Es simplemente otra forma de hacerlo y, en mi opinión, la forma incorrecta.
K - La toxicidad en SO está creciendo.
2
@KarlMorrison Aminu no escribió "forma de hacerlo", simplemente lo escribió usted mismo y tomó la excepción. Su punto puede ser justo sobre ES6 vs ES2015, pero en la conversación las personas a menudo recurren a una convención más corta para la eficiencia, por lo que creo que eliminarlo de la escritura no es posible o seguro.
wuliwong
Gracias por la parte ES6 de su respuesta; eso aclara mucho, especialmente cuando se combina con las 2 respuestas "públicas + privilegiadas" anteriores. Sin embargo estoy totalmente confundido por su obj.constructor === Personbienestar trueejemplo, aunque ... Whaaaat? ¿Cómo puede el constructor de una instancia de ===clase la clase misma ...? (Eso es como decir que un subconjunto de un conjunto es el conjunto en sí, etc.)
Andrew
Ohhh ... ¿esto es todo para decir que, literalmente, el constructor es todo lo que una clase JS realmente es al final del día? ¿Todo lo demás está amontonado en el constructor o es una construcción totalmente estática aislada de la clase, excepto por nombre / concepto (y como un "esto" implícito que está disponible obviamente)? (Por lo tanto, lo que pensé que era un subconjunto del conjunto no era en realidad un subconjunto)
Andrew