JavaScript: Class.method против Class.prototype.method


499

В чем разница между следующими двумя декларациями?

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

Можно ли считать первый оператор объявлением статического метода, а второй оператор - объявлением метода экземпляра?

Ответы:


696

Да, первая функция не имеет отношения к экземпляру объекта этой функции конструктора , вы можете рассматривать ее как «статический метод» .

В JavaScript функции являются объектами первого класса , это означает, что вы можете обращаться с ними как с любым объектом, в этом случае вы просто добавляете свойство к объекту функции .

Вторая функция, поскольку вы расширяете прототип функции конструктора, будет доступна для всех экземпляров объекта, созданных с помощью newключевого слова, и контекст в этой функции ( thisключевое слово) будет ссылаться на фактический экземпляр объекта, где вы его вызываете.

Рассмотрим этот пример:

// 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
Но почему Function.prototype.method == Function.method?
Рагхавендра

1
@Рагхавендра это не так
Зоргатоне

1
@ Неужели твоя ссылка мертва
Евгений Сунич

19

Когда вы создаете более одного экземпляра MyClass, у вас все равно будет только один экземпляр publicMethod в памяти, но в случае privilegedMethod вы в конечном итоге создадите множество экземпляров, а staticMethod не имеет отношения к экземпляру объекта.

Вот почему прототипы экономят память.

Кроме того, если вы измените свойства родительского объекта, если соответствующее свойство ребенка не было изменено, оно будет обновлено.


15

Для визуальных учеников, при определении функции без .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`  

С тем же кодом, если .prototypeон добавлен,

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`

Чтобы было понятнее,

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

**** Обратите внимание, что в приведенном выше примере someInstance.method () не будет выполняться, так как
ExampleClass.method () вызывает ошибку, и выполнение не может продолжаться.
Но ради иллюстрации и легкого понимания я сохранил эту последовательность. ****

Результаты, полученные из chrome developer console& Нажмите на ссылку jsbin выше, чтобы пройти через код. Переключить прокомментированный раздел с помощью +JS Bin

ctrl/


15

Да, первый static methodтакже называется class method, а второй - instance method.

Рассмотрим следующие примеры, чтобы понять это более подробно.

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

В приведенном выше коде isPersonэто статический метод, а sayHiметод экземпляра Person.

Ниже описано, как создать объект из Personконструктора.

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

Используя статический метод isPerson.

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

Используя метод экземпляра sayHi.

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

В ES6

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

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

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

Посмотрите, как staticключевое слово использовалось для объявления статического метода isPerson.

Создать объект Personкласса.

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

Используя статический метод isPerson.

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

Используя метод экземпляра sayHi.

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

ПРИМЕЧАНИЕ. Оба примера по сути одинаковы, JavaScript остается бесклассовым языком. Представленный classв ES6 является в первую очередь синтаксическим сахаром по сравнению с существующей моделью наследования на основе прототипов.


«В ES6» вы описываете только синтаксический сахар. Это не «ES2015» (пожалуйста, прекратите использовать ES6, используйте подходящий термин ES2015), чтобы сделать это. Это просто другой способ сделать это и, на мой взгляд, неправильный путь.
К - Токсичность в СО растет.

2
@KarlMorrison Амину не написал «способ сделать это», вы просто написали это сами и воспользовались этим. Ваша точка зрения может быть справедливой в отношении ES6 против ES2015, но в разговоре люди часто прибегают к более короткому соглашению по эффективности, поэтому я думаю, что исключить его из записи невозможно или наверняка желательно.
Вуливонг

Спасибо за ES6 часть вашего ответа; это многое проясняет, особенно в сочетании с 2 ответами «публичный + привилегированный» выше. Я, однако, полностью смущен тем obj.constructor === Person, trueчто ты являешься примером ... Вааааа? Как конструктор экземпляра ===класса сам класс ...? (Это все равно, что сказать, что подмножество набора - это сам набор и т. Д.)
Эндрю

Оооо ... это все, что буквально означает, что конструктор - это все, чем на самом деле является класс JS? Все остальное либо сваливается в конструктор, либо полностью является статической конструкцией, изолированной от класса, кроме как по имени / концепции (и, как подразумеваемое «это», сделанное доступным, очевидно)? (Таким образом, то, что я считал подмножеством набора, на самом деле не было подмножеством.)
Эндрю
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.