นอกจากนี้เรายังสามารถกำหนดวิธีการให้กับชั้นเรียนโดยรวมได้ วิธีการดังกล่าวเรียกว่า static
ในการประกาศคลาส จะเติมคำสำคัญ static
ไว้หน้า เช่นนี้
ผู้ใช้คลาส { คง staticMethod() { การแจ้งเตือน (สิ่งนี้ === ผู้ใช้); - - User.staticMethod(); // จริง
จริงๆ แล้วนั่นก็เหมือนกับการมอบหมายให้เป็นทรัพย์สินโดยตรง:
ผู้ใช้คลาส { } User.staticMethod = ฟังก์ชั่น () { การแจ้งเตือน (สิ่งนี้ === ผู้ใช้); - User.staticMethod(); // จริง
ค่าของ this
ในการเรียก User.staticMethod()
คือตัวสร้างคลาส User
เอง (กฎ "วัตถุก่อนจุด")
โดยปกติแล้ว วิธีการคงที่จะใช้เพื่อใช้งานฟังก์ชันที่เป็นของคลาสโดยรวม แต่ไม่ใช่กับอ็อบเจ็กต์ใด ๆ โดยเฉพาะ
ตัวอย่างเช่น เรามีออบเจ็กต์ Article
และจำเป็นต้องมีฟังก์ชันเพื่อเปรียบเทียบ
วิธีแก้ปัญหาตามธรรมชาติคือการเพิ่ม Article.compare
วิธีการคงที่:
บทความในชั้นเรียน { ตัวสร้าง (ชื่อ, วันที่) { this.title = ชื่อ; this.date = วันที่; - การเปรียบเทียบแบบคงที่ (บทความ A, บทความ B) { กลับบทความA.date - บทความB.date; - - //การใช้งาน ให้บทความ = [ บทความใหม่ ("HTML", วันที่ใหม่ (2019, 1, 1)), บทความใหม่ ("CSS", วันที่ใหม่ (2019, 0, 1)), บทความใหม่ ("JavaScript", วันที่ใหม่ (2019, 11, 1)) - Articles.sort(บทความเปรียบเทียบ); alert( บทความ[0].title ); // ซีเอสเอส
ที่นี่วิธี Article.compare
หมายถึงบทความ "ด้านบน" เพื่อใช้เปรียบเทียบ ไม่ใช่วิธีการของบทความ แต่เป็นวิธีการของทั้งชั้นเรียน
อีกตัวอย่างหนึ่งคือสิ่งที่เรียกว่าวิธี "โรงงาน"
สมมติว่าเราต้องการหลายวิธีในการสร้างบทความ:
สร้างตามพารามิเตอร์ที่กำหนด ( title
, date
ฯลฯ )
สร้างบทความเปล่าพร้อมวันที่วันนี้
…หรืออย่างอื่น
วิธีแรกสามารถนำไปใช้โดยตัวสร้าง และสำหรับวิธีที่สอง เราสามารถสร้างวิธีการแบบคงที่ของคลาสได้
เช่น Article.createTodays()
ที่นี่:
บทความในชั้นเรียน { ตัวสร้าง (ชื่อ, วันที่) { this.title = ชื่อ; this.date = วันที่; - createTodays แบบคงที่ () { // จำไว้ว่านี่คือ = บทความ return new this("สรุปวันนี้", new Date()); - - ให้ article = Article.createTodays(); การแจ้งเตือน( Article.title ); //สรุปวันนี้.
ตอนนี้ทุกครั้งที่เราต้องการสร้างสรุปข้อมูลของวันนี้ เราสามารถเรียก Article.createTodays()
ได้ ขอย้ำอีกครั้งว่านั่นไม่ใช่วิธีการของบทความ แต่เป็นวิธีการของทั้งชั้นเรียน
วิธีการคงที่ยังใช้ในคลาสที่เกี่ยวข้องกับฐานข้อมูลเพื่อค้นหา/บันทึก/ลบรายการออกจากฐานข้อมูล เช่นนี้:
// สมมติว่า Article เป็นคลาสพิเศษสำหรับจัดการบทความ // วิธีคงที่เพื่อลบบทความด้วย id: Article.remove({id: 12345});
วิธีการคงที่ไม่พร้อมใช้งานสำหรับแต่ละวัตถุ
วิธีการแบบคงที่สามารถเรียกได้ในคลาส ไม่ใช่ในแต่ละอ็อบเจ็กต์
เช่นรหัสดังกล่าวใช้ไม่ได้:
- บทความ.createTodays(); /// ข้อผิดพลาด: article.createTodays ไม่ใช่ฟังก์ชัน
นอกจากนี้ล่าสุด
นี่เป็นส่วนเพิ่มเติมล่าสุดของภาษา ตัวอย่างใช้งานได้ใน Chrome ล่าสุด
คุณสมบัติแบบคงที่ก็เป็นไปได้เช่นกัน โดยมีลักษณะเหมือนคุณสมบัติของคลาสปกติ แต่ขึ้นต้นด้วย static
:
บทความในชั้นเรียน { ผู้เผยแพร่คงที่ = "Ilya Kantor"; - การแจ้งเตือน ( Article.publisher ); // อิลยา คันตอร์
นั่นก็เหมือนกับการมอบหมายโดยตรงให้กับ Article
:
Article.publisher = "อิลยา คันตอร์";
คุณสมบัติและวิธีการแบบคงที่ได้รับการสืบทอดมา
ตัวอย่างเช่น Animal.compare
และ Animal.planet
ในโค้ดด้านล่างได้รับการสืบทอดและเข้าถึงได้เป็น Rabbit.compare
และ Rabbit.planet
:
สัตว์ชั้น { ดาวเคราะห์คงที่ = "โลก"; ตัวสร้าง (ชื่อ, ความเร็ว) { this.speed = ความเร็ว; this.name = ชื่อ; - วิ่ง (ความเร็ว = 0) { this.speed += ความเร็ว; alert(`${this.name} ทำงานด้วยความเร็ว ${this.speed}.`); - การเปรียบเทียบแบบคงที่ (animalA, AnimalB) { กลับ AnimalA.speed - AnimalB.speed; - - // สืบทอดจากสัตว์ คลาส Rabbit ขยายสัตว์ { ซ่อน() { alert(`${this.name} ซ่อน!`); - - ให้กระต่าย = [ กระต่ายใหม่("กระต่ายขาว", 10), กระต่ายใหม่("กระต่ายดำ", 5) - rabbits.sort(Rabbit.เปรียบเทียบ); กระต่าย[0].รัน(); // กระต่ายดำวิ่งด้วยความเร็ว 5 การแจ้งเตือน (Rabbit.planet); // โลก
ตอนนี้เมื่อเราเรียก Rabbit.compare
Animal.compare
ที่สืบทอดมาจะถูกเรียก
มันทำงานอย่างไร? อีกครั้งโดยใช้ต้นแบบ ดังที่คุณอาจเดาได้แล้วว่า extends
ทำให้ Rabbit
มีการอ้างอิงถึง [[Prototype]]
ถึง Animal
ดังนั้น Rabbit extends Animal
จึงสร้างการอ้างอิง [[Prototype]]
สองรายการ:
ฟังก์ชัน Rabbit
ต้นแบบสืบทอดมาจากฟังก์ชัน Animal
Rabbit.prototype
ต้นแบบสืบทอดมาจาก Animal.prototype
เป็นผลให้การสืบทอดทำงานได้ทั้งสำหรับวิธีปกติและแบบคงที่
ที่นี่เรามาตรวจสอบด้วยรหัส:
สัตว์ชั้น {} คลาส Rabbit ขยายสัตว์ {} // สำหรับสถิติ การแจ้งเตือน (Rabbit.__proto__ === สัตว์); // จริง // สำหรับวิธีการปกติ การแจ้งเตือน (Rabbit.prototype.__proto__ === Animal.prototype); // จริง
วิธีการคงที่ใช้สำหรับการทำงานที่เป็นของคลาส "โดยรวม" มันไม่เกี่ยวข้องกับอินสแตนซ์คลาสที่เป็นรูปธรรม
ตัวอย่างเช่น วิธีการเปรียบเทียบ Article.compare(article1, article2)
หรือวิธีการจากโรงงาน Article.createTodays()
มีป้ายกำกับด้วยคำว่า static
ในการประกาศชั้นเรียน
คุณสมบัติคงที่จะใช้เมื่อเราต้องการจัดเก็บข้อมูลระดับชั้นเรียน และไม่ได้เชื่อมโยงกับอินสแตนซ์ด้วย
ไวยากรณ์คือ:
คลาส MyClass { ทรัพย์สินคงที่ = ...; วิธีการคงที่ () { - - -
ในทางเทคนิคแล้ว การประกาศแบบคงที่จะเหมือนกับการกำหนดให้กับชั้นเรียนเอง:
มายคลาส.พร็อพเพอร์ตี้ = ... MyClass.method = ...
คุณสมบัติและวิธีการแบบคงที่ได้รับการสืบทอดมา
สำหรับ class B extends A
ต้นแบบของคลาส B
จะชี้ไปที่ A
: B.[[Prototype]] = A
ดังนั้นหากไม่พบฟิลด์ใน B
การค้นหาจะดำเนินต่อไปใน A
ความสำคัญ: 3
ดังที่เราทราบ โดยปกติแล้ว Object ทั้งหมดจะสืบทอดมาจาก Object.prototype
และเข้าถึงวิธีการของ Object “ทั่วไป” เช่น hasOwnProperty
เป็นต้น
ตัวอย่างเช่น:
คลาสแรบบิท { ตัวสร้าง (ชื่อ) { this.name = ชื่อ; - - ให้กระต่าย = กระต่ายใหม่ ("Rab"); // วิธีการ hasOwnProperty มาจาก Object.prototype การแจ้งเตือน( rabbit.hasOwnProperty('ชื่อ') ); // จริง
แต่ถ้าเราสะกดมันออกมาอย่างชัดเจนเช่น "class Rabbit extends Object"
ผลลัพธ์ก็จะแตกต่างจาก "class Rabbit"
ธรรมดา ๆ ?
ความแตกต่างคืออะไร?
นี่คือตัวอย่างของโค้ดดังกล่าว (ใช้งานไม่ได้ – เพราะเหตุใด โปรดแก้ไข):
คลาส Rabbit ขยายวัตถุ { ตัวสร้าง (ชื่อ) { this.name = ชื่อ; - - ให้กระต่าย = กระต่ายใหม่ ("Rab"); การแจ้งเตือน( rabbit.hasOwnProperty('ชื่อ') ); // ข้อผิดพลาด
ก่อนอื่น เรามาดูกันว่าเหตุใดโค้ดหลังจึงใช้งานไม่ได้
เหตุผลจะชัดเจนถ้าเราพยายามเรียกใช้ ตัวสร้างคลาสที่สืบทอดจะต้องเรียก super()
มิฉะนั้น "this"
จะไม่ถูก "กำหนด"
นี่คือวิธีแก้ไข:
คลาส Rabbit ขยายวัตถุ { ตัวสร้าง (ชื่อ) { ซุปเปอร์(); // จำเป็นต้องเรียกตัวสร้างพาเรนต์เมื่อสืบทอด this.name = ชื่อ; - - ให้กระต่าย = กระต่ายใหม่ ("Rab"); การแจ้งเตือน( rabbit.hasOwnProperty('ชื่อ') ); // จริง
แต่นั่นยังไม่ใช่ทั้งหมด
แม้หลังจากการแก้ไขแล้ว ยังคงมีความแตกต่างที่สำคัญระหว่าง "class Rabbit extends Object"
และ class Rabbit
ดังที่เราทราบ ไวยากรณ์ "ขยาย" จะสร้างต้นแบบขึ้นมาสองแบบ:
ระหว่าง "prototype"
ของฟังก์ชันคอนสตรัคเตอร์ (สำหรับวิธีการ)
ระหว่างตัวสร้างทำหน้าที่ของตัวเอง (สำหรับวิธีคงที่)
ในกรณีของ class Rabbit extends Object
มันหมายถึง:
คลาส Rabbit ขยายวัตถุ {} การแจ้งเตือน ( Rabbit.prototype.__proto__ === Object.prototype ); // (1) จริง การแจ้งเตือน ( Rabbit.__proto__ === วัตถุ ); // (2) จริง
ดังนั้นตอนนี้ Rabbit
ให้การเข้าถึงวิธีการคงที่ของ Object
ผ่าน Rabbit
เช่นนี้:
คลาส Rabbit ขยายวัตถุ {} // ปกติเราจะเรียก Object.getOwnPropertyNames การแจ้งเตือน ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // ก,ข
แต่ถ้าเราไม่มี extends Object
ดังนั้น Rabbit.__proto__
จะไม่ถูกตั้งค่าเป็น Object
นี่คือการสาธิต:
คลาสแรบบิท {} การแจ้งเตือน ( Rabbit.prototype.__proto__ === Object.prototype ); // (1) จริง การแจ้งเตือน ( Rabbit.__proto__ === วัตถุ ); // (2) เท็จ (!) การแจ้งเตือน ( Rabbit.__proto__ === Function.prototype ); // เป็นฟังก์ชันใดๆ ตามค่าเริ่มต้น // ข้อผิดพลาด ไม่มีฟังก์ชันดังกล่าวใน Rabbit การแจ้งเตือน ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // ข้อผิดพลาด
ดังนั้น Rabbit
จึงไม่ให้การเข้าถึงวิธีการคงที่ของ Object
ในกรณีนั้น
อย่างไรก็ตาม Function.prototype
ยังมีเมธอดของฟังก์ชัน "ทั่วไป" เช่น call
, bind
ฯลฯ ซึ่งท้ายที่สุดแล้วพวกมันก็สามารถใช้ได้ในทั้งสองกรณี เพราะสำหรับ Object
Constructor ในตัวนั้น Object.__proto__ === Function.prototype
นี่คือภาพ:
สรุปให้สั้นลง มีข้อแตกต่างอยู่ 2 ประการ คือ
คลาสกระต่าย | คลาส Rabbit ขยายวัตถุ |
---|---|
- | จำเป็นต้องเรียก super() ในตัวสร้าง |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |