-->
ads here

Giải ngố về Scope và Closure trong Javascript

advertise here
Scope và Closure là các khái niệm rất cơ bản và quan trọng mà một Javascript developer nên biết và hiểu rõ, vì chỉ có như vậy chúng ta mới có thể tránh được một số lỗi thường gặp và đồng thời giúp ta bước thêm một bước trên con đường trở thành master Javascipt.
Scope
Bài viết gồm các mục như sau:
  1. Scope in JS
  2. Closure in JS
  3. The Infamous Loop Problem

1. Scope in JS

Khái niệm Scope

Trong Javascript, Scope có thể hiểu như là một nơi mà các variable và function có thể truy cập và sử dụng được thông qua tên trực tiếp. Về căn bản, các variable và function có thể được định nghĩa ở local scope và global scope

Global scope

Là scope mà bất cứ biến và hàm nào được định nghĩa trong nó đều có thể access được ở bất kì nơi nào.
Ví dụ


var globalVar = 1234;
globalFunc() {
 console.log(globalVar)
 return "This is global function"
}
Ta có thể thấy, globalVar là biến global (global variable), và globalFunc là hàm global (global function). Chúng ta có thể access globalVar từ bất kì đâu, kể cả bên trong globalFunc

Local scope

Ngược lại với global scope, local scope là khi một biến hoặc hàm được định nghĩa và access được bên trong 1 phần nào đó của code, ví dụ trong thân của 1 function chẳng hạn


var globalVar = 1234
globalFunc() {
 var localVariable = "this is local variable"
 console.log(globalVar)
 console.log(localVariable) // 1
 return "This is global function"
}
console.log(localVariable) // 2

Ta có thể thấy, trong đoạn code trên, localVariable được định nghĩa bên trong thân hàm globalFunc nên nó là biến local, nó chỉ có thể được access bên trong scope của nó ở console.log số 1. Ta không thể access ở ngoài scope của biến được. console.log số 2 sẽ bị lỗi

2. Closure

Để hiểu được khái niệm closure, trước tiên ta xét đến ví dụ sau


function outerFunc (x) {
 function innerFunc (y) {
  return x + y
 }
 return innerFunc
}
var outer = outerFunc(5) // (1)
console.log(outer(10)) // Display 15 in console
Như ta thấy, khi hàm outerFunc được gọi, nó trả về 1 hàm, sau đó outerFunc sẽ close context hiện tại của nó lại là nhớ giá trị 5 được truyền vào.
Biến outer trỏ đến 1 hàm mà nó luôn biết rằng có giá trị 5 đã được truyền vào. Ta có thể tưởng tượng outer như sau


// This is outer 
function outer (y) { return 5 + y }
Cuối cùng, khi ta gọi outer và truyền vào cho nó giá trị 10, thì nó sẽ cộng thêm 5 vào và trả về kết quả là 15
Kết luận, closure là việc inner function có thể access 1 local variable của outer function khi outer function đã return.

3. The Infamous Loop Problem

Closure là một khái niệm dễ gây nhầm lẫn và khó nắm rõ đối với người mới tìm hiểu về JS, và đây là một trong những ví dụ điển hình để minh họa cho sự nhầm lẫn này.
Nội dung của ví dụ này như sau:
Bạn muốn tạo ra 5 link từ tag <a>, innerHTML của tag a đó chính là giá trị i và khi click vào link sẽ hiện alert với giá trị i tương ứng của link đó.


<a>Link 0 </a>
<a>Link 1 </a>
<a>Link 2 </a>
<a>Link 3 </a>
<a>Link 4 </a>
Ta hiện thực như sau:


function addLinks () {
 for (var i = 0, link; i < 5; i++) {
  link = document.createElement('a')
  link.innerHTML = "Link" + i
  link.onclick = function () {
   alert(i)
  }
  document.body.appendChild(link);
 }
}
Khi chạy đoạn code trên ta thấy trên màn hình có 5 tag a với giá trị là
Link0 Link1 Link2 Link3 Link4
Tuy nhiên khi click vào bất kì tag a nào đều alert giá trị là 5 chứ ko phải giá trị i tương ứng.
Để giải thích cho hiện tượng này, ta cần chú ý đến scope của vòng lặp for và của hàm addLinks. Biến i được gán vào hàm onclick là biến i của hàm addLinks. Và tại thời điểm ta click vào bất kì link nào trên browser thì lúc đó vòng lặp for đã thực hiện xong, giá trị của i lúc đó là 5 rồi, nên khi đó lúc nào alert cũng hiện ra là 5.
Để khắc phục hiện tượng này ta thực hiện như sau:


function addLinks () {
 var click = function(i) {
  return function() {
   alert(i)
  }
 }
 for (var i = 0, link; i < 5; i++) {
  link = document.createElement('a')
  link.innerHTML = "Link" + i
  link.onclick = click(i)
  document.body.appendChild(link);
 }
}

Kết

Hi vọng qua bài viết này sẽ giúp các bạn có cái nhìn rõ ràng hơn về hai khái niệm scope và closure trong Javascript, qua đó có thể ứng dụng vào trong công việc hàng ngày.
Nếu các bạn thấy bài viết có ích hãy chia sẻ cho mọi người cùng biết. Nếu có ý kiến đóng góp, đừng ngần ngại để lại comment bên dưới nhé. Xin cảm ơn!

Reference

Advertisement
COMMENTS ()