JavaScript: Class.method vs. 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?
Raghavendra

1
@Raghavendra ไม่ใช่
Zorgatone

1
@ แก้ไขการเชื่อมโยงของคุณตายแล้ว
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 methodinstance 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เป็นวิธีการแบบคงที่ในขณะที่เป็นวิธีการที่ตัวอย่างของsayHiPerson

ด้านล่างเป็นวิธีการสร้างวัตถุจากตัว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}`;
   }
}

ดูที่วิธีการคำหลักที่ถูกใช้ในการประกาศวิธีการคงstaticisPerson

เพื่อสร้างวัตถุ Personคลาส

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

ใช้วิธีการคงที่ isPersonโดยใช้วิธีการแบบคงที่

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

ใช้วิธีการอินสแตนซ์ sayHiโดยใช้วิธีการเช่น

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

หมายเหตุ:ทั้งสองตัวอย่างนั้นโดยพื้นฐานแล้วจาวาสคริปต์ยังคงเป็นภาษาที่ไม่มีคลาส การclassแนะนำในES6นั้นส่วนใหญ่เป็นน้ำตาลในรูปแบบ syntax มากกว่าต้นแบบที่มีอยู่เดิม


"ใน ES6" คุณอธิบายถึงไวยากรณ์น้ำตาลเท่านั้น นี่ไม่ใช่ "ES2015" (โปรดให้ทุกคนหยุดใช้ ES6 ใช้คำที่เหมาะสม ES2015) ในการดำเนินการ มันเป็นอีกวิธีหนึ่งในการทำมันและในความคิดของฉันวิธีที่ไม่ถูกต้อง
K - ความเป็นพิษใน SO กำลังเพิ่มขึ้น

2
@KarlMorrison Aminu ไม่ได้เขียน "วิธีการทำ" คุณเพียงแค่เขียนว่าตัวคุณเองและได้รับการยกเว้น ประเด็นของคุณอาจมีความยุติธรรมเกี่ยวกับ ES6 กับ ES2015 แต่ในการสนทนาผู้คนมักใช้วิธีการที่สั้นกว่าเพื่อประสิทธิภาพดังนั้นฉันคิดว่าการลบออกจากการเขียนนั้นเป็นไปไม่ได้หรือไม่แนะนำให้เลือกอย่างแน่นอน
wuliwong

ขอบคุณสำหรับคำตอบของคุณในส่วน ES6; ที่ชัดเจนมากโดยเฉพาะเมื่อรวมกับคำตอบ 2 "สาธารณะ + สิทธิ์" ด้านบน อย่างไรก็ตามฉันสับสนอย่างถี่ถ้วนจากobj.constructor === Personการเป็นtrueตัวอย่างของคุณแม้ว่า ... Whaaaat? ตัวสร้างคลาสอินสแตนซ์ของ===คลาสเองได้อย่างไร ... ? (นั่นก็เหมือนกับการบอกว่าเซตย่อยของเซตคือเซตตัวเอง ฯลฯ ... )
Andrew

โอ้ ... นี่คือทั้งหมดที่จะพูดแล้วว่าแท้จริงผู้สร้างคือสิ่งที่ชั้นเรียน JS จริงๆในตอนท้ายของวัน? ทุกอย่างอื่นถูกซ้อนลงในคอนสตรัคเตอร์หรือสแตติกแบบคงที่ที่แยกได้จากคลาสยกเว้นตามชื่อ / แนวคิด (ดังนั้นสิ่งที่ฉันคิดว่าเป็นเซตย่อยของเซตไม่ใช่เซตย่อย)
แอนดรูว์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.