Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Thêm property, method vào Constructor JavaScript (Prototype)

Để thêm property/method **chia sẻ cho tất cả instances** của một constructor, gán vào Constructor.prototype. Đây là cơ chế **Prototype-based Inheritance** của JavaScript.

Cơ chế:

  • Mỗi function có property prototype (object)
  • Instance tạo bằng new có __proto__ trỏ đến Constructor.prototype
  • Truy cập property: tìm trên instance → không thấy → tìm trên prototype chain

Ví dụ minh họa

<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Add Property/Method to Constructor</title>
  <style>
    body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
    .result { font-size: 18px; font-weight: 500; margin: 10px 0; color: green; }
    button { padding: 8px 16px; cursor: pointer; }
  </style>
</head>
<body>
  <h1>Thêm Property/Method vào Constructor (Prototype)</h1>
  <div class="result" id="result"></div>
  <button class="Btn" id="btn">NHẤN VÀO ĐÂY</button>
  <h3> Nhấn nút để thêm property/method vào prototype </h3>
  <script>
    const resEle = document.querySelector("#result");
    
    function Human(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
    }
    
    const obj = new Human('Rohan', 'Sharma');
    
    document.querySelector("#btn").addEventListener("click", () => {
      // 1. Thêm property vào prototype
      Human.prototype.age = 22;
      
      // 2. Thêm method vào prototype
      Human.prototype.welcome = function() {
        return `Welcome ${this.firstName} ${this.lastName}`;
      };
      
      // 3. Instance truy cập được prototype property/method
      resEle.innerHTML = 'Thêm property age qua prototype: ' + obj.age + '
'; resEle.innerHTML += 'Thêm method welcome() qua prototype: ' + obj.welcome() + '
'; // 4. Kiểm tra own vs inherited resEle.innerHTML += '
--- Kiểm tra ---
'; resEle.innerHTML += `obj.hasOwnProperty('firstName'): ${obj.hasOwnProperty('firstName')}
`; resEle.innerHTML += `obj.hasOwnProperty('age'): ${obj.hasOwnProperty('age')}
`; resEle.innerHTML += `'age' in obj: ${'age' in obj}
`; resEle.innerHTML += `Object.getPrototypeOf(obj) === Human.prototype: ${Object.getPrototypeOf(obj) === Human.prototype}
`; }); </script> </body> </html>

Kết quả

Thêm property age qua prototype: 22
Thêm method welcome() qua prototype: Welcome Rohan Sharma

--- Kiểm tra ---
obj.hasOwnProperty('firstName'): true
obj.hasOwnProperty('age'): false
'age' in obj: true
Object.getPrototypeOf(obj) === Human.prototype: true

Prototype Chain Visualization:

obj (instance)
  ├── firstName: "Rohan" (own)
  ├── lastName: "Sharma" (own)
  └── __proto__ → Human.prototype
                ├── age: 22
                ├── welcome: ƒ()
                └── __proto__ → Object.prototype
                                    ├── toString: ƒ()
                                    ├── hasOwnProperty: ƒ()
                                    └── __proto__ → null

Property Shadowing (Che giấu):

Human.prototype.age = 22;
const obj = new Human("Rohan", "Sharma");

obj.age = 25; // Tạo OWN property 'age' trên instance
console.log(obj.age);        // 25 (own property)
console.log(obj.hasOwnProperty('age')); // true

delete obj.age; // Xóa own property
console.log(obj.age);        // 22 (từ prototype)

Best Practice:

  • Property instance-specific (name, id, data) → gán trong constructor (this.prop = ...)
  • Method/Shared property (logic, constants, default values) → gán vào Constructor.prototype
  • Tránh gán object/array vào prototype (shared reference bug):
// ❌ SAI: Shared reference!
function User() {}
User.prototype.friends = []; // TẤT CẢ instances dùng CÙNG MỘT mảng
const u1 = new User(); u1.friends.push("A");
const u2 = new User(); console.log(u2.friends); // ["A"] - BUG!

// ✅ ĐÚNG: Khởi tạo trong constructor
function User() {
  this.friends = []; // Mỗi instance có mảng riêng
}

// Hoặc dùng getter (ES6 class)
class User {
  get friends() { return this._friends || (this._friends = []); }
}

ES6 Class Syntax (Tương đương):

class Human {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  
  // Instance method (tự động vào prototype)
  welcome() {
    return `Welcome ${this.firstName} ${this.lastName}`;
  }
  
  // Static method (trên class, không phải instance)
  static species() { return "Homo sapiens"; }
  
  // Getter/Setter
  get fullName() { return `${this.firstName} ${this.lastName}`; }
  set fullName(value) { [this.firstName, this.lastName] = value.split(" "); }
}