Kodokon kodokon.com

this, โปรโตไทป์ และคลาส

ทำความเข้าใจว่า this ถูกกำหนดค่าแบบไดนามิกอย่างไร โปรโตไทป์เชนทำงานอย่างไร และคลาสเพิ่มอะไรเข้าไปในโมเดลของอ็อบเจกต์กันแน่

10 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

ค่าของ this ถูกตัดสิน ณ จุดที่เรียกใช้ ไม่ใช่ตอนประกาศ มีสี่กฎ เรียงตามลำดับความสำคัญจากมากไปน้อย: การเรียกด้วย new, การผูกอย่างชัดเจน (call, apply, bind), การเรียกในฐานะเมทอด (obj.fn()) และการเรียกแบบธรรมดา (fn()) ซึ่งให้ค่า undefined ในโหมด strict บั๊กที่พบบ่อยที่สุดในโลกจริงตามมาโดยตรง: การดึงเมทอดออกจากอ็อบเจกต์ของมันทำให้การผูกโดยปริยายพัง

JAVASCRIPT
const user = {
  name: "Ada",
  greet() {
    return "Hello, " + this.name;
  },
};
console.log(user.greet()); // "Hello, Ada"
const detached = user.greet;
// detached(); // TypeError: this is undefined
const bound = user.greet.bind(user);
console.log(bound()); // "Hello, Ada"
การผูกโดยปริยายหายไปทันทีที่เมทอดถูกดึงออกมา

arrow function ไม่มี this เป็นของตัวเอง: มันจับ this แบบ เล็กซิคัล จากขอบเขตที่ครอบมันไว้ เหมือนกับ closure ทำให้มันเหมาะกับคอลแบ็ก แต่กลับให้ผลตรงข้ามเมื่อใช้เป็นเมทอดใน object literal ซึ่ง this จะชี้ไปยังขอบเขตภายนอก เบื้องหลังนั้น อ็อบเจกต์ทุกตัวมอบหมายผ่าน โปรโตไทป์เชน: พร็อพเพอร์ตีที่หายไปจะถูกค้นหาทีละลิงก์จนกว่าจะถึง null

JAVASCRIPT
function Animal(name) {
  this.name = name;
}
Animal.prototype.describe = function () {
  return this.name + " is an animal";
};
const dog = new Animal("Rex");
console.log(dog.describe()); // "Rex is an animal"
console.log(Object.getPrototypeOf(dog) ===
  Animal.prototype); // true
console.log(dog.hasOwnProperty("describe")); // false
describe อยู่บนโปรโตไทป์ ใช้ร่วมกันโดยทุกอินสแตนซ์

ไวยากรณ์ class ไม่ได้เปลี่ยนโมเดลนี้: มันทำให้อ่านง่ายขึ้น เมทอดที่ประกาศในตัวคลาสจะไปอยู่บน Constructor.prototype และจึงถูก ใช้ร่วมกัน ส่วนฟิลด์ของอินสแตนซ์ (รวมถึง arrow function ที่กำหนดเป็นฟิลด์) จะถูกสร้างใหม่ ต่อหนึ่งอินสแตนซ์: รับประกันการผูก this แต่ต้นทุนหน่วยความจำเพิ่มทวีคูณ สุดท้าย ฟิลด์ #private ให้ความเป็นส่วนตัวที่บังคับโดยเอนจิน ต่างจากธรรมเนียม _underscore

JAVASCRIPT
class BankAccount {
  #balance = 0;
  deposit(amount) {
    if (amount <= 0) throw new Error("Invalid amount");
    this.#balance += amount;
    return this;
  }
  get balance() {
    return this.#balance;
  }
}
const account = new BankAccount();
account.deposit(50).deposit(25);
console.log(account.balance); // 75
ฟิลด์ส่วนตัวที่บังคับโดยเอนจินและ API แบบเชนได้

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ในโหมด strict this มีค่าอะไรระหว่างการเรียกแบบธรรมดา fn() ที่ไม่มีอ็อบเจกต์และไม่มีการผูกอย่างชัดเจน?
    • globalThis
    • undefined
    • อ็อบเจกต์ที่ fn ถูกประกาศไว้
    • null
  2. เมทอดที่ประกาศในตัวคลาส (ไม่รวมฟิลด์) ถูกจัดเก็บไว้ที่ไหน?
    • บนแต่ละอินสแตนซ์ คัดลอก ณ เวลาสร้าง
    • บน Constructor.prototype ใช้ร่วมกันโดยทุกอินสแตนซ์
    • ในขอบเขตภายในที่เข้าถึงไม่ได้
  3. อะไรคือความพิเศษของ arrow function ในเรื่องของ this?
    • มันผูก this กับอ็อบเจกต์ที่เรียก เหมือนเมทอด
    • มันไม่มี this เป็นของตัวเองและจับ this จากขอบเขตที่ครอบมันไว้
    • มันผูก this กับ globalThis เสมอ
    • มันห้ามการเข้าถึง this ใด ๆ ทั้งสิ้น