Prototype trong javascript

Trong JavaScript, prototype là một cơ chế cho phép bạn thêm các phương thức và thuộc tính vào một đối tượng hoặc lớp (constructor function). Mỗi hàm tạo (constructor function) trong JavaScript đều có một thuộc tính đặc biệt gọi là prototype, giúp các đối tượng được tạo ra từ hàm tạo đó kế thừa các phương thức hoặc thuộc tính.

  1. Cơ chế hoạt động của prototype
  2. Cú pháp Prototype
  3. Ví dụ sử dụng prototype
  4. Kế thừa với prototype
  5. Lợi ích của prototype
  6. Ứng dụng viết thêm vào các đối tượng có sẵn trong Javascript

1. Cơ chế hoạt động của prototype

    Thuộc tính prototype là một đối tượng, và mỗi đối tượng được tạo ra từ một hàm tạo (constructor function) sẽ có một liên kết (reference) tới thuộc tính prototype của hàm tạo đó.

    Kế thừa thông qua prototype: Khi bạn gọi một phương thức hoặc thuộc tính của đối tượng, JavaScript sẽ kiểm tra xem phương thức đó có tồn tại trong đối tượng hay không. Nếu không, JavaScript sẽ tìm kiếm trong đối tượng prototype của đối tượng đó. Nếu không tìm thấy trong prototype, nó sẽ tiếp tục tìm trong prototype của prototype, và cứ tiếp tục như vậy cho đến khi gặp null.

2. Cú pháp Prototype

function Constructor() {
  this.property = 'value';
}

// Thêm phương thức vào prototype của Constructor
Constructor.prototype.method = function() {
  console.log('Method called!');
};


Ở đây:

    Constructor là một hàm tạo (constructor function).
    Constructor.prototype.method là một phương thức được thêm vào prototype của hàm tạo Constructor.
    Mọi đối tượng được tạo ra từ Constructor sẽ có thể gọi phương thức method.

3. Ví dụ sử dụng prototype

Ví dụ 1 sử dụng prototype

 

function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Thêm phương thức vào prototype của Person
Person.prototype.sayHello = function() {
  console.log('Hello, my name is ' + this.name);
};

// Tạo đối tượng từ hàm tạo Person
var person1 = new Person('John', 30);
person1.sayHello();  // Output: Hello, my name is John


    Ở đây, sayHello không phải là một phương thức được khai báo trong constructor, mà là phương thức được thêm vào prototype của Person. Điều này có nghĩa là tất cả các đối tượng Person đều có thể truy cập phương thức sayHello, và phương thức này chỉ tồn tại một bản sao duy nhất trong bộ nhớ.

Sử dụng prototype để thêm thuộc tính mới:

 
function Car(model, year) {
      this.model = model;
      this.year = year;
    }

    // Thêm thuộc tính vào prototype của Car
    Car.prototype.manufacturer = 'Toyota';

    var car1 = new Car('Camry', 2020);
    var car2 = new Car('Corolla', 2021);

    console.log(car1.manufacturer);  // Output: Toyota
    console.log(car2.manufacturer);  // Output: Toyota


        manufacturer không phải là thuộc tính riêng của các đối tượng car1 và car2, mà là thuộc tính được thêm vào prototype của Car. Điều này giúp tiết kiệm bộ nhớ, vì tất cả các đối tượng Car sẽ chia sẻ thuộc tính này.

Ví dụ 2 sử dụng prototype

Giả sử bạn đang tạo một ứng dụng quản lý sách và bạn muốn mỗi cuốn sách có thể có phương thức để in thông tin của nó.

Bạn không muốn tạo phương thức này cho từng đối tượng sách mà thay vào đó bạn muốn tất cả các cuốn sách có thể sử dụng phương thức này, nhờ vào prototype.

 

// Hàm tạo đối tượng Book (Sách)
function Book(title, author) {
  this.title = title;
  this.author = author;
}

// Thêm phương thức 'getDetails' vào prototype của Book
Book.prototype.getDetails = function() {
  return `${this.title} by ${this.author}`;
};

// Tạo các đối tượng sách
var book1 = new Book("The Great Gatsby", "F. Scott Fitzgerald");
var book2 = new Book("To Kill a Mockingbird", "Harper Lee");

// Sử dụng phương thức 'getDetails' cho các đối tượng sách
console.log(book1.getDetails());  // Output: The Great Gatsby by F. Scott Fitzgerald
console.log(book2.getDetails());  // Output: To Kill a Mockingbird by Harper Lee

Giải thích đối tượng quản lý sách:

    Hàm tạo Book: Được sử dụng để tạo các đối tượng sách với thuộc tính title và author.
    prototype.getDetails: Phương thức getDetails được thêm vào prototype của Book, nghĩa là tất cả các đối tượng Book đều có thể sử dụng phương thức này mà không cần phải tạo lại mỗi lần.
    Tiết kiệm bộ nhớ: Nếu bạn tạo ra 1000 cuốn sách, tất cả sẽ chia sẻ cùng một phương thức getDetails, thay vì mỗi cuốn sách có một bản sao riêng của phương thức này.

4. Kế thừa với prototype

JavaScript sử dụng cơ chế prototype-based inheritance (kế thừa dựa trên prototype) thay vì kế thừa dựa trên lớp (class-based inheritance) như trong các ngôn ngữ khác (ví dụ: Java, C++). Điều này có nghĩa là một đối tượng có thể kế thừa phương thức và thuộc tính từ một đối tượng khác thông qua chuỗi liên kết prototype.

Ví dụ về kế thừa

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound');
};

function Dog(name) {
  Animal.call(this, name);  // Gọi hàm tạo của Animal để kế thừa thuộc tính
}

// Kế thừa phương thức speak từ Animal
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log(this.name + ' barks');
};

var dog = new Dog('Buddy');
dog.speak();  // Output: Buddy makes a sound
dog.bark();   // Output: Buddy barks


Trong ví dụ trên:

    Dog kế thừa từ Animal thông qua Dog.prototype = Object.create(Animal.prototype).
    Điều này cho phép đối tượng Dog kế thừa phương thức speak từ Animal và đồng thời có thể thêm các phương thức riêng như bark.

5. Lợi ích của prototype

    Tiết kiệm bộ nhớ: Các phương thức và thuộc tính thêm vào prototype sẽ được chia sẻ bởi tất cả các đối tượng tạo ra từ hàm tạo, thay vì mỗi đối tượng phải có một bản sao riêng.
    Kế thừa dễ dàng: prototype giúp bạn dễ dàng kế thừa và mở rộng các hàm tạo khác, cung cấp khả năng tái sử dụng mã nguồn.

6. Ứng dụng viết thêm vào các đối tượng có sẵn trong Javascript

Ứng dụng thêm vào biến kiểu String

* Không truyền tham số vào

String.prototype.AddText = function() {
  return "TEXT: "+this ;
};

// Sử dụng phương thức 'toTitleCase'
var str2 = "hello world, how are you?";
alert(str2.AddText()); 

* Có truyền tham số vào

String.prototype.AddText = function(add) {    
  return add+this ;
};

// Sử dụng phương thức 'toTitleCase'
var str2 = "hello world, how are you?";
alert(str2.AddText("A:"));  
alert(str2.AddText("B:")); 

Ứng dụng thêm vào biến kiểu Số

Number.prototype.CoSoDu = function() {
  return this % 2 !== 0;
};

// Kiểm tra các số với phương thức CoSoDu
var num1 = 10;
var num2 = 15;
alert(num1.CoSoDu());  // Output: false
alert(num2.CoSoDu());  // Output: true

Ứng dụng thêm vào biến kiểu Date

// Thêm phương thức getWeekNumber vào Date prototype
Date.prototype.getWeekNumber = function() {
  var date = new Date(this.getFullYear(), 0, 1);
  var days = Math.floor((this - date) / (24 * 60 * 60 * 1000));
  return Math.ceil((days + 1) / 7);
};

// Kiểm tra phương thức getWeekNumber
var date1 = new Date('2024-12-21');
alert(date1.getWeekNumber());  // Output: 51 (Tuần thứ 51 của năm 2024)

Bài viết liên quan:

Trong JavaScript, prototype là một cơ chế cho phép bạn thêm các phương thức và thuộc tính vào một đối tượng hoặc lớp (constructor function). Mỗi hàm tạo (constructor function) trong JavaScript đều có một thuộc tính đặc biệt gọi là prototype, giúp các đối tượng được tạo ra từ hàm tạo đó kế thừa các phương thức hoặc thuộc tính.