10 Phương Pháp Console JavaScript Đột Phá Giúp Bạn Gỡ Lỗi Hiệu Quả và Tiết Kiệm Thời Gian

Bạn có từng nghĩ rằng mình đã nắm vững API Console trong JavaScript? Với nhiều nhà phát triển, công cụ gỡ lỗi chủ yếu chỉ xoay quanh console.log(), cùng lắm là console.error() khi có sự cố và console.warn() cho các cảnh báo. Tuy nhiên, đó chỉ là phần nổi của tảng băng chìm.

Thực tế là Console API của trình duyệt là một kho vũ khí khổng lồ, nhưng hầu hết chúng ta chỉ sử dụng một phần rất nhỏ trong số đó. Phần còn lại đang chờ được khám phá, hứa hẹn sẽ giải thoát bạn khỏi hàng giờ vật lộn với những dòng console.log('đây 1'), console.log('đây 2') trong tuyệt vọng, giúp bạn định vị lỗi nhanh hơn và hiểu rõ hơn về hiệu suất ứng dụng.

Bài viết này sẽ giới thiệu 10 phương pháp console JavaScript ít được biết đến nhưng cực kỳ mạnh mẽ, sẽ thay đổi hoàn toàn cách bạn gỡ lỗi. Hãy cùng khám phá những “siêu năng lực” mới để làm chủ quá trình phát triển của mình!


1. console.table() – Hiển Thị Dữ Liệu Dưới Dạng Bảng Chuyên Nghiệp

Chức Năng Chính

console.table() nhận các mảng (arrays) và đối tượng (objects) rồi hiển thị chúng dưới dạng một bảng dễ đọc trực tiếp trong console. Tạm biệt những cấu trúc đối tượng lồng ghép khó hiểu hay chỉ số mảng rối rắm! Giờ đây, dữ liệu của bạn sẽ được trình bày gọn gàng, có cấu trúc như một bảng tính.

Vì Sao Bị Bỏ Qua?

Phần lớn chúng ta học console.log() ngay từ đầu và không bao giờ tìm hiểu thêm. console.log() vẫn “hoạt động”, nhưng khi đối mặt với các cấu trúc dữ liệu phức tạp, nó biến thành một mớ hỗn độn. Bạn phải liên tục mở rộng các mũi tên, cuộn qua các đối tượng lồng nhau và dễ dàng mất dấu điều mình đang tìm kiếm.

Cách Sử Dụng

Cú pháp vô cùng đơn giản:

console.table(data, [columns]);

Tham số đầu tiên là dữ liệu của bạn (có thể là mảng hoặc đối tượng). Tham số thứ hai, tùy chọn, cho phép bạn chỉ định các cột muốn hiển thị.

Ví Dụ Thực Tế

Ví dụ 1: Mảng Các Đối Tượng Cơ Bản

const users = [
  { id: 1, name: 'Nguyễn Văn A', role: 'Developer', active: true },
  { id: 2, name: 'Trần Thị B', role: 'Designer', active: true },
  { id: 3, name: 'Lê Văn C', role: 'Product Manager', active: false },
  { id: 4, name: 'Phạm Thị D', role: 'Developer', active: true }
];

console.table(users);

Kết quả sẽ là một bảng đẹp mắt với các cột như index, id, name, role, và active. Bạn có thể dễ dàng nhận ra các mẫu, tìm người dùng không hoạt động, và hiểu cấu trúc dữ liệu ngay lập tức.

Ví dụ 2: Lọc Cột Hiển Thị

// Chỉ hiển thị cột name và role
console.table(users, ['name', 'role']);

Điều này cực kỳ hữu ích khi bạn làm việc với các đối tượng có hàng tá thuộc tính nhưng chỉ quan tâm đến một vài trong số đó.

Ví dụ 3: Đối Tượng Chứa Các Đối Tượng

const apiResponses = {
  github: { status: 200, time: 145, cached: false },
  twitter: { status: 200, time: 312, cached: true },
  stripe: { status: 503, time: 5000, cached: false },
  sendgrid: { status: 200, time: 89, cached: true }
};

console.table(apiResponses);

Với console.table(), bạn ngay lập tức thấy rằng Stripe đang gặp sự cố và mất nhiều thời gian, trong khi SendGrid cực kỳ nhanh và đã được cache. Sẽ rất khó để có được cái nhìn sâu sắc này chỉ với console.log(apiResponses).

Ví dụ 4: Dữ Liệu Lồng Ghép (Lưu ý)

const complexData = [
  { 
    user: 'Alice', 
    stats: { posts: 45, likes: 230 },
    lastLogin: new Date('2024-11-28')
  },
  { 
    user: 'Bob', 
    stats: { posts: 12, likes: 89 },
    lastLogin: new Date('2024-11-30')
  }
];

console.table(complexData);

Bạn sẽ thấy các đối tượng lồng ghép hiển thị dưới dạng [object Object]. Đây là một hạn chế. Đối với dữ liệu lồng ghép sâu, bạn có thể cần làm phẳng nó trước hoặc sử dụng console.table() trên từng phần lồng ghép cụ thể.

Mẹo Chuyên Nghiệp

  1. Kết Hợp Với Các Phương Thức Mảng: Sử dụng console.table() ở cuối chuỗi phương thức mảng để xem kết quả chuyển đổi:
    console.table(
      users
        .filter(u => u.active)
        .map(u => ({ name: u.name, role: u.role }))
    );
    
  2. Sắp Xếp Trước Khi Hiển Thị: Trình duyệt không tự sắp xếp bảng cho bạn, hãy chuẩn bị dữ liệu trước:
    console.table(users.sort((a, b) => a.name.localeCompare(b.name)));
    
  3. Gỡ Lỗi Phản Hồi API: Khi làm việc với các API REST trả về mảng dữ liệu, console.table() là người bạn tốt nhất của bạn. Bạn có thể ngay lập tức phát hiện sự không nhất quán, các trường bị thiếu hoặc giá trị không mong muốn.

Các Trường Hợp Sử Dụng Nâng Cao

Bảng So Sánh Hiệu Suất

const performanceMetrics = [];

function measureOperation(name, fn) {
  const start = performance.now();
  fn();
  const end = performance.now();
  performanceMetrics.push({
    operation: name,
    duration: `${(end - start).toFixed(2)}ms`
  });
}

measureOperation('For Loop', () => {
  for (let i = 0; i < 100000; i++) { /* công việc */ }
});

measureOperation('forEach', () => {
  Array.from({ length: 100000 }).forEach(() => { /* công việc */ });
});

measureOperation('Map', () => {
  Array.from({ length: 100000 }).map(() => { /* công việc */ });
});

console.table(performanceMetrics);

Bây giờ bạn có một bảng so sánh hiệu suất rõ ràng ngay trong console của mình.

Kết Quả Truy Vấn Cơ Sở Dữ Liệu

Nếu bạn sử dụng MongoDB hoặc làm việc với kết quả truy vấn SQL (được chuyển đổi sang JSON), console.table() làm cho việc xem xét kết quả truy vấn dễ dàng hơn vô cùng so với việc chỉ log dữ liệu thô.

Những Lỗi Cần Tránh

  1. Không Dùng Với Mảng Quá Lớn: Hiển thị 10.000 hàng trong một bảng có thể làm đóng băng trình duyệt của bạn. Hãy lọc dữ liệu trước.
  2. Cẩn Thận Với Tham Chiếu Vòng Tròn: Nếu đối tượng của bạn có tham chiếu vòng tròn, console.table() có thể không xử lý chúng một cách suôn sẻ. Chrome thường quản lý được, nhưng Firefox có thể gặp khó khăn.
  3. Chỉ Để Xem, Không Chỉnh Sửa: Bạn không thể chỉnh sửa các giá trị trong bảng console và mong chúng phản ánh lại vào mã của mình. Nó chỉ đơn thuần để trực quan hóa.

console.table() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Hãy tưởng tượng bạn đang gỡ lỗi một hàm xử lý dữ liệu người dùng. Với console.log(), bạn sẽ thấy:

[{id: 1, name: "Sarah", ...}, {id: 2, name: "Marcus", ...}, ...]

Bạn phải nhấp để mở rộng, cuộn, so sánh giá trị trong đầu. Với console.table(), bạn thấy mọi thứ cùng một lúc. Bạn ngay lập tức nhận ra rằng người dùng ID 7 có địa chỉ email trống, hoặc ba người dùng có số điện thoại sai định dạng. Điều này, nếu dùng console.log(), sẽ mất năm phút để nhấp và cuộn, thì với console.table(), chỉ mất năm giây.


2. console.time() và console.timeEnd() – Đồng Hồ Bấm Giờ Gỡ Lỗi Hiệu Suất

Chức Năng Chính

Hai phương thức này hoạt động theo cặp. console.time() bắt đầu một bộ hẹn giờ với một nhãn cụ thể, và console.timeEnd() dừng nó, sau đó ghi lại thời gian đã trôi qua. Nó giống như có một đồng hồ bấm giờ tích hợp trực tiếp vào mã của bạn.

Vì Sao Bị Bỏ Qua?

Hầu hết các nhà phát triển đều biết về tab Performance trong DevTools, rất tốt cho việc lập hồ sơ phức tạp. Nhưng đôi khi bạn không cần một biểu đồ flame graph – bạn chỉ cần biết liệu một hàm cụ thể có chậm hay không. Việc sử dụng Date.now() hoặc performance.now() và thực hiện các phép toán thủ công có vẻ rườm rà. console.time() đơn giản đến mức mọi người không nhận ra nó tồn tại.

Cách Sử Dụng

console.time('tenNhan');
// ... mã bạn muốn đo ...
console.timeEnd('tenNhan');

Nhãn phải khớp chính xác. Trình duyệt sẽ ghi lại một cái gì đó giống như: tenNhan: 234.56ms

Ví Dụ Thực Tế

Ví dụ 1: Đo Thời Gian Hàm Cơ Bản

console.time('fetchUserData');

async function fetchUserData() {
  const response = await fetch('/api/users');
  const data = await response.json();
  return data;
}

const users = await fetchUserData();
console.timeEnd('fetchUserData');
// Output: fetchUserData: 347.82ms

Ví dụ 2: So Sánh Hiệu Suất Thuật Toán

const largeArray = Array.from({ length: 100000 }, (_, i) => i);

// Test forEach
console.time('forEach');
largeArray.forEach(num => num * 2);
console.timeEnd('forEach');

// Test map
console.time('map');
largeArray.map(num => num * 2);
console.timeEnd('map');

// Test for loop
console.time('for-loop');
for (let i = 0; i < largeArray.length; i++) {
  largeArray[i] * 2;
}
console.timeEnd('for-loop');

// Kết quả có thể là:
// forEach: 8.23ms
// map: 12.45ms
// for-loop: 3.67ms

Bây giờ bạn có dữ liệu thực nghiệm về phương pháp nào nhanh nhất trong kịch bản cụ thể của bạn.

Ví dụ 3: Bộ Hẹn Giờ Lồng Nhau

console.time('toan-bo-hoat-dong');

console.time('buoc-1-database');
await database.query('SELECT * FROM users');
console.timeEnd('buoc-1-database');

console.time('buoc-2-xu-ly');
const processed = processData(rawData);
console.timeEnd('buoc-2-xu-ly');

console.time('buoc-3-render');
renderToDOM(processed);
console.timeEnd('buoc-3-render');

console.timeEnd('toan-bo-hoat-dong');

// Kết quả:
// buoc-1-database: 234.12ms
// buoc-2-xu-ly: 45.67ms
// buoc-3-render: 12.34ms
// toan-bo-hoat-dong: 292.45ms

Điều này cho bạn biết chính xác nút thắt cổ chai của bạn nằm ở đâu. Trong trường hợp này, truy vấn cơ sở dữ liệu đang chiếm 80% thời gian của bạn.

Ví dụ 4: Đo Thời Gian Tương Tác Người Dùng

button.addEventListener('click', () => {
  console.time('xu-ly-nut-bam');

  // Mô phỏng các hoạt động phức tạp
  const result = performHeavyCalculation();
  updateUI(result);

  console.timeEnd('xu-ly-nut-bam');
});

Nếu điều này ghi lại xu-ly-nut-bam: 1247.89ms, bạn biết tại sao người dùng phàn nàn về giao diện người dùng chậm chạp.

Mẹo Chuyên Nghiệp

  1. Sử Dụng Nhãn Mô Tả: Không sử dụng các nhãn chung chung như ‘timer1’ hoặc ‘test’. Hãy sử dụng `’api-fetch-user-profile’` hoặc `’sort-10k-products’`. Bạn của tương lai sẽ biết ơn.
  2. Kết Hợp Với console.timeLog(): Còn có console.timeLog(label) ghi lại thời gian đã trôi qua hiện tại mà không dừng bộ hẹn giờ:
    console.time('long-operation');
    
    await step1();
    console.timeLog('long-operation'); // long-operation: 123ms
    
    await step2();
    console.timeLog('long-operation'); // long-operation: 456ms
    
    await step3();
    console.timeEnd('long-operation'); // long-operation: 789ms
    
  3. Tự Động Hóa Việc Đo Thời Gian Bằng Hàm Bao Bọc: Tạo một hàm tiện ích:
    async function timeAsync(label, asyncFn) {
      console.time(label);
      try {
        return await asyncFn();
      } finally {
        console.timeEnd(label);
      }
    }
    
    const data = await timeAsync('fetch-data', () => fetch('/api/data'));
    

Các Trường Hợp Sử Dụng Nâng Cao

Kiểm Tra Hiệu Suất A/B Testing

function measureImplementation(name, implementation, iterations = 1000) {
  console.time(name);
  for (let i = 0; i < iterations; i++) {
    implementation();
  }
  console.timeEnd(name);
}

measureImplementation('string-concat', () => {
  let str = '';
  for (let i = 0; i < 1000; i++) str += 'x';
});

measureImplementation('array-join', () => {
  const arr = [];
  for (let i = 0; i < 1000; i++) arr.push('x');
  arr.join('');
});

Đo Lường Hiệu Suất Tải Trì Hoãn (Lazy Loading)

console.time('initial-bundle');
// JavaScript khởi tạo
console.timeEnd('initial-bundle');

button.addEventListener('click', async () => {
  console.time('dynamic-import');
  const module = await import('./heavy-feature.js');
  console.timeEnd('dynamic-import');

  console.time('feature-initialization');
  module.initialize();
  console.timeEnd('feature-initialization');
});

Những Lỗi Cần Tránh

  1. Quên Gọi timeEnd(): Nếu bạn quên, bộ hẹn giờ sẽ vẫn mở. Không có lỗi, không có cảnh báo. Luôn luôn ghép chúng lại với nhau.
  2. Nhãn Không Khớp: console.time('myTimer')console.timeEnd('mytimer') sẽ không hoạt động. JavaScript phân biệt chữ hoa, chữ thường.
  3. Không Sử Dụng Cho Giám Sát Sản Xuất: Đây là các công cụ phát triển. Để giám sát hiệu suất sản xuất, hãy sử dụng các công cụ APM phù hợp như New Relic, Datadog hoặc Performance API với phân tích.
  4. Tránh Đo Thời Gian Mã Bất Đồng Bộ Mà Không Có Await: Điều này sẽ không hoạt động:
    console.time('fetch');
    fetch('/api/data'); // Không await
    console.timeEnd('fetch'); // Ghi lại ~0ms vì fetch chưa hoàn thành
    

console.time() & console.timeEnd() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Bạn nhận được báo cáo rằng ứng dụng của bạn chậm. Bạn bắt đầu từ đâu? Thay vì đoán mò hoặc thực hiện các hồ sơ phức tạp, bạn đặt console.time()console.timeEnd() xung quanh các khối mã đáng ngờ. Trong vòng vài phút, bạn đã xác định rằng đúng vậy, phần chậm là hàm xử lý hình ảnh mất 2 giây cho mỗi hình ảnh. Bây giờ bạn biết chính xác nơi cần tối ưu hóa.

Không có các phương thức này, bạn sẽ sử dụng performance.now(), thực hiện phép trừ thủ công và làm bẩn mã của bạn bằng logic đo thời gian. console.time() sạch hơn, dễ đọc hơn và nhanh hơn để triển khai.


3. console.trace() – Theo Dõi Dấu Vết Thực Thi Mã

Chức Năng Chính

console.trace() in ra một stack trace (dấu vết ngăn xếp) vào console, hiển thị cho bạn đường dẫn gọi hoàn chỉnh đã dẫn đến dòng mã đó. Nó giống như một nút “Làm thế nào tôi đến đây?” cho mã của bạn.

Vì Sao Bị Bỏ Qua?

Hầu hết các nhà phát triển chỉ thấy stack traces khi có lỗi được ném ra. Họ không nhận ra bạn có thể tạo chúng theo yêu cầu. Khi gỡ lỗi các ứng dụng phức tạp với các stack call sâu, việc biết cách một hàm được gọi thường quan trọng hơn việc biết rằng nó đã được gọi.

Cách Sử Dụng

console.trace('Nhãn tùy chọn');

Chỉ vậy thôi. Trình duyệt sẽ xuất ra toàn bộ stack trace từ điểm đó.

Ví Dụ Thực Tế

Ví dụ 1: Theo Dõi Các Lệnh Gọi Hàm

function calculateTotal(items) {
  console.trace('calculateTotal được gọi');
  return items.reduce((sum, item) => sum + item.price, 0);
}

function processOrder(order) {
  const total = calculateTotal(order.items);
  return total;
}

function handleCheckout(userId) {
  const order = getOrderForUser(userId);
  processOrder(order);
}

handleCheckout(12345);

// Kết quả hiển thị:
// console.trace: calculateTotal được gọi
//   calculateTotal @ app.js:2
//   processOrder @ app.js:7
//   handleCheckout @ app.js:12
//   (anonymous) @ app.js:15

Bây giờ bạn có thể thấy đường dẫn chính xác: handleCheckoutprocessOrdercalculateTotal.

Ví dụ 2: Gỡ Lỗi Trình Xử Lý Sự Kiện

function handleClick(event) {
  console.trace('Trình xử lý click được kích hoạt');
  updateUI();
}

document.addEventListener('click', handleClick);

// Khi nhấp, bạn sẽ thấy chính xác điều gì đã kích hoạt click:
// Có thể là tương tác người dùng, hoặc có thể một script khác đã gọi .click()

Ví dụ 3: Theo Dõi Re-renders của React

function UserProfile({ userId }) {
  console.trace('UserProfile đang render');

  const user = useUser(userId);
  return <div>{user.name}</div>;
}

Nếu thành phần này đang re-render một cách không mong muốn, stack trace sẽ cho bạn biết thành phần cha nào đã kích hoạt nó.

Ví dụ 4: Gỡ Lỗi Đệ Quy

function factorial(n) {
  if (n === 1) {
    console.trace('Đã đạt đến trường hợp cơ sở');
    return 1;
  }
  return n * factorial(n - 1);
}

factorial(5);

// Hiển thị toàn bộ stack call đệ quy

Mẹo Chuyên Nghiệp

  1. Thêm Ngữ Cảnh Bằng Nhãn: Luôn bao gồm một thông báo mô tả:
    console.trace(`Người dùng ${userId} đã đến trang thanh toán`);
    
  2. Kết Hợp Với Điều Kiện: Chỉ trace khi có điều gì đó không mong muốn xảy ra:
    function updateCart(item) {
      if (!item.price) {
        console.trace('Mặt hàng không có giá - điều này không bao giờ nên xảy ra');
      }
      // phần còn lại của mã
    }
    
  3. Sử Dụng Trong Mã Thư Viện/Framework: Khi sử dụng thư viện bên thứ ba, trace các lệnh gọi để hiểu cách chúng được gọi:
    const originalFetch = window.fetch;
    window.fetch = function(...args) {
      console.trace('Fetch được gọi');
      return originalFetch.apply(this, args);
    };
    

Các Trường Hợp Sử Dụng Nâng Cao

Theo Dõi Thay Đổi Trạng Thái (State Mutations)

const state = {
  _count: 0,
  get count() {
    return this._count;
  },
  set count(value) {
    console.trace(`Count thay đổi từ ${this._count} thành ${value}`);
    this._count = value;
  }
};

state.count = 5; // Hiển thị ai đã thay đổi nó

Tìm Nguồn Gốc Rò Rỉ Bộ Nhớ

class CacheManager {
  constructor() {
    this.cache = new Map();
  }

  set(key, value) {
    if (this.cache.size > 1000) {
      console.trace('Bộ nhớ cache vượt quá 1000 mục - tiềm năng rò rỉ bộ nhớ');
    }
    this.cache.set(key, value);
  }
}

Gỡ Lỗi Tích Hợp Bên Thứ Ba

// Bao bọc một hàm bên thứ ba để xem khi nào nó được gọi
const analytics = window.analytics;
window.analytics.track = function(...args) {
  console.trace('Analytics track được gọi với:', args);
  return analytics.track.apply(this, args);
};

Những Lỗi Cần Tránh

  1. Không Để Lại Chúng Trong Sản Xuất: Stack traces có chi phí hiệu suất. Hãy xóa chúng trước khi triển khai.
  2. Lưu Ý Sự Khác Biệt Giữa Các Trình Duyệt: Định dạng stack trace khác nhau giữa Chrome, Firefox và Safari. Không dựa vào việc phân tích output theo chương trình.
  3. Stack Traces Bất Đồng Bộ Có Thể Gây Nhầm Lẫn: Với async/await và promises, stack trace có thể không hiển thị toàn bộ bức tranh. Các trình duyệt hiện đại có async stack traces, nhưng chúng không phải lúc nào cũng hoàn hảo.
  4. Quá Nhiều Traces = Nhiễu: Nếu bạn trace mọi thứ, bạn sẽ bị chôn vùi trong output. Hãy thực hiện một cách chọn lọc.

console.trace() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Bạn có một lỗi mà một giá trị đang được đặt sai, nhưng bạn không biết ở đâu. Bạn có thể thêm các câu lệnh console.log() ở 20 nơi khác nhau, hoặc bạn có thể thêm một console.trace() nơi giá trị không chính xác đang được đặt và ngay lập tức thấy đường dẫn gọi. Tôi từng gỡ lỗi một Redux action đang được dispatch từ một nơi không mong muốn. Thay vì tìm kiếm hàng ngàn dòng mã, tôi đã thêm console.trace() vào reducer. Stack trace đã chỉ cho tôi trực tiếp đến một thư viện bên thứ ba đang dispatch các action mà tôi không biết. Điều này đã tiết kiệm cho tôi hàng giờ.


4. console.assert() – Kiểm Tra Tính Hợp Lệ Nhanh Chóng

Chức Năng Chính

console.assert() đánh giá một điều kiện và chỉ ghi lại lỗi nếu điều kiện đó là false. Nó giống như một bài kiểm tra đơn vị nhỏ được tích hợp vào mã runtime của bạn.

Vì Sao Bị Bỏ Qua?

Hầu hết các nhà phát triển nghĩ rằng các xác nhận chỉ dành cho các framework kiểm thử như Jest hoặc Mocha. Họ không nhận ra API console có khả năng xác nhận tích hợp sẵn. Nó cũng không hiển thị rõ ràng như console.log(), vì vậy nó không xuất hiện trong tâm trí khi gỡ lỗi.

Cách Sử Dụng

console.assert(condition, message, ...optionalData);

Nếu condition là truthy, không có gì xảy ra. Nếu nó là falsy, bạn sẽ nhận được một thông báo lỗi trong console.

Ví Dụ Thực Tế

Ví dụ 1: Các Xác Nhận Cơ Bản

function divide(a, b) {
  console.assert(b !== 0, 'Lỗi chia cho số không', { a, b });
  return a / b;
}

divide(10, 2); // Không có output
divide(10, 0); // Assertion failed: Lỗi chia cho số không {a: 10, b: 0}

Ví dụ 2: Kiểm Tra Kiểu Dữ Liệu

function processUser(user) {
  console.assert(typeof user === 'object', 'Người dùng phải là một đối tượng');
  console.assert('id' in user, 'Người dùng phải có một id', user);
  console.assert('email' in user, 'Người dùng phải có một email', user);

  // Xử lý người dùng...
}

processUser({ id: 1 }); // Assertion failed: Người dùng phải có một email

Ví dụ 3: Xác Thực Mảng

function processItems(items) {
  console.assert(Array.isArray(items), 'Items phải là một mảng', items);
  console.assert(items.length > 0, 'Mảng items không được rỗng');
  console.assert(
    items.every(item => item.id),
    'Tất cả các item phải có một id',
    items.filter(item => !item.id)
  );

  // Xử lý items...
}

Ví dụ 4: Bất Biến Trạng Thái

class ShoppingCart {
  constructor() {
    this.items = [];
    this.total = 0;
  }

  addItem(item) {
    this.items.push(item);
    this.total += item.price;

    // Xác nhận bất biến: tổng phải bằng tổng giá các mặt hàng
    const expectedTotal = this.items.reduce((sum, i) => sum + i.price, 0);
    console.assert(
      this.total === expectedTotal,
      'Tổng giỏ hàng không khớp',
      { actual: this.total, expected: expectedTotal }
    );
  }
}

Mẹo Chuyên Nghiệp

  1. Bao Gồm Dữ Liệu Ngữ Cảnh: Các tham số tùy chọn có thể là bất kỳ giá trị nào:
    console.assert(
      user.age >= 18,
      'Người dùng phải từ 18 tuổi trở lên',
      { user, currentAge: user.age, required: 18 }
    );
    
  2. Sử Dụng Cho Điều Kiện Tiên Quyết: Kiểm tra các giả định ở đầu hàm:
    function updateProfile(userId, updates) {
      console.assert(userId, 'userId là bắt buộc');
      console.assert(Object.keys(updates).length > 0, 'updates không được rỗng');
      // Logic hàm...
    }
    
  3. Kiểm Tra Phản Hồi API: Xác thực dữ liệu từ các nguồn bên ngoài:
    async function fetchUser(id) {
      const response = await fetch(`/api/users/${id}`);
      const user = await response.json();
    
      console.assert(user.id === id, 'ID phản hồi không khớp', { requested: id, received: user.id });
      console.assert(user.email, 'Người dùng thiếu email', user);
    
      return user;
    }
    

Các Trường Hợp Sử Dụng Nâng Cao

Xác Nhận Hiệu Suất

function criticalOperation() {
  const start = performance.now();

  // ... hoạt động ...

  const duration = performance.now() - start;
  console.assert(
    duration < 100,
    'Thao tác mất quá nhiều thời gian',
    { duration: `${duration}ms`, threshold: '100ms' }
  );
}

Kiểm Tra Chỉ Dành Cho Phát Triển

const isDev = process.env.NODE_ENV === 'development';

function transferFunds(from, to, amount) {
  if (isDev) {
    console.assert(from.balance >= amount, 'Không đủ tiền');
    console.assert(amount > 0, 'Số tiền phải dương');
    console.assert(from.id !== to.id, 'Không thể chuyển vào cùng một tài khoản');
  }

  // Logic chuyển tiền...
}

Xác Thực Props Thành Phần React

function UserCard({ user, onEdit, theme }) {
  console.assert(user, 'UserCard: prop user là bắt buộc');
  console.assert(user.name, 'UserCard: user.name là bắt buộc', user);
  console.assert(
    typeof onEdit === 'function',
    'UserCard: onEdit phải là một hàm',
    { onEdit }
  );
  console.assert(
    ['light', 'dark'].includes(theme),
    'UserCard: chủ đề không hợp lệ',
    { theme }
  );

  // Render thành phần...
}

Những Lỗi Cần Tránh

  1. Không Sử Dụng Cho Xử Lý Lỗi: console.assert() không ném lỗi hoặc dừng thực thi. Nó chỉ ghi log. Để xử lý lỗi thực tế, hãy sử dụng throw:
    // Sai - mã vẫn tiếp tục thực thi
    console.assert(user.isAdmin, 'Phải là quản trị viên');
    deleteAllUsers(); // Vẫn chạy!
    
    // Đúng - dừng thực thi
    if (!user.isAdmin) throw new Error('Phải là quản trị viên');
    deleteAllUsers(); // Không chạy
    
  2. Xác Nhận Không Nên Có Tác Dụng Phụ: Đừng làm điều này:
    console.assert(userId = getUserId(), 'Không có ID người dùng'); // Gán trong xác nhận!
    
  3. Cẩn Thận Với Truthy/Falsy: Hãy nhớ rằng 0, ''null là falsy:
    console.assert(count, 'Count là bắt buộc'); // Lỗi khi count là 0!
    console.assert(count !== undefined, 'Count là bắt buộc'); // Tốt hơn
    

console.assert() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Các xác nhận hoạt động như tài liệu runtime và hệ thống cảnh báo sớm. Thay vì một lỗi bí ẩn xuất hiện sâu ba hàm, xác nhận bắt dữ liệu xấu ở điểm vào và cho bạn biết chính xác điều gì sai. Tôi sử dụng xác nhận rất nhiều trong quá trình phát triển. Chúng đã bắt vô số lỗi nơi tôi truyền đối số sai thứ tự, quên xử lý các trường hợp biên, hoặc đưa ra các giả định không chính xác về cấu trúc dữ liệu. Xác nhận thất bại ngay lập tức, ngay tại nơi vấn đề phát sinh, thay vì gây ra một lỗi khó hiểu sau này.


5. console.group() và console.groupEnd() – Sắp Xếp Output Console Của Bạn Như Một Chuyên Gia

Chức Năng Chính

Các phương thức này cho phép bạn tạo các nhóm có thể thu gọn trong output console của mình. Mọi thứ được ghi lại giữa console.group()console.groupEnd() sẽ được thụt lề và có thể thu gọn/mở rộng. Hãy nghĩ về nó như các thư mục trong hệ thống tệp, nhưng dành cho các log của bạn.

Vì Sao Bị Bỏ Qua?

Khi bạn chỉ ghi log vài thứ, việc tổ chức không quan trọng. Nhưng khi bạn ghi log các hoạt động phức tạp với nhiều bước, các lệnh gọi hàm lồng nhau hoặc gỡ lỗi các tính năng với nhiều bộ phận chuyển động, console của bạn trở thành một mớ hỗn độn không thể đọc được. Hầu hết các nhà phát triển không bao giờ học được rằng họ có thể tổ chức nó.

Cách Sử Dụng

console.group('Nhãn');
// ... các log ...
console.groupEnd();

// Hoặc sử dụng console.groupCollapsed() để bắt đầu thu gọn
console.groupCollapsed('Nhãn');
// ... các log ...
console.groupEnd();

Ví Dụ Thực Tế

Ví dụ 1: Nhóm Cơ Bản

console.group('Quy Trình Đăng Nhập Người Dùng');
console.log('Đang xác thực thông tin đăng nhập...');
console.log('Đang kiểm tra cơ sở dữ liệu...');
console.log('Đang tạo mã thông báo phiên...');
console.log('Đang đặt cookie...');
console.groupEnd();

console.group('Đang Tải Dữ Liệu Người Dùng');
console.log('Đang tìm nạp hồ sơ...');
console.log('Đang tìm nạp tùy chọn...');
console.log('Đang tìm nạp thông báo...');
console.groupEnd();

Bây giờ, thay vì một danh sách phẳng gồm 8 log, bạn có hai nhóm được tổ chức rõ ràng hiển thị các hoạt động khác nhau.

Ví dụ 2: Nhóm Lồng Nhau

console.group('Đang Xử Lý Đơn Hàng #12345');

  console.group('Xác Thực Mặt Hàng');
    console.log('Mặt hàng 1: Widget - $10.99 ✓');
    console.log('Mặt hàng 2: Gadget - $24.99 ✓');
    console.log('Tổng số mặt hàng: 2');
  console.groupEnd();

  console.group('Xử Lý Thanh Toán');
    console.log('Phương thức thanh toán: Thẻ Tín Dụng');
    console.log('Số tiền: $35.98');
    console.log('Trạng thái: Đã Phê Duyệt');
  console.groupEnd();

  console.group('Cập Nhật Kho Hàng');
    console.log('Giảm số lượng Widget: 150 → 149');
    console.log('Giảm số lượng Gadget: 75 → 74');
  console.groupEnd();

console.groupEnd();

Điều này tạo ra một cấu trúc phân cấp cực kỳ dễ đọc và dễ hiểu.

Ví dụ 3: Gỡ Lỗi Cuộc Gọi API

async function fetchUserData(userId) {
  console.group(`Gọi API: GET /users/${userId}`);

  console.log('Yêu cầu được khởi tạo vào:', new Date().toISOString());

  try {
    const response = await fetch(`/api/users/${userId}`);

    console.group('Chi Tiết Phản Hồi');
      console.log('Trạng thái:', response.status);
      console.log('Headers:', response.headers);
    console.groupEnd();

    const data = await response.json();

    console.group('Dữ Liệu Phản Hồi');
      console.table(data);
    console.groupEnd();

    console.log('✓ Yêu cầu hoàn thành thành công');

  } catch (error) {
    console.error('✗ Yêu cầu thất bại:', error);
  }

  console.groupEnd();
}

Ví dụ 4: Vòng Đời Thành Phần React

class UserProfile extends React.Component {
  componentDidMount() {
    console.group(`${this.constructor.name} Lifecycle`);
    console.log('componentDidMount');
    this.loadUserData();
    console.groupEnd();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      console.group(`${this.constructor.name} Update`);
      console.log('Người dùng đã thay đổi:', prevProps.userId, '→', this.props.userId);
      this.loadUserData();
      console.groupEnd();
    }
  }

  loadUserData() {
    console.group('Đang Tải Dữ Liệu Người Dùng');
    console.log('ID Người dùng:', this.props.userId);
    // ... logic tìm nạp ...
    console.groupEnd();
  }
}

Mẹo Chuyên Nghiệp

  1. Sử Dụng groupCollapsed Cho Thông Tin Không Quan Trọng: Bắt đầu các nhóm thu gọn theo mặc định để giữ console của bạn sạch sẽ:
    console.groupCollapsed('Thông Tin Gỡ Lỗi Chi Tiết');
    console.log('Sử dụng bộ nhớ:', performance.memory.usedJSHeapSize);
    console.log('Trạng thái mạng:', navigator.onLine);
    console.groupEnd();
    
  2. Luôn Ghép group() và groupEnd(): Sử dụng try/finally để đảm bảo dọn dẹp:
    console.group('Hoạt Động Quan Trọng');
    try {
      riskyOperation();
    } finally {
      console.groupEnd(); // Luôn đóng ngay cả khi lỗi được ném ra
    }
    
  3. Thêm Chỉ Báo Hình Ảnh: Sử dụng biểu tượng cảm xúc hoặc ký hiệu để làm cho các nhóm dễ quét:
    console.group('⚠️ Lỗi Xác Thực');
    console.group('✓ Hoạt Động Thành Công');
    console.group('🔍 Thông Tin Gỡ Lỗi');
    

Các Trường Hợp Sử Dụng Nâng Cao

Theo Dõi Hàm Tự Động

function trace(fn, name) {
  return function(...args) {
    console.group(`Hàm: ${name || fn.name}`);
    console.log('Đối số:', args);

    try {
      const result = fn.apply(this, args);
      console.log('Giá trị trả về:', result);
      return result;
    } catch (error) {
      console.error('Lỗi:', error);
      throw error;
    } finally {
      console.groupEnd();
    }
  };
}

const tracedAdd = trace((a, b) => a + b, 'add');
tracedAdd(5, 3);

// Output:
// Hàm: add
//   Đối số: [5, 3]
//   Giá trị trả về: 8

Ghi Log Action Redux

const actionLogger = store => next => action => {
  console.group(`Action: ${action.type}`);
  console.log('Payload:', action.payload);
  console.log('Trạng thái trước:', store.getState());

  const result = next(action);

  console.log('Trạng thái mới:', store.getState());
  console.groupEnd();

  return result;
};

Tổ Chức Bộ Kiểm Thử

function describe(suiteName, tests) {
  console.group(`Bộ Kiểm Thử: ${suiteName}`);
  tests();
  console.groupEnd();
}

function it(testName, testFn) {
  try {
    testFn();
    console.log(`✓ ${testName}`);
  } catch (error) {
    console.error(`✗ ${testName}:`, error);
  }
}

describe('Máy Tính', () => {
  it('nên cộng các số', () => {
    console.assert(add(2, 3) === 5);
  });

  it('nên nhân các số', () => {
    console.assert(multiply(2, 3) === 6);
  });
});

Lập Hồ Sơ Hiệu Suất Với Nhóm

class PerformanceMonitor {
  static start(label) {
    console.group(`⏱️ ${label}`);
    console.time(label);
  }

  static end(label) {
    console.timeEnd(label);
    console.groupEnd();
  }

  static checkpoint(message) {
    console.log(`  ↳ ${message}`);
  }
}

PerformanceMonitor.start('Tải Trang');
PerformanceMonitor.checkpoint('DOM Sẵn Sàng');
// ... tải tài sản ...
PerformanceMonitor.checkpoint('Tài Sản Đã Tải');
// ... khởi tạo ứng dụng ...
PerformanceMonitor.checkpoint('Ứng Dụng Đã Khởi Tạo');
PerformanceMonitor.end('Tải Trang');

Những Lỗi Cần Tránh

  1. Quên Đóng Nhóm: Các nhóm không đóng sẽ thụt lề mọi thứ sau đó:
    console.group('Quy Trình A');
    // ... các log ...
    // Quên console.groupEnd()!
    
    console.log('Điều này sẽ bị thụt lề không chính xác');
    
  2. Lồng Quá Sâu: Hơn 3-4 cấp độ sâu sẽ khó đọc:
    // Quá sâu!
    console.group('Cấp 1');
      console.group('Cấp 2');
        console.group('Cấp 3');
          console.group('Cấp 4');
            console.group('Cấp 5'); // Không ai muốn mở rộng nhiều cấp độ như vậy
    
  3. Nhóm Các Mục Đơn Lẻ: Đừng tạo một nhóm cho một log duy nhất:
    // Vô nghĩa
    console.group('Người Dùng');
    console.log(user);
    console.groupEnd();
    
    // Chỉ cần làm điều này
    console.log('Người Dùng:', user);
    
  4. Không Sử Dụng groupCollapsed: Nếu bạn đang ghi log rất nhiều thông tin gỡ lỗi, hãy bắt đầu thu gọn:
    // Điều này sẽ làm lộn xộn console của bạn
    console.group('Thông Tin Yêu Cầu Chi Tiết');
    // ... 50 dòng log ...
    console.groupEnd();
    
    // Điều này giữ cho nó sạch sẽ
    console.groupCollapsed('Thông Tin Yêu Cầu Chi Tiết');
    // ... 50 dòng log ...
    console.groupEnd();
    

console.group() & console.groupEnd() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Hãy tưởng tượng việc gỡ lỗi một luồng thanh toán phức tạp với xử lý thanh toán, cập nhật kho hàng, thông báo email và theo dõi phân tích. Không có nhóm, bạn có hơn 100 câu lệnh log tất cả trộn lẫn với nhau. Bạn đang cuộn, tìm kiếm, cố gắng tìm ra log nào thuộc hoạt động nào.

Với các nhóm, bạn sẽ thấy:

  • 📦 Xử Lý Đơn Hàng (thu gọn)
  • 💳 Thanh Toán (mở rộng vì đó là nơi có lỗi)
    • Xác thực thẻ
    • Xử lý phí ← lỗi ở đây
    • Tạo biên lai
  • 📧 Thông Báo Email (thu gọn)
  • 📊 Phân Tích (thu gọn)

Bạn ngay lập tức tập trung vào nhóm Thanh Toán, bỏ qua mọi thứ khác và tìm thấy lỗi của mình trong vài giây thay vì vài phút.


6. console.dir() – Khám Phá Sâu Các Thuộc Tính Đối Tượng

Chức Năng Chính

console.dir() hiển thị một danh sách tương tác các thuộc tính của một đối tượng. Không giống như console.log(), vốn cố gắng hiển thị các đối tượng theo cách “đẹp mắt” (đặc biệt đối với các phần tử DOM), console.dir() luôn hiển thị biểu diễn đối tượng JavaScript với tất cả các thuộc tính và phương thức của nó.

Vì Sao Bị Bỏ Qua?

console.log() hoạt động tốt cho hầu hết các đối tượng, vì vậy mọi người không bao giờ khám phá các lựa chọn thay thế. Nhưng console.log() có định dạng đặc biệt cho một số loại (nút DOM, mảng, v.v.) mà đôi khi ẩn đi những gì bạn cần xem. console.dir() cung cấp cho bạn cấu trúc đối tượng thô, không lọc.

Cách Sử Dụng

console.dir(object, options);

Tham số options (chủ yếu được sử dụng trong Node.js) có thể bao gồm độ sâu, màu sắc, v.v. Trong trình duyệt, nó thường chỉ là console.dir(object).

Ví Dụ Thực Tế

Ví dụ 1: Các Phần Tử DOM

const button = document.querySelector('button');

console.log(button);
// Hiển thị: <button class="btn">Click me</button>
// Trông giống HTML, hiển thị phần tử trực quan

console.dir(button);
// Hiển thị: button {className: "btn", innerHTML: "Click me", onclick: null, ...}
// Hiển thị tất cả các thuộc tính và phương thức của đối tượng nút

Điều này cực kỳ hữu ích khi bạn cần xem các thuộc tính và phương thức có sẵn trên một phần tử DOM.

Ví dụ 2: Hàm

function greet(name) {
  return `Xin chào, ${name}!`;
}

console.log(greet);
// Hiển thị: ƒ greet(name) { return `Xin chào, ${name}!`; }

console.dir(greet);
// Hiển thị tất cả các thuộc tính hàm:
// {
//   length: 1,
//   name: "greet",
//   arguments: null,
//   caller: null,
//   prototype: {constructor: ƒ},
//   __proto__: ƒ ()
// }

Bây giờ bạn có thể thấy độ dài của hàm (số lượng tham số), prototype của nó và các siêu dữ liệu khác.

Ví dụ 3: Các Thể Hiện Lớp (Class Instances)

class User {
  constructor(name) {
    this.name = name;
    this.createdAt = new Date();
  }

  greet() {
    return `Xin chào, ${this.name}`;
  }
}

const user = new User('Alice');

console.log(user);
// User {name: "Alice", createdAt: Mon Dec 02 2024...}

console.dir(user);
// Cây có thể mở rộng hiển thị:
// - name: "Alice"
// - createdAt: Đối tượng Date
// - __proto__: User
//   - greet: ƒ greet()
//   - constructor: ƒ User(name)
//   - __proto__: Object

Điều này tiết lộ chuỗi prototype, hiển thị chính xác nơi các phương thức được định nghĩa.

Ví dụ 4: Mảng Với Các Thuộc Tính Bổ Sung

const arr = [1, 2, 3];
arr.customProp = 'custom value';
arr.customMethod = () => 'custom';

console.log(arr);
// Hiển thị: [1, 2, 3]
// Ẩn các thuộc tính tùy chỉnh!

console.dir(arr);
// Hiển thị:
// Array(3)
//   0: 1
//   1: 2
//   2: 3
//   customProp: "custom value"
//   customMethod: ƒ ()
//   length: 3
//   __proto__: Array

Ví dụ 5: Kiểm Tra Các Đối Tượng Tích Hợp Sẵn

console.dir(document);
// Hiển thị tất cả các thuộc tính và phương thức của đối tượng document
// Hữu ích để khám phá những gì có sẵn

console.dir(window.localStorage);
// Hiển thị chuỗi prototype và các phương thức của localStorage

console.dir(Promise);
// Hiển thị các thuộc tính của hàm tạo Promise:
// all, allSettled, any, race, reject, resolve, v.v.

Mẹo Chuyên Nghiệp

  1. Sử Dụng Để Khám Phá API: Khi làm việc với các thư viện không quen thuộc:
    import * as THREE from 'three';
    console.dir(THREE);
    // Xem tất cả các exports có sẵn và cấu trúc của chúng
    
  2. So Sánh log() và dir(): Khi bối rối về cấu trúc của một đối tượng:
    console.log('log:', myObject);
    console.dir(myObject);
    // So sánh các output để hiểu điều gì đang xảy ra
    
  3. Kiểm Tra Đối Tượng Sự Kiện: Các sự kiện có rất nhiều thuộc tính:
    button.addEventListener('click', (event) => {
      console.dir(event);
      // Xem tất cả các thuộc tính sự kiện: target, currentTarget, clientX, clientY, v.v.
    });
    
  4. Tùy Chọn Node.js: Trong Node.js, bạn có thể kiểm soát độ sâu:
    console.dir(deeplyNestedObject, { depth: null }); // Hiển thị mọi thứ
    console.dir(deeplyNestedObject, { depth: 2 });    // Giới hạn độ sâu
    

Các Trường Hợp Sử Dụng Nâng Cao

Gỡ Lỗi Proxies và Các Đối Tượng Được Bao Bọc

const handler = {
  get(target, prop) {
    console.log(`Đang lấy ${prop}`);
    return target[prop];
  }
};

const proxy = new Proxy({ name: 'Alice' }, handler);

console.log(proxy);    // Có thể kích hoạt proxy traps
console.dir(proxy);    // Hiển thị cấu trúc proxy mà không kích hoạt traps

Kiểm Tra Các Trình Lặp Tùy Chỉnh

const range = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    return {
      current: this.from,
      last: this.to,
      next() {
        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

console.dir(range);
// Hiển thị phương thức Symbol.iterator và tất cả các thuộc tính

Hiểu Rõ Kế Thừa

class Animal {
  eat() { return 'eating'; }
}

class Dog extends Animal {
  bark() { return 'woof'; }
}

const dog = new Dog();
console.dir(dog);

// Chế độ xem có thể mở rộng hiển thị:
// Dog {}
//   __proto__: Animal
//     bark: ƒ bark()
//     constructor: class Dog
//     __proto__: Object
//       eat: ƒ eat()
//       constructor: class Animal

Những Lỗi Cần Tránh

  1. Không Sử Dụng Cho Giá Trị Đơn Giản: Đối với các kiểu dữ liệu nguyên thủy, nó là quá mức:
    console.dir(42);           // Chỉ hiển thị: 42
    console.dir("hello");      // Chỉ hiển thị: "hello"
    console.log(42);           // Cùng output, ngữ nghĩa hơn
    
  2. Hãy Nhớ Rằng Nó Chỉ Để Đọc: Bạn không thể chỉnh sửa giá trị trong output console.dir() (giống như console thông thường):
    console.dir(user); // Không thể nhấp và chỉnh sửa user.name
    
  3. Đừng Mong Đợi Output Giống Hệt Nhau Giữa Các Trình Duyệt: Chrome, Firefox và Safari hiển thị các đối tượng khác nhau.

console.dir() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Bạn đang sử dụng một thư viện bên thứ ba và cần biết các phương thức nào có sẵn. Bạn có thể đọc tài liệu (nếu có và cập nhật), hoặc bạn có thể làm:

import { SomeClass } from 'mystery-library';
const instance = new SomeClass();
console.dir(instance);

Boom. Bạn thấy mọi thuộc tính và phương thức, chuỗi prototype, các thuộc tính được kế thừa—mọi thứ. Bạn phát hiện ra rằng instance.reset() tồn tại, điều này không được ghi lại. Vấn đề đã được giải quyết.

Hoặc bạn đang gỡ lỗi một vấn đề DOM và console.log(element) hiển thị HTML đẹp mắt, nhưng bạn cần biết offsetWidth, scrollTop, hoặc các trình xử lý sự kiện của nó. console.dir(element) tiết lộ tất cả.


7. console.count() và console.countReset() – Theo Dõi Số Lần Gọi Hàm

Chức Năng Chính

console.count() ghi lại số lần nó đã được gọi với một nhãn cụ thể. console.countReset() đặt lại bộ đếm. Nó giống như có một bộ đếm nhấp chuột tích hợp sẵn cho mã của bạn.

Vì Sao Bị Bỏ Qua?

Hầu hết các nhà phát triển theo dõi số lượt gọi theo cách thủ công bằng các biến:

let callCount = 0;
function myFunction() {
  callCount++;
  console.log('Được gọi', callCount, 'lần');
}

Nó hoạt động, nhưng nó dài dòng và bạn cần quản lý trạng thái. console.count() làm điều này chỉ trong một dòng mà không cần thêm biến nào.

Cách Sử Dụng

console.count(label);        // Tăng và ghi lại số lượt gọi
console.countReset(label);   // Đặt lại bộ đếm về 0

Nếu bạn không cung cấp nhãn, nó sẽ sử dụng `’default’`.

Ví Dụ Thực Tế

Ví dụ 1: Đếm Cơ Bản

function handleClick() {
  console.count('số lần nhấp nút');
  // Xử lý click...
}

button.addEventListener('click', handleClick);

// Lần nhấp đầu tiên:  số lần nhấp nút: 1
// Lần nhấp thứ hai: số lần nhấp nút: 2
// Lần nhấp thứ ba:  số lần nhấp nút: 3

Ví dụ 2: Theo Dõi Các Lệnh Gọi Hàm

function fetchData(endpoint) {
  console.count(`Gọi API đến ${endpoint}`);
  return fetch(endpoint);
}

fetchData('/users');      // Gọi API đến /users: 1
fetchData('/posts');      // Gọi API đến /posts: 1
fetchData('/users');      // Gọi API đến /users: 2
fetchData('/users');      // Gọi API đến /users: 3

Điều này ngay lập tức cho bạn thấy các endpoint nào đang được gọi thường xuyên nhất.

Ví dụ 3: Gỡ Lỗi Vòng Lặp Vô Hạn Hoặc Đệ Quy

function problematicRecursion(n) {
  console.count('độ sâu đệ quy');

  if (n <= 0) return;

  // Oops, quên giảm!
  problematicRecursion(n);
}

problematicRecursion(5);

// Output:
// độ sâu đệ quy: 1
// độ sâu đệ quy: 2
// độ sâu đệ quy: 3
// ... tiếp tục ...

Bạn sẽ ngay lập tức thấy bộ đếm tăng và biết bạn có vấn đề về đệ quy.

Ví dụ 4: Đếm Lượt Render của React

function UserProfile({ userId }) {
  console.count(`UserProfile render cho người dùng ${userId}`);

  // Logic thành phần...
  return <div>Profile</div>;
}

// Output hiển thị chính xác bao nhiêu lần và cho người dùng nào:
// UserProfile render cho người dùng 123: 1
// UserProfile render cho người dùng 123: 2
// UserProfile render cho người dùng 456: 1

Ví dụ 5: Theo Dõi Trình Xử Lý Sự Kiện

input.addEventListener('input', () => {
  console.count('sự kiện input');
});

input.addEventListener('change', () => {
  console.count('sự kiện change');
});

// Gõ "hello":
// sự kiện input: 1
// sự kiện input: 2
// sự kiện input: 3
// sự kiện input: 4
// sự kiện input: 5
// sự kiện change: 1 (khi bạn blur)

Mẹo Chuyên Nghiệp

  1. Sử Dụng Nhãn Mô Tả: Hãy đặt nhãn có ý nghĩa:
    // Xấu
    console.count('x');
    
    // Tốt
    console.count('thất bại-xác-thực');
    console.count('truy-cập-cache');
    console.count('truy-vấn-cơ-sở-dữ-liệu');
    
  2. Đặt Lại Khi Cần: Xóa số đếm giữa các lần chạy thử:
    function runTest() {
      console.countReset('xác-nhận-kiểm-thử');
      // Chạy kiểm thử...
    }
    
    function assert(condition) {
      if (!condition) {
        console.count('xác-nhận-kiểm-thử');
      }
    }
    
  3. Kết Hợp Với Điều Kiện: Chỉ đếm một số kịch bản nhất định:
    function processData(data) {
      if (data.size > 10000) {
        console.count('tập-dữ-liệu-lớn');
      }
    
      if (data.hasErrors) {
        console.count('lỗi-dữ-liệu');
      }
    }
    
  4. Theo Dõi Nhiều Chỉ Số Đồng Thời:
    function handleRequest(req) {
      console.count('tổng-yêu-cầu');
    
      if (req.method === 'GET') {
        console.count('yêu-cầu-get');
      } else if (req.method === 'POST') {
        console.count('yêu-cầu-post');
      }
    
      if (req.authenticated) {
        console.count('yêu-cầu-đã-xác-thực');
      } else {
        console.count('yêu-cầu-ẩn-danh');
      }
    }
    

Các Trường Hợp Sử Dụng Nâng Cao

Phát Hiện Giới Hạn Tốc Độ

let requestCount = 0;

async function apiCall() {
  console.count('Số yêu cầu API trong phiên này');

  requestCount++;
  if (requestCount > 100) {
    console.warn('Đang tiếp cận giới hạn tốc độ!');
  }

  // Thực hiện yêu cầu...
}

Phát Hiện Rò Rỉ Bộ Nhớ

class ResourceManager {
  constructor() {
    console.count('Số lượng thể hiện ResourceManager được tạo');
  }

  destroy() {
    console.count('Số lượng thể hiện ResourceManager bị hủy');
  }
}

// Nếu số lượng tạo ra vượt xa số lượng bị hủy, bạn có một rò rỉ

Theo Dõi Kiểm Thử A/B

function showFeature(variant) {
  console.count(`biến-thể-tính-năng-${variant}`);

  if (variant === 'A') {
    showVariantA();
  } else {
    showVariantB();
  }
}

// Kiểm tra nhanh trực quan phân phối biến thể:
// biến-thể-tính-năng-A: 523
// biến-thể-tính-năng-B: 477

Gỡ Lỗi Polling

let pollCount = 0;
const MAX_POLLS = 10;

async function pollForResult() {
  console.count('lần-thử-polling');
  pollCount++;

  const result = await checkStatus();

  if (result.complete) {
    console.log(`✓ Hoàn thành sau ${pollCount} lần polling`);
    console.countReset('lần-thử-polling');
    return result;
  }

  if (pollCount < MAX_POLLS) {
    setTimeout(pollForResult, 1000);
  } else {
    console.error('Đã đạt đến số lần polling tối đa');
  }
}

Những Lỗi Cần Tránh

  1. Không Sử Dụng Cho Chỉ Số Sản Xuất: Đây là các công cụ phát triển. Đối với sản xuất, hãy sử dụng phân tích phù hợp:
    // Phát triển: OK
    console.count('đăng-ký-người-dùng');
    
    // Sản xuất: Sử dụng dịch vụ phân tích
    analytics.track('đăng-ký-người-dùng');
    
  2. Hãy Nhớ Nhãn Phân Biệt Chữ Hoa/Thường:
    console.count('MyLabel');
    console.count('mylabel');
    // Đây là các bộ đếm KHÁC NHAU!
    
  3. Số Lượng Đếm Vẫn Tồn Tại: Các bộ đếm không tự động đặt lại giữa các lệnh gọi hàm:
    function processItems(items) {
      items.forEach(item => {
        console.count('item-đã-xử-lý');
      });
      // Bộ đếm tiếp tục tăng trên nhiều lệnh gọi processItems
    }
    
    // Nếu bạn muốn đặt lại:
    function processItems(items) {
      console.countReset('item-đã-xử-lý');
      items.forEach(item => {
        console.count('item-đã-xử-lý');
      });
    }
    

console.count() & console.countReset() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Bạn nghi ngờ một hàm đang được gọi thường xuyên hơn mức cần thiết. Thay vì thêm một biến đếm, khởi tạo nó, tăng nó và ghi log, bạn chỉ cần thêm một dòng:

console.count('hamNghiNgo');

Ngay lập tức, bạn thấy nó đang được gọi 50 lần khi đáng lẽ chỉ nên gọi một lần. Lỗi được tìm thấy trong 10 giây.

Hoặc bạn đang gỡ lỗi các trình nghe sự kiện và không thể tìm ra liệu cả hai trình nghe có đang kích hoạt hay không. Thêm console.count() vào mỗi trình nghe và xem output. Nếu bạn chỉ thấy số đếm cho một, bạn biết trình nghe kia không được gắn đúng cách.


8. console.clear() – Dọn Dẹp Console Của Bạn

Chức Năng Chính

console.clear() xóa tất cả output console. Nó giống như nhấn nút “clear” trong DevTools, nhưng từ mã của bạn.

Vì Sao Bị Bỏ Qua?

Hầu hết các nhà phát triển tự xóa console bằng nút xóa của trình duyệt hoặc các phím tắt DevTools. Họ không nhận ra họ có thể làm điều đó theo chương trình, điều này cho phép một số mẫu gỡ lỗi mạnh mẽ.

Cách Sử Dụng

console.clear();

Chỉ vậy thôi. Không có tham số, không phức tạp.

Ví Dụ Thực Tế

Ví dụ 1: Xóa Trước Các Hoạt Động Lớn

function runDiagnostics() {
  console.clear();
  console.log('=== Đang Chạy Chẩn Đoán Hệ Thống ===');

  checkDatabase();
  checkAPI();
  checkCache();

  console.log('=== Chẩn Đoán Hoàn Thành ===');
}

Bây giờ mỗi lần chạy chẩn đoán bắt đầu với một bảng sạch, giúp dễ dàng xem output của lần chạy hiện tại.

Ví dụ 2: Xóa Khi Chuyển Trang (Single Page Application)

// Trong một Single Page Application
router.on('navigate', (route) => {
  if (process.env.NODE_ENV === 'development') {
    console.clear();
    console.log(`Đã chuyển đến: ${route.path}`);
  }
});

Mỗi route nhận được output console mới, ngăn ngừa sự nhầm lẫn giữa các trang khác nhau.

Ví dụ 3: Gỡ Lỗi Vòng Lặp Trò Chơi

function gameLoop() {
  // Xóa console mỗi khung hình để gỡ lỗi thời gian thực
  console.clear();

  console.log('=== Khung Hình', frameCount, '===');
  console.log('Vị Trí Người Chơi:', player.x, player.y);
  console.log('Số Lượng Kẻ Thù:', enemies.length);
  console.log('Điểm Số:', score);
  console.log('FPS:', calculateFPS());

  requestAnimationFrame(gameLoop);
}

Bạn có một hiển thị trực tiếp, cập nhật trạng thái trò chơi mà không làm lộn xộn console.

Ví dụ 4: Đặt Lại Bộ Kiểm Thử

function runTestSuite(tests) {
  console.clear();
  console.log('╔════════════════════════════╗');
  console.log('║   Bộ Kiểm Thử Đang Bắt Đầu ║');
  console.log('╚════════════════════════════╝');

  tests.forEach(test => {
    try {
      test();
      console.log(`✓ ${test.name}`);
    } catch (error) {
      console.error(`✗ ${test.name}:`, error);
    }
  });
}

Ví dụ 5: Quản Lý Console Chế Độ Phát Triển

class DevConsole {
  static enabled = process.env.NODE_ENV === 'development';

  static section(title) {
    if (!this.enabled) return;

    console.clear();
    console.log('═'.repeat(50));
    console.log(title.toUpperCase().padStart(25 + title.length / 2));
    console.log('═'.repeat(50));
  }
}

// Sử dụng
DevConsole.section('Xác Thực Người Dùng');
console.log('Đang kiểm tra thông tin đăng nhập...');
// ... logic xác thực ...

DevConsole.section('Đang Tải Bảng Điều Khiển');
console.log('Đang tìm nạp dữ liệu người dùng...');
// ... logic bảng điều khiển ...

Mẹo Chuyên Nghiệp

  1. Xóa Có Điều Kiện: Chỉ xóa trong quá trình phát triển:
    if (process.env.NODE_ENV === 'development') {
      console.clear();
    }
    
  2. Giữ Lại Các Log Quan Trọng: Ghi log thông tin quan trọng sau khi xóa:
    console.clear();
    console.log('Phiên Bản Ứng Dụng:', APP_VERSION);
    console.log('Môi Trường:', process.env.NODE_ENV);
    console.log('ID Người Dùng:', currentUser.id);
    console.log('─'.repeat(50));
    // Bây giờ bắt đầu ghi log thực tế của bạn
    
  3. Tạo Điểm Xóa Trong Các Hoạt Động Dài:
    async function longProcess() {
      console.clear();
      console.log('Giai Đoạn 1: Thu Thập Dữ Liệu');
      await collectData();
    
      console.clear();
      console.log('Giai Đoạn 2: Xử Lý');
      await processData();
    
      console.clear();
      console.log('Giai Đoạn 3: Output');
      await generateOutput();
    }
    
  4. Thay Thế Phím Tắt Bàn Phím: Gắn vào một phím để xóa nhanh:
    window.addEventListener('keydown', (e) => {
      if (e.ctrlKey && e.key === 'l') {
        e.preventDefault();
        console.clear();
        console.log('Console đã được xóa thủ công');
      }
    });
    

Các Trường Hợp Sử Dụng Nâng Cao

Màn Hình Giám Sát Dữ Liệu Trực Tiếp

class Monitor {
  constructor(interval = 1000) {
    this.metrics = {};
    this.interval = interval;
  }

  start() {
    this.timer = setInterval(() => {
      console.clear();
      console.log('═══ CHỈ SỐ TRỰC TIẾP ═══');
      console.log('Cập nhật:', new Date().toLocaleTimeString());
      console.log('');
      console.table(this.metrics);
    }, this.interval);
  }

  update(key, value) {
    this.metrics[key] = value;
  }

  stop() {
    clearInterval(this.timer);
  }
}

const monitor = new Monitor();
monitor.start();

// Ở đâu đó trong ứng dụng của bạn:
monitor.update('Người Dùng Hoạt Động', getActiveUsers());
monitor.update('Sử Dụng Bộ Nhớ', getMemoryUsage());
monitor.update('Độ Trễ API', getAPILatency());

Menu Gỡ Lỗi Tương Tác

const debugMenu = {
  showState() {
    console.clear();
    console.log('📊 TRẠNG THÁI ỨNG DỤNG');
    console.log('Người Dùng:', currentUser);
    console.log('Route:', currentRoute);
    console.log('Store:', store.getState());
  },

  showPerformance() {
    console.clear();
    console.log('⚡ CHỈ SỐ HIỆU SUẤT');
    console.table(performance.getEntries());
  },

  showNetwork() {
    console.clear();
    console.log('🌐 YÊU CẦU MẠNG');
    // Ghi log thống kê mạng
  }
};

// Truy cập từ console trình duyệt:
// debugMenu.showState()
// debugMenu.showPerformance()

Output Console Động

const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
let frameIndex = 0;

function showLoader(message) {
  const interval = setInterval(() => {
    console.clear();
    console.log(`${frames[frameIndex]} ${message}`);
    frameIndex = (frameIndex + 1) % frames.length;
  }, 80);

  return () => {
    clearInterval(interval);
    console.clear();
  };
}

// Sử dụng
const stopLoader = showLoader('Đang tải dữ liệu...');
await fetchData();
stopLoader();
console.log('✓ Dữ liệu đã tải');

Những Lỗi Cần Tránh

  1. Không Xóa Trong Sản Xuất: Xóa console.clear() khỏi các bản dựng sản xuất:
    // Điều này sẽ làm phiền người dùng gỡ lỗi trang web của bạn
    setInterval(() => console.clear(), 1000); // KHÔNG LÀM ĐIỀU NÀY
    
  2. Không Xóa Các Lỗi Quan Trọng: Cẩn thận không xóa các thông báo lỗi quan trọng:
    try {
      riskyOperation();
    } catch (error) {
      console.error(error); // Ghi log lỗi trước
      console.clear();       // Bây giờ nó đã biến mất! Xấu!
    }
    
  3. Xóa Quá Nhiều = Console Khó Đọc: Đừng xóa quá thường xuyên:
    // Xấu: xóa mỗi lần lặp
    for (let i = 0; i < 100; i++) {
      console.clear();
      console.log(i); // Quá nhanh để đọc!
    }
    
  4. Hạn Chế Trình Duyệt: Một số trình duyệt hiển thị thông báo “Console was cleared”. Người dùng có thể thấy điều này khó chịu.

console.clear() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Khi gỡ lỗi các quy trình làm việc phức tạp với nhiều bước, bạn không muốn cuộn qua hàng trăm dòng log từ các lần chạy trước. console.clear() ở đầu phiên gỡ lỗi của bạn mang lại cho bạn một bảng sạch.

Hoặc hãy tưởng tượng việc gỡ lỗi một tính năng thời gian thực như ứng dụng trò chuyện hoặc nguồn cấp dữ liệu trực tiếp. Các tin nhắn cũ làm lộn xộn console của bạn. Thêm console.clear() trước khi ghi log các tin nhắn mới sẽ giữ mọi thứ dễ đọc.

Tôi sử dụng điều này liên tục khi gỡ lỗi các vòng lặp hoạt hình hoặc logic trò chơi nơi tôi cần xem trạng thái hiện tại mà không có tiếng ồn từ các log của mọi khung hình trước đó.


9. console.warn() và console.error() – Ghi Log Ngữ Nghĩa Thực Sự Quan Trọng

Chức Năng Chính

console.warn() ghi lại các cảnh báo (màu vàng trong hầu hết các trình duyệt) và console.error() ghi lại các lỗi (màu đỏ). Chúng hoạt động giống như console.log() nhưng với các kiểu hình ảnh và ý nghĩa ngữ nghĩa khác nhau.

Vì Sao Bị Bỏ Qua?

Nhiều nhà phát triển sử dụng console.log() cho mọi thứ. Họ nghĩ rằng việc tạo kiểu không quan trọng, hoặc họ không nhận ra các trình duyệt xử lý các phương thức này khác nhau. Nhưng việc sử dụng đúng phương thức cải thiện khả năng đọc, cho phép lọc và cung cấp các stack traces tốt hơn.

Cách Sử Dụng

console.warn(message, ...optionalData);
console.error(message, ...optionalData);

Cả hai đều chấp nhận nhiều đối số giống như console.log().

Ví Dụ Thực Tế

Ví dụ 1: Cảnh Báo Không Dùng Nữa

function oldAPIMethod(data) {
  console.warn(
    'oldAPIMethod() không dùng nữa và sẽ bị xóa trong v3.0. Hãy sử dụng newAPIMethod() thay thế.',
    'Được gọi với:', data
  );

  // Vẫn thực thi để tương thích ngược
  return legacyLogic(data);
}

Cảnh báo màu vàng thu hút sự chú ý mà không dừng thực thi.

Ví dụ 2: Vấn Đề Cấu Hình

function initializeApp(config) {
  if (!config.apiKey) {
    console.error('NGHIÊM TRỌNG: Thiếu khóa API. Ứng dụng sẽ không hoạt động đúng.');
    throw new Error('Thiếu khóa API');
  }

  if (!config.analyticsId) {
    console.warn('CẢNH BÁO: ID phân tích không được cung cấp. Phân tích sẽ bị vô hiệu hóa.');
  }

  if (config.debugMode) {
    console.warn('Ứng dụng đang chạy ở chế độ gỡ lỗi. Hiệu suất có thể bị giảm.');
  }

  // Khởi tạo...
}

Lỗi có màu đỏ và nghiêm trọng. Cảnh báo có màu vàng và mang tính thông tin.

Ví dụ 3: Lỗi Xác Thực

function validateUser(user) {
  const errors = [];
  const warnings = [];

  if (!user.email) {
    errors.push('Email là bắt buộc');
  } else if (!isValidEmail(user.email)) {
    errors.push('Định dạng email không hợp lệ');
  }

  if (!user.phone) {
    warnings.push('Số điện thoại được khuyến nghị nhưng không bắt buộc');
  }

  if (user.age < 13) {
    errors.push('Người dùng phải từ 13 tuổi trở lên');
  } else if (user.age < 18) {
    warnings.push('Người dùng dưới 18 tuổi - một số tính năng bị hạn chế');
  }

  if (errors.length > 0) {
    console.error('Xác thực người dùng thất bại:', errors);
    return false;
  }

  if (warnings.length > 0) {
    console.warn('Cảnh báo xác thực người dùng:', warnings);
  }

  return true;
}

Ví dụ 4: Cảnh Báo Hiệu Suất

function processLargeDataset(data) {
  if (data.length > 10000) {
    console.warn(
      `Đang xử lý ${data.length} mục có thể ảnh hưởng đến hiệu suất.`,
      'Hãy cân nhắc sử dụng phân trang hoặc cuộn ảo.'
    );
  }

  const start = performance.now();
  const result = process(data);
  const duration = performance.now() - start;

  if (duration > 1000) {
    console.error(
      `THAO TÁC CHẬM: Xử lý mất ${duration}ms`,
      'Điều này sẽ gây ra giật giao diện người dùng'
    );
  }

  return result;
}

Ví dụ 5: Xử Lý Lỗi API

async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      if (response.status === 404) {
        console.error(`Không tìm thấy người dùng ${userId}`);
      } else if (response.status >= 500) {
        console.error(`Lỗi máy chủ (${response.status}) khi tìm nạp người dùng`);
      } else {
        console.warn(`Phản hồi không mong muốn: ${response.status}`);
      }

      throw new Error(`HTTP ${response.status}`);
    }

    return await response.json();

  } catch (error) {
    console.error('Không tìm nạp được dữ liệu người dùng:', error);
    throw error;
  }
}

Mẹo Chuyên Nghiệp

  1. Stack Traces: console.error() tự động bao gồm một stack trace trong hầu hết các trình duyệt:
    function deepFunction() {
      console.error('Có gì đó không ổn ở đây');
      // Trình duyệt tự động hiển thị toàn bộ call stack
    }
    
  2. Lọc Theo Loại: DevTools cho phép bạn lọc output console theo loại. Sử dụng đúng phương thức làm cho việc lọc hiệu quả:
    // Trong DevTools, nhấp vào "Errors" để chỉ xem các lỗi này
    console.error('Vấn đề nghiêm trọng');
    console.error('Kết nối cơ sở dữ liệu thất bại');
    
    // Nhấp vào "Warnings" để chỉ xem các cảnh báo này
    console.warn('Đã sử dụng phương thức không dùng nữa');
    console.warn('Cảnh báo bộ nhớ thấp');
    
  3. Thêm Đối Tượng Ngữ Cảnh: Bao gồm dữ liệu liên quan để gỡ lỗi:
    console.error('Không lưu được người dùng', {
      userId: user.id,
      attemptedData: data,
      timestamp: new Date(),
      sessionId: getSessionId()
    });
    
  4. Sử Dụng Với Đối Tượng Lỗi: Truyền các đối tượng Error thực tế để có stack traces tốt hơn:
    try {
      riskyOperation();
    } catch (error) {
      console.error('Thao tác thất bại:', error);
      // Hiển thị thông báo lỗi VÀ stack trace
    }
    

Các Trường Hợp Sử Dụng Nâng Cao

Các Mức Độ Lỗi Tùy Chỉnh

const Logger = {
  ERROR: 'error',
  WARN: 'warn',
  INFO: 'info',
  DEBUG: 'debug',

  log(level, message, data) {
    const timestamp = new Date().toISOString();
    const prefix = `[${timestamp}] [${level.toUpperCase()}]`;

    switch(level) {
      case this.ERROR:
        console.error(prefix, message, data);
        // Gửi đến dịch vụ theo dõi lỗi
        sendToErrorTracking(message, data);
        break;
      case this.WARN:
        console.warn(prefix, message, data);
        break;
      default:
        console.log(prefix, message, data);
    }
  },

  error(message, data) { this.log(this.ERROR, message, data); },
  warn(message, data) { this.log(this.WARN, message, data); },
  info(message, data) { this.log(this.INFO, message, data); },
  debug(message, data) { this.log(this.DEBUG, message, data); }
};

// Sử dụng
Logger.error('Xử lý thanh toán thất bại', { orderId: 123, amount: 99.99 });
Logger.warn('Đang tiếp cận giới hạn tốc độ API', { remaining: 10, limit: 100 });

Ghi Log Giới Hạn Lỗi (React)

class ErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    console.error('React Error Boundary đã bắt một lỗi:', {
      error: error.toString(),
      componentStack: errorInfo.componentStack,
      timestamp: new Date().toISOString()
    });

    // Cũng có thể gửi đến dịch vụ theo dõi lỗi
  }

  render() {
    if (this.state.hasError) {
      return <ErrorFallback />;
    }
    return this.props.children;
  }
}

Leo Thang Lỗi Tăng Dần

class APIClient {
  constructor() {
    this.retryCount = 0;
    this.maxRetries = 3;
  }

  async request(url) {
    try {
      const response = await fetch(url);

      if (!response.ok) {
        this.retryCount++;

        if (this.retryCount === 1) {
          console.warn(`Yêu cầu thất bại (lần thử ${this.retryCount}), đang thử lại...`);
        } else if (this.retryCount < this.maxRetries) {
          console.warn(`Yêu cầu thất bại (lần thử ${this.retryCount}/${this.maxRetries})`);
        } else {
          console.error(`Yêu cầu thất bại sau ${this.maxRetries} lần thử`, {
            url,
            status: response.status
          });
          throw new Error('Đã vượt quá số lần thử tối đa');
        }

        await this.delay(1000 * this.retryCount);
        return this.request(url);
      }

      this.retryCount = 0; // Đặt lại khi thành công
      return response;

    } catch (error) {
      console.error('Lỗi mạng:', error);
      throw error;
    }
  }

  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

Ghi Log Tùy Theo Môi Trường

const isDev = process.env.NODE_ENV === 'development';
const isTest = process.env.NODE_ENV === 'test';

const logger = {
  error(...args) {
    console.error(...args);
    if (!isDev && !isTest) {
      sendToErrorTracking(...args);
    }
  },

  warn(...args) {
    if (isDev) {
      console.warn(...args);
    }
  },

  info(...args) {
    if (isDev) {
      console.log(...args);
    }
  }
};

// Bây giờ các cảnh báo và thông tin chỉ xuất hiện trong quá trình phát triển
logger.warn('API này chậm'); // Chỉ dev
logger.error('Lỗi nghiêm trọng'); // Luôn ghi log + theo dõi trong sản xuất

Phân Loại Lỗi Thông Minh

class ErrorHandler {
  static handle(error, context = {}) {
    // Lỗi mạng
    if (error instanceof TypeError && error.message.includes('fetch')) {
      console.error('Lỗi Mạng:', {
        message: 'Không thể kết nối đến máy chủ',
        context,
        originalError: error
      });
      return;
    }

    // Lỗi xác thực
    if (error.name === 'ValidationError') {
      console.warn('Lỗi Xác Thực:', {
        message: error.message,
        context
      });
      return;
    }

    // Lỗi quyền
    if (error.status === 403) {
      console.error('Quyền Bị Từ Chối:', {
        message: 'Người dùng thiếu quyền cần thiết',
        context
      });
      return;
    }

    // Lỗi không xác định
    console.error('Lỗi Không Mong Muốn:', {
      error,
      context,
      stack: error.stack
    });
  }
}

// Sử dụng
try {
  await saveData(data);
} catch (error) {
  ErrorHandler.handle(error, { userId: currentUser.id, action: 'save' });
}

Những Lỗi Cần Tránh

  1. Không Sử Dụng error() Cho Các Trường Hợp Không Phải Lỗi:
    // Xấu - đây không phải là lỗi
    console.error('Người dùng đã nhấp nút');
    
    // Tốt
    console.log('Người dùng đã nhấp nút');
    
  2. Không Lạm Dụng warn():
    // Quá nhiều cảnh báo = nhiễu
    console.warn('Đang xử lý...');
    console.warn('Bước 1 hoàn thành');
    console.warn('Bước 2 hoàn thành');
    
    // Chỉ cần sử dụng log() cho luồng bình thường
    console.log('Đang xử lý...');
    
  3. Không Bỏ Qua Lỗi Một Cách Im Lặng:
    // Xấu - lỗi bị bắt nhưng không được ghi log
    try {
      await criticalOperation();
    } catch (error) {
      // Không có gì - lỗi biến mất!
    }
    
    // Tốt
    try {
      await criticalOperation();
    } catch (error) {
      console.error('Thao tác quan trọng thất bại:', error);
      throw error; // Hoặc xử lý thích hợp
    }
    
  4. Không Ghi Log Dữ Liệu Nhạy Cảm:
    // Nguy hiểm - ghi log mật khẩu người dùng
    console.error('Đăng nhập thất bại', { username, password });
    
    // An toàn
    console.error('Đăng nhập thất bại', { username });
    

console.warn() & console.error() Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Sự phân biệt trực quan là rất mạnh mẽ. Khi console của bạn có 100 câu lệnh log, các lỗi màu đỏ tươi sẽ nổi bật ngay lập tức. Bạn không lãng phí thời gian đọc qua các log bình thường để tìm vấn đề.

Lọc còn mạnh mẽ hơn. Nhấp vào “Chỉ lỗi” trong DevTools và ngay lập tức chỉ thấy các vấn đề. Nhấp vào “Cảnh báo” để xem các vấn đề tiềm ẩn mà không phải là lỗi nghiêm trọng.

Hơn nữa, các dịch vụ theo dõi lỗi như Sentry tự động thu thập các lệnh gọi console.error() trong sản xuất. Sử dụng console.error() một cách chính xác có nghĩa là các lỗi sản xuất của bạn được tự động ghi lại vào hệ thống giám sát mà không cần thêm mã.


10. console.memory – Giám Sát Mức Sử Dụng Bộ Nhớ Theo Thời Gian Thực

Chức Năng Chính

console.memory (trong các trình duyệt dựa trên Chrome) cung cấp thông tin thời gian thực về mức sử dụng bộ nhớ heap JavaScript. Nó không phải là một phương thức—nó là một thuộc tính trả về một đối tượng với các thống kê bộ nhớ.

Vì Sao Bị Bỏ Qua?

Nó dành riêng cho Chrome và không được chuẩn hóa, vì vậy ít được biết đến. Ngoài ra, hầu hết các nhà phát triển không nghĩ về bộ nhớ cho đến khi họ có một rò rỉ bộ nhớ. Nhưng giám sát bộ nhớ chủ động giúp bạn phát hiện rò rỉ trước khi chúng trở thành vấn đề.

Cách Sử Dụng

console.log(console.memory);

// Trả về một cái gì đó giống như:
// {
//   jsHeapSizeLimit: 2190000000,  // Kích thước heap tối đa
//   totalJSHeapSize: 50000000,    // Tổng số đã cấp phát
//   usedJSHeapSize: 30000000      // Thực tế được sử dụng
// }

Tất cả các giá trị đều tính bằng byte.

Ví Dụ Thực Tế

Ví dụ 1: Kiểm Tra Bộ Nhớ Cơ Bản

function checkMemory() {
  const memory = console.memory;
  const used = (memory.usedJSHeapSize / 1048576).toFixed(2);
  const limit = (memory.jsHeapSizeLimit / 1048576).toFixed(2);
  const percentage = ((memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100).toFixed(2);

  console.log(`Bộ nhớ: ${used} MB / ${limit} MB (${percentage}%)`);
}

checkMemory();
// Output: Bộ nhớ: 45.23 MB / 2089.00 MB (2.17%)

Ví dụ 2: Giám Sát Bộ Nhớ Theo Thời Gian

function startMemoryMonitor(interval = 5000) {
  const readings = [];

  const timer = setInterval(() => {
    const memory = console.memory;
    const usedMB = (memory.usedJSHeapSize / 1048576).toFixed(2);

    readings.push({
      timestamp: new Date().toLocaleTimeString(),
      usedMB: parseFloat(usedMB)
    });

    console.clear();
    console.log('=== GIÁM SÁT BỘ NHỚ ===');
    console.table(readings.slice(-10)); // Hiển thị 10 lần đọc gần nhất

    // Cảnh báo nếu bộ nhớ đang tăng quá nhanh
    if (readings.length > 2) {
      const recent = readings.slice(-3);
      const trend = recent[2].usedMB - recent[0].usedMB;

      if (trend > 10) {
        console.warn(`⚠️ Bộ nhớ tăng ${trend.toFixed(2)} MB trong 3 lần đọc gần nhất`);
      }
    }
  }, interval);

  return () => clearInterval(timer);
}

const stopMonitor = startMemoryMonitor(2000);
// Chạy ứng dụng của bạn, theo dõi rò rỉ bộ nhớ
// stopMonitor() khi hoàn thành

Ví dụ 3: So Sánh Bộ Nhớ Trước/Sau

async function profileMemoryUsage(operationName, operation) {
  // Buộc thu gom rác nếu có (Chrome với cờ --expose-gc)
  if (global.gc) {
    global.gc();
  }

  const before = console.memory.usedJSHeapSize;

  await operation();

  const after = console.memory.usedJSHeapSize;
  const delta = ((after - before) / 1048576).toFixed(2);

  console.log(`${operationName} tác động bộ nhớ: ${delta} MB`);

  if (delta > 10) {
    console.warn(`Phát hiện sử dụng bộ nhớ cao cho ${operationName}`);
  }

  return delta;
}

// Sử dụng
await profileMemoryUsage('Đang tải 1000 người dùng', async () => {
  const users = await fetchUsers(1000);
  renderUsers(users);
});

// Output: Tải 1000 người dùng tác động bộ nhớ: 15.23 MB

Ví dụ 4: Bộ Phát Hiện Rò Rỉ Bộ Nhớ

class MemoryLeakDetector {
  constructor(threshold = 50) {
    this.baseline = null;
    this.threshold = threshold; // MB
    this.checkpoints = [];
  }

  setBaseline() {
    this.baseline = console.memory.usedJSHeapSize;
    console.log('Đã đặt cơ sở bộ nhớ:', (this.baseline / 1048576).toFixed(2), 'MB');
  }

  checkpoint(label) {
    const current = console.memory.usedJSHeapSize;
    const increase = (current - this.baseline) / 1048576;

    this.checkpoints.push({
      label,
      memory: (current / 1048576).toFixed(2),
      increase: increase.toFixed(2)
    });

    if (increase > this.threshold) {
      console.error(`🚨 PHÁT HIỆN RÒ RỈ BỘ NHỚ tại "${label}"`, {
        current: (current / 1048576).toFixed(2) + ' MB',
        baseline: (this.baseline / 1048576).toFixed(2) + ' MB',
        increase: increase.toFixed(2) + ' MB',
        threshold: this.threshold + ' MB'
      });
    }

    return increase;
  }

  report() {
    console.table(this.checkpoints);
  }
}

// Sử dụng
const detector = new MemoryLeakDetector(30);
detector.setBaseline();

// Kiểm tra các hoạt động khác nhau
loadInitialData();
detector.checkpoint('Dữ liệu ban đầu đã tải');

for (let i = 0; i < 10; i++) {
  processLargeDataset();
  detector.checkpoint(`Lần lặp ${i + 1}`);
}

detector.report();

Ví dụ 5: Lập Hồ Sơ Bộ Nhớ Thành Phần

class ComponentProfiler {
  static profiles = new Map();

  static start(componentName) {
    this.profiles.set(componentName, {
      startMemory: console.memory.usedJSHeapSize,
      startTime: performance.now()
    });
  }

  static end(componentName) {
    const profile = this.profiles.get(componentName);

    if (!profile) {
      console.warn(`Không có hồ sơ nào được bắt đầu cho ${componentName}`);
      return;
    }

    const endMemory = console.memory.usedJSHeapSize;
    const endTime = performance.now();

    const memoryDelta = ((endMemory - profile.startMemory) / 1048576).toFixed(2);
    const timeDelta = (endTime - profile.startTime).toFixed(2);

    console.group(`📊 Hồ Sơ ${componentName}`);
    console.log(`Bộ nhớ: ${memoryDelta} MB`);
    console.log(`Thời gian: ${timeDelta} ms`);

    if (parseFloat(memoryDelta) > 5) {
      console.warn('Phát hiện phân bổ bộ nhớ cao');
    }

    console.groupEnd();

    this.profiles.delete(componentName);
  }
}

// Sử dụng trong React
function HeavyComponent() {
  useEffect(() => {
    ComponentProfiler.start('HeavyComponent');

    // Logic thành phần...

    return () => {
      ComponentProfiler.end('HeavyComponent');
    };
  }, []);

  return <div>Thành Phần Nặng</div>;
}

Mẹo Chuyên Nghiệp

  1. Chuyển Đổi Sang Đơn Vị Dễ Đọc:
    function formatBytes(bytes) {
      if (bytes < 1024) return bytes + ' B';
      if (bytes < 1048576) return (bytes / 1024).toFixed(2) + ' KB';
      if (bytes < 1073741824) return (bytes / 1048576).toFixed(2) + ' MB';
      return (bytes / 1073741824).toFixed(2) + ' GB';
    }
    
    console.log('Bộ nhớ đã sử dụng:', formatBytes(console.memory.usedJSHeapSize));
    
  2. Kiểm Tra Hỗ Trợ Trình Duyệt:
    if (console.memory) {
      console.log('Giám sát bộ nhớ có sẵn');
    } else {
      console.log('Trình duyệt này không hỗ trợ giám sát bộ nhớ');
    }
    
  3. Kết Hợp Với Performance API:
    function fullSystemCheck() {
      console.group('Trạng Thái Hệ Thống');
    
      if (console.memory) {
        console.log('Bộ nhớ:', formatBytes(console.memory.usedJSHeapSize));
      }
    
      if (performance.memory) {
        console.log('Bộ nhớ hiệu suất:', formatBytes(performance.memory.usedJSHeapSize));
      }
    
      console.log('Thời gian điều hướng:', performance.timing);
      console.groupEnd();
    }
    
  4. Theo Dõi Xu Hướng Tăng Trưởng:
    let lastMemory = console.memory.usedJSHeapSize;
    
    setInterval(() => {
      const current = console.memory.usedJSHeapSize;
      const delta = current - lastMemory;
    
      if (delta > 1048576) { // Tăng 1 MB
        console.warn('Bộ nhớ tăng thêm', formatBytes(delta));
      }
    
      lastMemory = current;
    }, 10000);
    

Các Trường Hợp Sử Dụng Nâng Cao

Ảnh Chụp Bộ Nhớ Tự Động

class MemorySnapshot {
  static snapshots = [];

  static take(label) {
    const snapshot = {
      label,
      timestamp: Date.now(),
      memory: { ...console.memory }
    };

    this.snapshots.push(snapshot);

    console.log(`📸 Ảnh chụp "${label}" đã được tạo`);
  }

  static compare(label1, label2) {
    const snap1 = this.snapshots.find(s => s.label === label1);
    const snap2 = this.snapshots.find(s => s.label === label2);

    if (!snap1 || !snap2) {
      console.error('Không tìm thấy ảnh chụp');
      return;
    }

    const delta = snap2.memory.usedJSHeapSize - snap1.memory.usedJSHeapSize;
    const timeDelta = snap2.timestamp - snap1.timestamp;

    console.group(`So sánh: ${label1} → ${label2}`);
    console.log('Thay đổi bộ nhớ:', formatBytes(delta));
    console.log('Thời gian trôi qua:', (timeDelta / 1000).toFixed(2), 'giây');
    console.log('Tốc độ:', formatBytes(delta / (timeDelta / 1000)), '/giây');
    console.groupEnd();
  }

  static list() {
    console.table(this.snapshots.map(s => ({
      label: s.label,
      time: new Date(s.timestamp).toLocaleTimeString(),
      memory: formatBytes(s.memory.usedJSHeapSize)
    })));
  }
}

// Sử dụng
MemorySnapshot.take('khởi-động-ứng-dụng');
// ... chạy ứng dụng ...
MemorySnapshot.take('sau-tải-ban-đầu');
// ... tương tác người dùng ...
MemorySnapshot.take('sau-hành-động-người-dùng');

MemorySnapshot.compare('khởi-động-ứng-dụng', 'sau-tải-ban-đầu');
MemorySnapshot.list();

Phát Hiện Áp Lực Bộ Nhớ

class MemoryPressureMonitor {
  constructor() {
    this.threshold = 0.85; // 85% giới hạn heap
    this.listeners = [];
  }

  start() {
    this.interval = setInterval(() => {
      const memory = console.memory;
      const usage = memory.usedJSHeapSize / memory.jsHeapSizeLimit;

      if (usage > this.threshold) {
        const event = {
          type: 'áp-lực-bộ-nhớ',
          usage: (usage * 100).toFixed(2) + '%',
          used: formatBytes(memory.usedJSHeapSize),
          limit: formatBytes(memory.jsHeapSizeLimit)
        };

        console.error('🔴 ÁP LỰC BỘ NHỚ', event);
        this.listeners.forEach(listener => listener(event));
      }
    }, 1000);
  }

  stop() {
    clearInterval(this.interval);
  }

  onPressure(callback) {
    this.listeners.push(callback);
  }
}

const monitor = new MemoryPressureMonitor();
monitor.onPressure(event => {
  // Thực hiện hành động: xóa cache, giảm chất lượng, v.v.
  console.warn('Đang giảm dấu chân bộ nhớ ứng dụng...');
  clearCaches();
});

monitor.start();

Những Lỗi Cần Tránh

  1. Không Dựa Vào Nó Trong Sản Xuất: Nó dành riêng cho Chrome và không được chuẩn hóa:
    // Xấu - sẽ gây lỗi trong Firefox
    const memory = console.memory.usedJSHeapSize;
    
    // Tốt
    const memory = console.memory ? console.memory.usedJSHeapSize : null;
    
  2. Thời Gian Thu Gom Rác: Các chỉ số bộ nhớ có thể gây hiểu lầm nếu GC chưa chạy:
    // Bộ nhớ có thể vẫn cao ngay cả sau khi xóa
    let bigArray = new Array(1000000);
    bigArray = null; // Đủ điều kiện cho GC, nhưng chưa được thu gom
    console.log(console.memory.usedJSHeapSize); // Có thể vẫn hiển thị cao
    
  3. Không Giám Sát Quá Mức: Kiểm tra bộ nhớ quá thường xuyên có thể ảnh hưởng đến hiệu suất:
    // Xấu - kiểm tra mỗi 100ms
    setInterval(checkMemory, 100);
    
    // Tốt - kiểm tra mỗi 5 giây
    setInterval(checkMemory, 5000);
    
  4. Hãy Nhớ Rằng Nó Chỉ Là Heap: Nó không hiển thị bộ nhớ DOM, hình ảnh hoặc các tài nguyên khác:
    // console.memory sẽ không hiển thị bộ nhớ của hình ảnh này
    const img = new Image();
    img.src = 'huge-image.png';
    

console.memory Giúp Tiết Kiệm Thời Gian Gỡ Lỗi Như Thế Nào?

Rò rỉ bộ nhớ nổi tiếng là khó theo dõi. Không có giám sát bộ nhớ, bạn có thể không biết mình bị rò rỉ cho đến khi người dùng phàn nàn về việc các tab trình duyệt bị treo.

Với console.memory, bạn có thể chủ động giám sát mức sử dụng bộ nhớ trong quá trình phát triển. Thực hiện một luồng người dùng điển hình, kiểm tra các chỉ số bộ nhớ. Nếu bộ nhớ tiếp tục tăng, bạn biết có một rò rỉ. Thêm các điểm kiểm tra ở các giai đoạn khác nhau để thu hẹp nơi rò rỉ xảy ra.

Tôi từng gỡ lỗi một rò rỉ bộ nhớ trong một ứng dụng trực quan hóa dữ liệu, nơi bộ nhớ sẽ tăng từ 50MB lên 500MB sau một giờ sử dụng. Bằng cách thêm các điểm kiểm tra bộ nhớ sau mỗi lần render biểu đồ, tôi phát hiện ra rằng các trình nghe sự kiện không được dọn dẹp. Đã khắc phục nó trong 20 phút nhờ giám sát bộ nhớ.


Thông Tin Bổ Sung: Console Thực Sự Hoạt Động Như Thế Nào

Hiểu cách console hoạt động bên trong giúp bạn trở thành một debugger giỏi hơn. Dưới đây là một số điều mà hầu hết các nhà phát triển không biết:

Console Là Bất Đồng Bộ

Console không chặn mã của bạn. Khi bạn ghi log một cái gì đó, nó được xếp hàng và hiển thị bất đồng bộ. Điều này có nghĩa là:

const obj = { value: 1 };
console.log(obj);
obj.value = 2;

// Trong console, bạn có thể thấy { value: 2 }
// mặc dù nó là 1 khi được ghi log!

Điều này xảy ra vì console thường ghi log một tham chiếu, không phải một ảnh chụp nhanh. Để tránh điều này:

// Tạo một ảnh chụp nhanh
console.log(JSON.parse(JSON.stringify(obj)));

// Hoặc sử dụng một breakpoint/câu lệnh debugger
console.log(obj);
debugger; // Tạm dừng thực thi để bạn thấy trạng thái thực

Các Phương Thức Console Có Thể Bị Ghi Đè

Bạn có thể bao bọc hoặc thay thế các phương thức console:

const originalLog = console.log;

console.log = function(...args) {
  originalLog('[ĐÃ GHI LOG]', ...args);
  // Gửi đến phân tích, lưu vào tệp, v.v.
};

console.log('Xin chào'); // Output: [ĐÃ GHI LOG] Xin chào

Điều này hữu ích cho:

  • Thêm dấu thời gian vào tất cả các log
  • Gửi log đến một máy chủ từ xa
  • Vô hiệu hóa console trong sản xuất
  • Tạo hệ thống ghi log tùy chỉnh

Các Phương Thức Console Không Được Chuẩn Hóa

Các trình duyệt khác nhau triển khai Console API khác nhau. Chrome có các phương thức mà Firefox không có, và ngược lại. Luôn kiểm tra khả năng tương thích:

// Một số trình duyệt không có console.table
if (typeof console.table === 'function') {
  console.table(data);
} else {
  console.log(data);
}

Tác Động Hiệu Suất

Các phương thức console có chi phí hiệu suất, đặc biệt là console.log() với các đối tượng lớn. Trong các vòng lặp nóng:

// Chậm - ghi log 1.000.000 lần
for (let i = 0; i < 1000000; i++) {
  console.log(i);
}

// Nhanh hơn - ghi log một lần
for (let i = 0; i < 1000000; i++) {
  // công việc
}
console.log('Hoàn thành');

Luôn xóa các log gỡ lỗi khỏi các bản dựng sản xuất, đặc biệt là trong mã quan trọng về hiệu suất.

Định Dạng Console (Chrome/Firefox)

Bạn có thể tạo kiểu output console bằng CSS:

console.log('%cVăn Bản Được Tạo Kiểu', 'color: blue; font-size: 20px; font-weight: bold;');

console.log(
  '%cLỗi: %cCó gì đó không ổn',
  'color: red; font-weight: bold;',
  'color: gray;'
);

Hữu ích cho:

  • Làm nổi bật các log quan trọng
  • Tạo biểu ngữ nghệ thuật ASCII
  • Mã hóa màu các loại log khác nhau

Sự Khác Biệt Giữa Các Trình Duyệt Cần Biết

Chrome DevTools

  • Triển khai console.table() tốt nhất
  • console.memory
  • Kiểm tra đối tượng xuất sắc
  • Xem biểu thức trực tiếp
  • Stack traces bất đồng bộ đầy đủ

Firefox Developer Tools

  • Tốt nhất để gỡ lỗi CSS
  • Các công cụ hiệu suất tuyệt vời
  • Không có console.memory (sử dụng about:memory thay thế)
  • Định dạng đối tượng khác nhau

Safari Web Inspector

  • Giao diện sạch sẽ
  • Tốt cho việc gỡ lỗi iOS
  • Hạn chế hơn Chrome
  • Một số phương thức hoạt động khác nhau

Edge (Chromium)

  • Tương tự Chrome (cùng engine)
  • Giao diện người dùng hơi khác
  • Thường tương thích với các ví dụ của Chrome

Lời khuyên: Kiểm tra mã gỡ lỗi của bạn trong các trình duyệt mục tiêu. Đừng cho rằng tất cả các phương thức console đều hoạt động ở mọi nơi.


Tạo Các Tiện Ích Console Của Riêng Bạn

Khi bạn đã thành thạo các phương thức tích hợp sẵn, hãy tạo các tiện ích tùy chỉnh:

const debug = {
  // Kiểm tra đối tượng nhanh chóng
  inspect(obj, depth = 3) {
    console.group('🔍 Kiểm Tra Đối Tượng');
    console.dir(obj, { depth });
    console.table(obj);
    console.groupEnd();
  },

  // Đo thời gian hiệu suất
  perf(label, fn) {
    console.time(label);
    const result = fn();
    console.timeEnd(label);
    return result;
  },

  // Ghi log có điều kiện
  when(condition, ...args) {
    if (condition) {
      console.log(...args);
    }
  },

  // Ghi log nhận biết bộ nhớ
  memory(label) {
    if (console.memory) {
      const used = (console.memory.usedJSHeapSize / 1048576).toFixed(2);
      console.log(`${label} - Bộ nhớ: ${used} MB`);
    }
  }
};

// Sử dụng
debug.inspect(complexObject);
debug.perf('tính-toán', () => heavyCalculation());
debug.when(isDev, 'Chế độ phát triển đang hoạt động');
debug.memory('Sau khi tải dữ liệu');

Kết Luận: Nâng Tầm Kỹ Năng Gỡ Lỗi Của Bạn

Sự thật là: hầu hết các nhà phát triển không bao giờ học các phương thức console này. Họ gắn bó với console.log() và tự hỏi tại sao việc gỡ lỗi lại mất nhiều thời gian đến vậy. Giờ đây bạn đã biết mười phương thức mạnh mẽ mà hầu hết các đồng nghiệp của bạn không biết:

  1. console.table() – Xem cấu trúc dữ liệu trong nháy mắt
  2. console.time/timeEnd() – Đo hiệu suất chính xác
  3. console.trace() – Theo dõi đường dẫn thực thi
  4. console.assert() – Bắt lỗi tại nguồn
  5. console.group/groupEnd() – Tổ chức các log phức tạp
  6. console.dir() – Kiểm tra đối tượng sâu sắc
  7. console.count/countReset() – Theo dõi các lệnh gọi hàm dễ dàng
  8. console.clear() – Giữ console của bạn dễ đọc
  9. console.warn/error() – Thêm ý nghĩa ngữ nghĩa
  10. console.memory – Giám sát bộ nhớ chủ động

Hãy bắt đầu sử dụng những phương pháp này ngay hôm nay. Chọn một phương pháp và tích hợp nó vào quy trình làm việc của bạn. Tuần tới, thêm một phương pháp khác. Trong vòng một tháng, bạn sẽ gỡ lỗi nhanh hơn và hiệu quả hơn bao giờ hết.

Hãy nhớ: gỡ lỗi tốt không phải là làm việc chăm chỉ hơn – đó là có các công cụ tốt hơn và biết cách sử dụng chúng. Console API là một trong những bộ công cụ mạnh mẽ nhất trong kho vũ khí của bạn. Hãy làm chủ nó.

Bây giờ hãy tự tin gỡ lỗi. Bạn của tương lai (và đồng đội của bạn) sẽ cảm ơn bạn.


Bạn có phương thức console yêu thích nào tôi chưa đề cập không? Hoặc một kỹ thuật gỡ lỗi mà bạn rất tâm đắc? Hãy để lại bình luận bên dưới – tôi rất muốn nghe những gì hiệu quả với bạn!

Chỉ mục