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


498

Яка різниця між наступними двома деклараціями?

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
@Raghavendra це не так
Zorgatone

1
@Menda ваше посилання мертве
Eugen Sunic

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, а другий - an 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 Aminu не написав "спосіб зробити це", ти просто написав це і взяв з нього виключення. Ваша думка може бути справедливою щодо ES6 проти ES2015, але в ході розмови люди часто вдаються до більш короткої конвенції щодо ефективності, тому я думаю, що вилучити її з письма неможливо або точно доцільно.
wuliwong

Дякую за частину вашої відповіді ES6; це багато що пояснює, особливо в поєднанні з двома відповідями "публічний + привілейований" вище. Я, однак, ретельно збентежений тим, що ти obj.constructor === Personє trueприкладом, хоча ... Whaaaat? Як може конструктор екземпляра ===класу сам клас ...? (Це як сказати, що підмножина набору - це сам набір тощо)
Андрій,

Ох ... це все тоді сказати, що буквально конструктор - це все, що насправді є клас JS в кінці дня? Все інше або накопичено в конструкторі, або повністю статична конструкція, ізольована від класу, за винятком назви / концепції (і немовби мається на увазі, що "це" стає доступним очевидно)? (Таким чином, те, що я вважав підмножиною набору, насправді не було підмножиною.)
Андрій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.