Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Hi các bạn 😁, trong bài viết này, chúng ta sẽ tìm hiểu về phạm vi biến hay Variable Scope trong JavaScript dùng để xác định khả năng truy cập của các biến.

Khi chúng ta viết một đoạn code JavaScript và execute nó, trình duyệt - browser sẽ không hiểu được mã JavaScript cấp cao (high-level) mà chúng ta viết trong các ứng dụng của mình. Những đoạn code này cần được chuyển đổi thành một định dạng mà trình duyệt và máy tính của chúng ta có thể hiểu được - machine code.

Ví dụ khi đọc file HTML, nếu trình duyệt gặp mã JavaScript ở thẻ <script> hoặc một thuộc tính có chứa mã JavaScript như onclick, nó sẽ gửi mã đến JavaScript engine của nó.

Sau đó, JavaScript engine của trình duyệt sẽ tạo ra một môi trường đặc biệt để xử lý và thực thi mã JavaScript này. Môi trường này được gọi là Execution Context.

Trong JavaScript có hai loại Execution Context:

  1. Global Execution Context (GEC)
  2. Function Execution Context (FEC)

Global Execution Context (GEC)

Bất cứ khi nào JavaScript engine nhận được một tệp script, trước tiên nó sẽ tạo ra một Execution Context được gọi là Global Execution Context (GEC).

GEC là nơi tất cả mã JavaScript không nằm trong một hàm - function được thực thi.

Đối với mỗi JavaScript file, chỉ có một GEC.

Function Execution Context (FEC)

Mỗi khi một hàm được gọi, JavaScript engine sẽ tạo ra một loại Execution Context khác được gọi là Function Execution Context (FEC) trong GEC để thực thi mã trong hàm đó.

Mỗi lệnh gọi hàm sẽ có FEC của riêng nó, nên có thể có nhiều hơn một FEC trong lúc runtime (mô tả thời gian chương trình đang chạy trên máy tính) của một tập lệnh.

Variable Scope trong JavaScript

Scope xác định khả năng truy cập của một biến. Trong JavaScript có ba scope:

  1. Global scope - phạm vi toàn cục
  2. Local scope - phạm vi cục bộ
  3. Block scope (ES6) - phạm vi trong một khối

Chúng ta sẽ tìm hiểu từng kiểu scope trong bài viết này nhé.

Global Scope trong Javascript

Khi JavaScript engine thực thi một file script, nó sẽ tạo ra một Global Execution Context (GEC). Nó cũng gán các biến mà bạn khai báo bên ngoài các hàm cho GEC. Các biến này sẽ thuộc phạm vi toàn cục - Global Scope. Chúng còn được gọi là các biến toàn cục - global variables.

Ví dụ:

var website = 'homiedev';

Biến website có phạm vi toàn cục (biến không nằm trong bất kỳ hàm - function nào). Do đó, biến này có thể được truy cập ở mọi nơi trong file script.

Lúc này trong GEC sẽ có biến website.

Global Execution Context
website: 'homiedev'

Local Scope trong JavaScript

Các biến mà bạn khai báo bên trong một hàm là các biến cục bộ (local) của hàm. Chúng được gọi là các biến cục bộ - local variables.

Ví dụ:

var message = 'Hi';

function say() {
    var message = 'Bye';
    console.log(message);
}

say();
console.log(message);

Output nhận được là:

Bye
Hi

Khi JavaScript engine thực thi hàm say(), nó sẽ tạo ra một Function Execution Context (FEC). Biến message được khai báo bên trong hàm say() sẽ được liên kết với FEC, mà không phải GEC 😁.

Global Execution Context
message = 'Hi'
say: function () {...}
Function Execution Context
message = 'Bye'

Scope chain trong JavaScript

Chúng ta cùng xem ví dụ sau:

var message = 'Bye 😜';

function say() {
    console.log(message);
}

say();

Output ở trên là:

Bye 😜

Trong ví dụ trên, hàm say() đã lấy giá trị biến message ngoài function này để in giá trị này ra. JavaScript lúc này đã xử lý như sau:

  1. Xem trong ngữ cảnh hiện tại - (FEC) của hàm say() có biến message hay không 🙄. Lúc này nó không tìm thấy biến message.
  2. Do không tìm thấy nên nó thử tìm biến message ở ngữ cảnh bên ngoài (chứa nó) là GEC. Và nó tìm thấy biến message 🤩🎉.

Cách JavaScript xem xét một biến có tồn tại trong phạm vi hiện tại của nó hay không, và nếu không thể tìm thấy biến, nó sẽ chuyển sang phạm vi bên ngoài, được gọi là Scope Chain.

Chúng ta cùng xem thêm ví dụ để hiểu Scope Chain trong JavaScript nhé:

var number = 2020;

function displayNumber() {
    var number = 2022;

    function show() {  
        console.log(number);
    }

    show();
}

displayNumber();

Kết quả chúng ta là: 2022.

Ở ví dụ trên:

  1. Đầu tiên, JavaScript engine tìm biến number trong phạm vi của hàm show(). Kết quả là không tìm thấy 😑. Vì vậy, nó thử tìm bên ngoài xem thế nào 🥱.
  2. Sau khi bước ra ngoài phạm vi hàm show(), nó tìm thấy biến number trong hàm displayNumber() 😻. Do đã tìm thấy thứ cần tìm, nó ngừng tìm kiếm. Kết quả là chúng ta được giá trị in ra là 2022.

Block Scope trong JavaScript

ES6 cung cấp cho chúng ta từ khóa letconst cho phép bạn khai báo các biến trong phạm vi khối - block scope.

Vậy khối - block là gì? Bất cứ khi nào các bạn nhìn thấy cặp dấu ngoặc nhọn {}, thì đó được coi là một khối. Ví dụ như khối trong lệnh if...else, for, do...whilewhile, hay chúng ta còn gọi là phần body của lệnh 😁.

Chúng ta cùng xem ví dụ:

function say(message) {
    if(!message) {
        let greeting = 'Hello 😁'; // block scope
        console.log(greeting);
    }
    // gọi thử greeting
    console.log(greeting); // Lỗi 😑
}

say();

Trong ví dụ này, mình đã gọi greeting được khai báo trong khối của lệnh if, do gọi biến bên ngoài khối if cho nên kết quả là chương trình báo lỗi. Như vậy greeting chỉ có thể hoạt động được trong phạm vi khối - block scope của nó tức là trong if.

Cùng xem tiếp một ví dụ khác nhé:

// global variable
let a = 'Lionel';

function displayPlayer() {

    // local variable
    let b = 'Ronaldo';

    console.log(a + ' ' + b);

    if (b == 'Ronaldo') {

        // block-scoped variable
        let c = 'is a ⚽ player';

        console.log(a + ' ' + b + ' ' + c);
    }

    // không thể sử dụng biến c tại đây
    console.log(a + ' ' + b + ' ' + c);
}

displayPlayer();

Trong chương trình trên, biến:

  1. a là một biến toàn cục. Nó có thể được sử dụng ở bất kỳ đâu trong chương trình.
  2. b là một biến cục bộ. Nó chỉ có thể được truy cập bên trong hàm displayPlayer().
  3. c là một biến phạm vi khối. Nó chỉ có thể được truy cập bên trong khối lệnh if.

Do đó, hai console.log() đầu tiên hoạt động mà không gặp vấn đề gì.

Tuy nhiên, khi chúng ta truy cập biến c ở bên ngoài phạm vi khối trong console.log() thứ ba. Điều này sẽ dẫn đến lỗi.


Trong bài viết này, chúng ta đã cùng nhau tìm hiểu về các phạm vi biến trong JavaScript. Hy vọng bài viết giúp ích cho các bạn.

Chúc các bạn học tốt ^^.

Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.