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

Nhược điểm của phương thức private thực sự trong JavaScript (trước ES2022)

Trước khi có private class fields (ký hiệu #, ES2022), cách phổ biến để tạo phương thức private trong JavaScript là dùng closure bên trong constructor. Cách này có nhược điểm lớn về bộ nhớ và hiệu năng.

Cách cũ: Private method qua Closure

function Student(name, marks) {
  this.name = name || "";
  this.marks = marks || 300;

  // Private method - tạo mới MỖI LẦN khởi tạo đối tượng
  var increaseMarks = function() {
    this.marks = this.marks + 10;
  };

  // Public method truy cập private method
  this.displayIncreasedMarks = function() {
    increaseMarks.call(this); // Cần call/apply để bind this
    console.log(this.marks);
  };
}

// Mỗi instance đều có bản sao riêng của increaseMarks
const student1 = new Student("Ayush", 294);
const student2 = new Student("Anak", 411);

student1.displayIncreasedMarks(); // 304
student2.displayIncreasedMarks(); // 421

// Kiểm tra: hai hàm increaseMarks KHÔNG CHUNG
console.log(student1.increaseMarks === student2.increaseMarks); // ReferenceError (private)
// Nhưng qua closure, mỗi instance nắm giữ một function object riêng

Tại sao đây là vấn đề?

Vấn đềGiải thích
Bộ nhớMỗi instance tạo một function object mới cho mỗi private method. 1000 instance = 1000 bản sao hàm.
GC (Garbage Collection)Các bản sao hàm chỉ được giải phóng khi instance bị hủy. Không thể chia sẻ/reuse.
Hiệu năng khởi tạoTốn thời gian allocate function object mỗi lần new.
Không thể kế thừaPrivate methods trong closure không thể override hoặc gọi từ subclass.
Debugging khóTrong DevTools, thấy nhiều function匿名 trùng tên, khó phân biệt.

Giải pháp hiện đại: Private Class Fields (ES2022+)

Từ ES2022, JavaScript hỗ trợ private fields/methods chuẩn với ký hiệu #:

class Student {
  #marks;           // Private field
  #increaseMarks() { // Private method
    this.#marks += 10;
  }

  constructor(name, marks) {
    this.name = name || "";
    this.#marks = marks || 300;
  }

  displayIncreasedMarks() {
    this.#increaseMarks();
    console.log(this.#marks);
  }
}

const s1 = new Student("Ayush", 294);
const s2 = new Student("Anak", 411);

s1.displayIncreasedMarks(); // 304
s2.displayIncreasedMarks(); // 421

// Ưu điểm:
// - Chỉ MỘT bản sao method trên prototype (chia sẻ)
// - Không tạo function mới mỗi instance
// - Hỗ trợ kế thừa, override
// - Truy cập #increaseMarks bên ngoài class → SyntaxError

So sánh bộ nhớ

// Cách cũ (Closure) - Mỗi instance: ~200-500 bytes thêm cho mỗi private method
// Cách mới (#private) - Chỉ 1 bản sao method trên prototype, instance chỉ lưu data

// Test sơ bộ
const N = 10000;
console.time("Closure");
const arr1 = Array.from({length: N}, (_, i) => new OldStudent("S" + i, i));
console.timeEnd("Closure");

console.time("Private #");
const arr2 = Array.from({length: N}, (_, i) => new NewStudent("S" + i, i));
console.timeEnd("Private #");
// Kết quả: Private # nhanh hơn và nhẹ bộ nhớ đáng kể

Khi nào vẫn dùng closure?

  • Môi trường không hỗ trợ ES2022 (trình duyệt cũ, Node.js < 14.6).
  • Cần private state thực sự mỗi instance không chia sẻ prototype (hiếm).
  • Module pattern / IIFE cho code không dùng class.

Tóm tắt

Cách tạo private method bằng closure trong constructor gây lãng phí bộ nhớ do nhân bản function cho mỗi instance. Hãy chuyển sang private class fields (#) từ ES2022 để code hiệu năng, sạch, và chuẩn hiện đại.