Let và Var – ví dụ nhỏ – khác biệt lớn.

Có thể bạn chưa biết: từ ES6 trở đi, LetConst được bổ sung vào Javascript để hỗ trợ việc khai báo biến. Vậy tại sao có Var rồi vẫn cần LetConst.

Tuy nhiên trong bài này, mình sẽ tạm bỏ qua Const để đến với 1 ví dụ nhỏ rất thú vị nhưng thể hiện rõ sự khác biệt của Let và Var trong Javascript.

Đoạn code 1.

for (var i = 0; i < 2; ++i) {
	setTimeout(()=> {
		console.log(i);
	})
}

Đoạn code 2.

for (let i = 0; i < 2; ++i) {
	setTimeout(()=> {
		console.log(i);
	})
}

Trên đây là 2 đoạn code nhỏ chỉ phân biệt nhau ở việc sử dụng var ở code 1 và let ở code 2. Kết quả sẽ là gì ở mỗi trường hợp và tại sao lại như vậy?

Để hiểu được kết quả cũa mỗi đoạn code trên, trước hết chúng ta cần phải biết rằng:


  • JavascriptSINGLE-THREADED PROGRAMMING Language – điều đó có nghĩa là tất cả các dòng lệnh của Javascript chỉ thực thi trên 1 thread và chỉ 1 câu lệnh được thực thi tại 1 thời điểm.
  • setTimeout(callback, timeout) – là hàm dùng để lập lịch – hàm này sẽ tạo 1 timer có giá trị bằng timeout trong WEB APIS. Sau khi Timer chạy xong (expired) nó sẽ đẩy callback vào 1 Message Queue. Tuy nhiên, callback này sẽ không được thực thi ngay lập tức, mà nó chỉ được thực thi khi CALL STACK trống (empty).
  • setTimeOut không phải là 1 hàm của Javascript Engine, mà nó là 1 hàm của WebAPIS trong Java Script Runtime Environment. Trên trình duyệt – Browser JRE có WebAPIS, còn trên Node.js RJE thì nó là C/C++ APIS.
  • CALL STACK – đại khái là 1 cấu trúc để lưu trữ thứ tự thực hiện lệnh trên thread theo cơ chế của 1 STACK là LIFO (Last In – First Out) – hay còn gọi là vào sau ra trước – hàm nào được đưa vào để thực thi trước sẽ kết thúc sau. Ví dụ hàm A gọi hàm B, trong B gọi C thì C xong trước rồi mới đến B và A sẽ xong sau cùng.
  • MESSAGE QUEUE (MQ)- thằng này lại là 1 cấu trúc cũng để lưu trữ thứ tự thực hiện lệnh nhưng lại theo cơ chế là FIFO (First IN – First OUT) – thằng nào vào trước thì thực thi trước. Giống kiểu xếp hàng mua đồ, ông nào đứng trước thì tôi cho mua trước.
  • EVENT LOOP – Ông nội này là cha đỡ đầu của MESSAGE QUEUE, cứ ngồi hóng xem lúc nào thằng CALL STACK trống thì tranh thủ nhét 1 Job (statement) từ thằng MQ vào CALL STACK để thực thi.

Ảnh 1. CALLSTACK, WEB APIS, MESSAGE QUEUE và EVENT LOOP

Đấy, để hiểu được cách hoạt động và kết quả cuối cùng của 2 đoạn code đơn giản trên mà mình phải hiểu được cơ chế hoạt động của Javascript và Runtime Env trước.

Trong 2 đoạn code trên thì dòng For sẽ được đặt vào CALL STACK để chạy cho xong, còn các lần gọi setTimeOut thì các callback là các lệnh console.log(i) sẽ được đặt vào Message Queue theo tuần tự khi Timer bị expired.

Điểm khác biệt dẫn đến kết quả thay đổi, chính là cách khai báo biến sử dụng varlet. Theo đó, var là kiểu khai báo biến Globally Scoped, còn let là Block Scoped.

Trong cả 2 đoạn code trên, sau khi thực hiện xong vòng for, các console.log(i) được nạp từ Message Queue bởi Event Loop đến Call Stack để thực thi.

Tuy nhiên, trong đoạn code 1, bởi vì khai báo i sử dụng var (global scoped), lúc này giá trị của i chính là giá trị sau khi đã kết thúc vòng lặp, i lúc này bằng 2. và dĩ nhiên kế quả in ra là: 2 2.

Trong đoạn code 2, vì khai báo i sử dụng let, các giá trị của i chính là các giá được truyền vào tuần tự lúc thực thiện setTimeOut, nên kết quả lần lượt là 0 1.

Ảnh 2. Kết quả chạy trên browser.

Có thể thấy, chỉ vài, đôi ba dòng code đơn giản, nhưng cũng đã thể hiện được sự khác biệt giữa varlet trong khai báo biến của Javascript. Để hiểu được cách thức hoạt động của các đoạn code này, đòi hỏi chúng ta cũng cần am hiểu cách thức, cơ chế hoạt động bên dưới của Javascript Runtime Environment, hiểu hơn về Web APIS, CallStack, Message Queue và Eventloop.

Have fun!

Loading

What’s your Reaction?
+1
0
+1
0
+1
0

One Reply to “Let và Var – ví dụ nhỏ – khác biệt lớn.”

  1. Uầy đỉnh quá bác, em dùng var chạy nó không ra như ý mình, đọc được bài này đổi thành let phát code chạy mượt luôn

Leave a Reply

Your email address will not be published. Required fields are marked *