Yêu cầu thg 8 19, 11:54 CH 65 1 2
  • 65 1 2
+1

Variable ENV, Lexical ENV, Closures và Execution context

Chia sẻ
  • 65 1 2

Trong Javascript, nếu làm việc với Closures thường xuyên thì chắc hẳn là ai cũng phải nghe đến 1 trong 2 thuật ngữ như : Lexical ENV và Variable ENV, thậm chí là kể cả Execution Context vì nó khá quan trọng để hiểu được các concept đằng sau hoạt động như thế nào, nhưng tìm các bài viết liên quan đến Lexical ENV thì họ nói không có kĩ và liên quan lắm, chủ yếu ai cũng bảo "Closures thì làm việc với tham chiếu chứ không phải với giá trị của biến" nhưng mà thật sự câu hỏi được đặt ra là liệu "Closures" có dính dáng gì đến "Variable ENV" hay không, hay chỉ thật sự làm việc dựa trên "Lexical ENV" vì "Lexical ENV" nó vẫn tồn tại mặc dù EC đã được pop ra khỏi stack (đồng thời VE cũng bay màu theo), theo em hiểu thì thằng "Lexical ENV" nó chỉ thật sự bay màu khi không còn closures nào refer đến nó, tự khắc Garbage Collector làm việc của nó, nhưng song song với việc đó thì nhiều content cho rằng "Closures" dựa trên "VE", bản chất thì cả 2 "VE" và "LE" đều thuộc "Lexical ENV" nhưng em thấy chỉ có mỗi "LE" là còn tồn tại sau cùng.

2 CÂU TRẢ LỜI


Đã trả lời thg 9 21, 4:19 SA
Đã được chấp nhận
+1

Chào bạn!

Haha, cái vòng xoáy của Javascript này đúng là khiến đầu óc mình quay cuồng thiệt!

Nói đơn giản thôi nhé:

  • Lexical Environment (LE): Nó như cái bản đồ đường đi của closure, chỉ ra closure cần tìm biến ở đâu. Cái bản đồ này tồn tại lâu dài, cho đến khi không còn closure nào cần nó nữa thì mới "bị dọn dẹp".
  • Variable Environment (VE): Nó là cái "kho chứa" của biến trong mỗi execution context. Mỗi khi một hàm được gọi, VE được tạo ra và khi hàm kết thúc thì VE cũng "bị vứt đi".
  • Execution Context (EC): Nó là cái "môi trường làm việc" khi hàm được gọi, chứa cả VE lẫn LE.

Vậy, Closure có liên quan đến VE không?

Câu trả lời là: Có, nhưng chỉ gián tiếp. Closure không trực tiếp thao tác với VE, mà nó thông qua LE. Khi closure được tạo ra, nó sẽ "nhớ" lại LE của hàm cha, và từ đó truy xuất được biến từ VE của hàm cha.

Ví dụ:

function outer() {
  let a = 10;
  function inner() {
    console.log(a); 
  }
  return inner;
}

let myClosure = outer(); 
myClosure(); 
  • Khi outer() được gọi, EC bên trong outer() được tạo ra bao gồm VE (có a) và LE.
  • inner() được trả về dưới dạng closure. Closure này ghi nhớ LE của outer().
  • Ngay cả khi outer() kết thúc và VE của nó "bị xóa", myClosure vẫn có thể truy cập vào biến a thông qua cái LE mà nó đang giữ.

Tóm lại, Closure dựa vào LE để hoạt động, LE được tạo ra trong EC. Và VE của hàm cha (nơi chứa các biến) được truy cập thông qua LE.

Nếu bạn muốn tìm hiểu thêm, bạn có thể tìm hiểu về CompilerScope Chain. Sử dụng thư viện React hoặc Vue có thể giúp bạn thấy rõ hơn trong thực hành nhé.

Hy vọng là giải thích này đủ dễ hiểu. Chúc bạn thành công!

Chia sẻ
Đã trả lời thg 8 21, 9:05 SA
+2

😉 Trước khi trả lời, ta cần hiểu rõ mọi thuật ngữ, mình giải thích đơn giản như sau:

  • Lexical Environment : Ngữ cảnh khi 1 hàm được tạo ra, bao gồm 2 thông tin

    • Environment Record : lưu trữ các biến và hàm được khai báo trong ngữ cảnh
    • Outer Lexical Environment Reference : tham chiếu tới 1 ngữ cảnh Lexical khác ở bên ngoài
  • Variable Environment : Là 1 phần của Lexical Enviroment, nơi lưu trữ các biến được khai báo. (Gần giống Environment Record)

  • Execution Context : bao gồm mọi thứ cần thiết để thực thi mã JavaScript tại một thời điểm nhất định. Một Context bao gồm.

    • Variable Environment : Lưu các biến tạo ra trong hàm hoặc khối lệnh khi thực thi
    • Lexical Environment : Như đã nói ở trên

😀 Nếu bạn đã hiểu các định nghĩa trên, ta hãy tham khảo ví dụ sau :

const funcHandle = () => {
  // "funcHandle Lexical Environment" được tạo ra
  // - Environment Record là a và temp
  // - Outer Lexical Environment Reference : Global Lexical Environment
  let a = 0;

  const temp = () => {
    // "temp Lexical Environment" được tạo ra
    // - Environment Record là c
    // - Outer Lexical Environment Reference : funcHandle Lexical Environment
    let c = 0;

    // +1 cho 2 biến và in ra
    console.log(++a, ++c);
  };
  
  return temp;
};

const run = funcHandle();
run(); // kết quả 1,1
run(); // kết quả 2,1
run(); // kết quả 3,1

Qua ví dụ trên, ta hiểu khi hàm temp được tạo, nó sẽ liên kết với Lexical Enviroment của hàm funcHandle.

Nên khi temp được đại diện bởi run, theo nguyên tắc Garbage Collector sẽ không thể pop Lexical Enviroment của temp (trong đó có biến a) bởi vì nó vẫn còn được tham chiếu tại biến run.

Và khi run được chạy, đồng nghĩa việc chạy temp, Execution Context sẽ được tạo mới, đồng nghĩa Environment Variable cũng phải được làm mới (tạo mới c), chỉ có mỗi funcHandle Lexical Environment vẫn còn được giữ nguyên và tái sử dụng, và đây chính là nguyên tắc hoạt động của Closures khi có thể tái sử dụng lại Lexical Enviroment tại nơi mà nó được tạo ra. 👍️

🚀 Kết luận: Câu trả lời cho câu hỏi của bạn là: Closures làm việc dựa trên Lexical Environment chứ không phải Variable Environment. Bởi Lexical Environment là tiêu điểm khi có thể tái sử dụng được, trong khi Environment Variable luôn được tạo mới mỗi khi Execution Context được tạo và khởi chạy.

👌Happy coding !!!

Chia sẻ
Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí