understand Closure in Javascript (p2)



  • Tl;dr

    (Xem phần trước của bài viết understand Closure in Javascript (p1))

    Excution Contexts và Script Chains

    (tiếp tục)

    Scope Chains và [[scope]] property

    Nhắc sơ lại rằng Javascript function luôn nằm trong một outer scope và bên trong chúng có một internal scope. Khi function object được tạo ra, nó là một phần của outer scope, và chỉ khi function được gọi thực thi (call) thì intenal scope (và execution context ) của nó mới được sinh ra. Ngoài ra Javascript standard còn cung cấp cho function object một property có tên là [[scope]], property này sẽ tham chiếu đến outer scope của function

    Ngay từ ban đầu trước khi browser chạy script file, nó sẽ lướt qua toàn bộ mã nguồn và tiến hành giai đoạn thứ nhất (Creation Phase). Ở giai đoạn này, global scope tự động được mở ra, đi kèm với sự hình thành của global execution context. Bên trong global execution context là thực thể global scope có dạng như một list (hay chain) chứa tất cả object nằm trong phạm vi toàn cục (global). Đặc biệt global object nằm ở đầu list. Các function được tạo ra thuộc phạm vi toàn cục (global) thì [[scope]] property của chúng sẽ tham chiếu trực tiếp đến thực thể global scope

    Xem lại toàn bộ đoạn code ở bài viết trước:

    // global scope
    var glVar1;
    var glVar2;
    
    function MyFunction(){
        var localVar1;
        var localVar2;
        function innerOfMyFunction(){
            // .... some code here ....
        }
    }
    
    function b(){
         // some code here….
    }
    
    var c = Function c(){
         // some code here….
    }
    
    
    // .......
    
    MyFunction();   // call MyFunction
    

    Tổng quan ta có
    alt text

    Ta cùng chuyển sự chú ý về [[scope]] property trong function object, property này đặt biệt ở chỗ, khi function object được tạo trong một outer scope nào thì nó sẽ tham chiếu đến outer scope đó.
    MyFunction’s function object được tạo ra trong global scope nên [[scope]] property tham chiếu đến global scope
    Thế innerOfMyFunction’s function object sẽ có [[scope]] property tham chiếu đến đâu?

    Trong hình minh họa trên ta thấy innerOfMyFunction’s [[scope]] property trỏ đến cả MyFunction’s scopeglobal scope. Mình họa như vậy để cho ta thấy được rằng outer scope của innerOfMyFunction là có sự kết hợp giữa MyFunction’s scopeglobal scope thôi chứ không phải nó tham chiếu một lúc đến hai nơi như vậy đâu. [[scope]] property cũng chỉ là một biến tham chiêu thôi mà, làm sao một biến có tham chiếu đến hai nơi được. Lúc này ta cần đến Scope chain, vậy Scope chain do đâu mà ra? Hãy thử ngẫm nghĩ một lần nữa xem, tại sao Javascript standard lại cung cấp cho ta [[scope]] property để làm cái nồi gì?

    Chính nó đấy, chính [[scope]] property của MyFunction là thứ cầu nối giữa global execution context và MyFunction’s execution context hay nói cách khác:
    Scope chain là sự nối liền MyFunction’s scope với global scope, tạo thành một list (hay chain) chứa rất nhiều objects nằm bên trong, bao gồm Activation object và global object (Lưu ý là MyFunction’s scope sẽ liền trước global object).
    Và innerOfMyFunction’s [[scope]] property sẽ tham chiếu đến Scope chain đó

    Dài quá ta phải rút gọn cái scope chain lại!
    Theo như jibbering, thì global scope chỉ nên biểu diễn mỗi phần tử global object thôi. Thật vậy, vì global object đã tham chiếu đến các phần tử còn lại thông qua các properties cùng tên, nên ta biểu diễn thêm chúng chỉ bằng thừa. Ta cũng thấy điều tương tự diễn ra đối với Activation object trong MyFunction’s scope

    Okay thế tuyệt quá rồi Scope chain mà innerOfMyFunction’s [[scope]] property tham chiếu tới sẽ có cấu trúc như vầy:
    alt textp1)
    Sẽ là điều bình thường khi kết thúc một function thì execution context của nó cũng mất theo. Tất cả mọi thứ từ đầu đến giờ, Scope chain, Activation/Variable object, các local variable, các local function object,…. sẽ bị garbage collection dọn sạch. Nhưng Javascript còn hơn thế, xem function như f*irst-class object, cho phép trả về một function thông qua lệnh return đã mang đến cho ta **Closure. Đối với Douglas Crockford, **Closure* là thứ gì đó rất hay ho đến từ Javascript mà trước nó, chưa có ngôn ngữ lập trình nào thật sự triển khai và áp dụng.

    Forming Closure

    function exampleClosureForm(arg1, arg2){
         var localVar = 8;
         function exampleReturned(innerArg){
              return ((arg1 + arg2) / (innerArg + localVar));
         }
    
          /* return a reference to the inner function defined as 
          exampleReturned
          */
          Return exampleReturned;
    }
    
    var globalVar = exampleClosureForm(2, 4);
    

    function object sau khi được trả về từ lần gọi hàm exampleClusureForm(2, 4) nhất định sẽ không bị giải phóng bởi garbage collection bởi vì nó được tham chiếu bởi một global variable tên globalVar, tất nhiên globalVar là exampleReturned’s function object, nên ta có thể gọi thực thi nó ở lần sau.

    Nhưng mọi chuyện có cái gì đó rất phức tạp, bởi vì function object được trả về và được tham chiếu bởi globalVar, nhưng trước đó nó đã được sinh ra trong exampleClosureForm’s scope. Điều đó đồng nghĩa với việc [[scope]] property của nó vẫn còn đang tham chiếu tới Scope chain (gồm các phần tử: Activation/Variable object của exampleClosureForm’s execution context, liền sau là global object). Như vậy thì các phần tử trong Scope chain sẽ không bị garbage collection giải phóng

    Closure đã được hình thành từ đó. exampleReturned’s function object sau khi được trả về có thể thoải mái gọi thực thi bất kì lúc nào và thoải mái sử các parameter, local variable hay các inner function khác,… bên trong exampleClosureForm(). Vì giờ đây tất cả đã được gói gọn trong Scope chain tham chiếu bởi [[scope]] property.

    Nhưng để cho chắc ăn, ngoài ra để ôn lại những gì đã biết. Hãy cùng thử hình dung những gì sẽ xảy ra:

    function exampleClosureForm(arg1, arg2){
         var localVar = 8;
         function exampleReturned(innerArg){
              return ((arg1 + arg2) / (innerArg + localVar));
         }
    
          /* return a reference to the inner function defined as 
          exampleReturned
          */
          Return exampleReturned;
    }
    
    var globalVar = exampleClosureForm(2, 4);
    
    globalVar(2);   // call
    

    Ta lại cùng điểm qua các giai đoạn:
    (Creation Phase) bắt đầu ---> global execution context được tạo ra (bao gồm sự hình thành exampleClosureForm’s function object)---> exampleClosureForm’s [[scope]] property tham chiếu đến global scope của global execution context ---> (Execution Phase) bắt đầu ---> chạy line-by-line các dòng script code ---> bắt gặp exampleClosureForm được gọi thực thi ---> exampleClosureForm’s execution context được tạo ra (bao gồm cả sự hình thành của Action/Variable object và exampleReturned’s function object) ---> xuất hiện Scope chain liên kết exampleClosureForm’s scopeglobal scope ---> exampleReturned’s [[scope]] property tham chiếu đến Scope chain ---> chạy line-by-line các dòng script code bên trong exampleClosureForm’s function body ---> bắt gặp lệnh return exampleReturned; ---> kết thúc exampleClosureForm ---> tiếp tục các dòng script code bên ngoài global scope ---> bắt gặp exampleRetuerned's function object được tham chiếu với globalVar bị gọi thực thi ---> exampleReturned’s execution context được tạo ra (bao gồm sự hình thành của Activation/Variable object bên trong exampleReturned’s scope) ---> hình thành Scope chain mới là sự liên kết giữa exampleReturned’s scopeScope chain cũ (tham chiếu bởi [[scope]] property) ---> ….

    Scope chain mới sẽ gồm các phần tử theo thứ tự: Activation/Variable object của exampleReturned’s scope --> Activation/Variable object của exampleClosureForm’s scope --> global object

    return ((arg1 + arg2) / (innerArg + localVar));
    

    Để thực thi lệnh trên, hệ thống sẽ duyệt theo Scope chain mới bắt đầu từ phần tử đầu tiên
    Hệ thống sẽ tìm thấy:

    • arg1, arg2 và localVar thuộc Activation/Variable object của exampleClosureForm’s scope
    • innerArg thuộc Activation/Variable object của exampleReturned’s scope

    Ta áp dụng Closures như thế nào?

    (sẽ tìm hiểu tiếp ở p3)

    Referrences

    (tham khảo từ bài viết Javascript Closure - FAQ notes)
    (xem them Javascript Closure & scope chain with example)

    Nguồn: Kipalog


Hãy đăng nhập để trả lời
 

Có vẻ như bạn đã mất kết nối tới LaptrinhX, vui lòng đợi một lúc để chúng tôi thử kết nối lại.