Bạn nên biết Closure hoạt động như thế nào

Chúng tôi rất vui mừng chia sẻ kiến thức về từ khóa Closure la gi để tối ưu hóa nội dung trang web và chiến dịch tiếp thị trực tuyến. Bài viết cung cấp phương pháp tìm kiếm, phân tích và lựa chọn từ khóa phù hợp, cùng với chiến lược và công cụ hữu ích. Hy vọng thông tin này sẽ giúp bạn xây dựng chiến lược thành công và thu hút lưu lượng người dùng. Cảm ơn sự quan tâm và hãy tiếp tục theo dõi blog để cập nhật kiến thức mới nhất.

Closure là một khái niệm cơ bản trong javascript mà mọi lập trình viên nên biết. Google search là một thông nhà thái với những lời giảng giải tuyệt vời về closure là cái gì, nhưng chỉ một tí đi sâu vào khía cạnh “why” của vấn đề. Tôi nhận ra rằng, sự hiểu biết cặn kẽ vấn đề giúp các developer làm chủ được những dụng cụ (tool) của họ một cách tốt hơn. Nội dung bài viết này dành riêng cho việc giảng giải cách thao tác làm việc của closure. Tôi hi vọng sau nội dung bài viết này chúng ta cũng có thể sử dụng những lợi thế của closure trong công việc hàng ngày. Mở màn nào !!!

Bạn Đang Xem: Bạn nên biết Closure hoạt động như thế nào

Closure là một tính chất cực mạnh của javascipt và hồ hết các tiếng nói lập trình khác. Theo khái niệm trên MDN:

Closure là những function tham chiếu tới những biến tự do (miễn phí avariable) tách biệt. Nói cách khác, function được khái niệm trong closure sẽ ghi nhớ môi trường tự nhiên (environment) trong nó được tạo ra.

Lưu ý: Các biến tự do không phải là các biến được khai báo cục bộ (local variable) hoặc được truyền vào như thông số (parameter)

Hãy xem một vài ví dụ sau đây:

Ví dụ 1:

function numberGenerator() { // Local miễn phí variable that ends up within the closure var num = 1; function checkNumber() { console.log(num); } num++; return checkNumber; } var number = numberGenerator(); number(); // 2

Trong ví dụ trên, hàm numberGenerator tạo ra một biến tự do cục bộ là num và hàm checkNumber in ra giá trị của biến num bằng console. Hàm checkNumber không có bất kỳ biến cục bộ nào, tuy nhiên nó có thể truy cập vào các biến trong các hàm phía bên phía ngoài như hàm numberGenerator bởi vì có closure. Vì vậy nó có thể sử dụng biến num được khai báo trong hàm numberGenerator để in ra log thậm chí còn sau khoản thời gian hàm numberGenerator đã trả về (return).

Ví dụ 2:

Trong ví dụ này, tất cả chúng ta sẽ chứng minh rằng một closure có thể chứa bất kỳ biến cục bộ nào hay tất cả những biến cục bộ được khai báo bên trong các hàm xung quanh bên phía ngoài.

function sayHello() { var say = function() { console.log(hello); } // Local variable that ends up within the closure var hello = ‘Hello, world!’; return say; } var sayHelloClosure = sayHello(); sayHelloClosure(); // ‘Hello, world!’

Lưu ý biến hello được khai báo sau khoản thời gian có hàm anonymous (những hàm không được đặt tên)- nhưng hàm này vẫn có thể truy cập vào biến hello. Bởi vì biến hello đã được khái niệm trong phạm vi (scope) của hàm sayHello vào thời khắc hàm được tạo ra trước đó, do đó biến hello trở thành sẵn dùng khi hàm anonymous được thực thi ở cuối. Có vẻ khó khiểu, tôi sẽ giảng giải phạm vi (scope) là gì trong nội dung bài viết này. Muốn biết chúng ta cũng có thể kéo xuống dưới.

Những ví dụ này cho thấy closure là một chiếc gì đó trừu tượng. Nhìn chung là: tất cả chúng ta có thể truy cập vào các biến được khái niệm trong các hàm bên phía ngoài trong cả lúc các biến đã được trả về (return). Rõ ràng, có một chiếc gì đó xẩy ra trong background được cho phép các biến này vẫn có thể được truy cập ngay cả những lúc hàm bên phía ngoài đã được trả về.

Để sở hữu thể hiểu được vấn đề này, tất cả chúng ta hãy nghiên cứu một số khái niệm liên quan sau. Hãy mở màn với cái nhìn tổng quát trong đó một hàm được thực thi, hay còn được gọi là execution context

Execution context (văn cảnh thực thi) là một khái niệm trừu tượng được sử dụng bởi ECMAScript để theo dõi việc thẩm định thời kì chạy của mã (code). Nó có thể là global context (văn cảnh toàn cục), khi đó mã của bạn được thực thi trước tiên hoặc khi luồng thực thi đi vào thân hàm.

Vào bất kỳ thời khắc nào, chỉ có một context được chạy. Đấy là lý do vì sao mà Javascript là single thread (đơn luồng), có tức thị chỉ có một lệnh có thể chạy ở một thời khắc. Thông thường các trình duyệt đảm bảo thực thi context bằng phương pháp sử dụng một ngăn xếp (stack). Ngăn xếp là cấu trúc tài liệu Last In First Out (LIFO), có tức thị thứ cuối cùng bạn đặt vào ngăn xếp sẽ là thứ trước tiên bạn lấy ra. Điều này bởi vì tất cả chúng ta có thể chèn hoặc xóa các thành phần trên đỉnh của ngăn xếp. Execution context ngày nay hoặc đang làm việc luôn là thành phần ở đỉnh ngăn xếp. Nó được đưa thoát khỏi ngăn xếp khi chạy execution context hoàn thành và được cho phép execution context tiếp theo theo trong ngăn xếp chạy.

Tuy nhiên, một execution context đang làm việc không có tức thị nó phải kết thúc trước lúc execution context khác có thể chạy. Có trường hợp execution context đang làm việc bị treo và một execution context khác được chạy. Execution context bị tạm dừng có thể sau đấy sẽ tiến hành chọn để chạy lại. Bất kể khi nào một execution context bị thay thế bằng một execution context khác thì execution context thay thế này sẽ tiến hành đưa lên đỉnh ngăn xếp và trở thành execution context ngày nay.

Để hiểu thêm về phần này ta xem ví dụ sau đây

var x = 10; function foo(a) { var b = 20; function bar(c) { var d = 30; return boop(x + a + b + c + d); } function boop(e) { return e * -1; } return bar; } var moar = foo(5); // Closure moar(15);

Khi chạy hàm foo thì hàm bar sẽ tiến hành trả về. Hàm bar sẽ gọi đến hàm boop, tại thời khắc này hàm bar sẽ bị treo và hàm boop sẽ bị đưa lên đầu ngăn xếp (bạn hãy xem ảnh chụp màn hình hiển thị phía bên dưới)

Khi mà boop được trả về, nó sẽ tiến hành đẩy thoát khỏi ngăn xếp và bar sẽ tiến hành tiếp tục chạy.

Khi tất cả chúng ta có một loạt các execution context đang rất được chạy lại – thường bị tạm dừng giữa chừng và sau này được chạy lại. Tất cả chúng ta cần một số phương pháp để theo dõi trạng thái để tất cả chúng ta có thể quản lý trật tự và thực thi các context. Trong trường hợp cụ thể, theo như ECMAScript spec, mỗi excution context có những component trạng thái khác nhau được sử dụng để theo dõi tiến trình trong mỗi context được thực hiện. Gồm có các:

  • Code evaluation state: bất kỳ trạng thái (state) nào thì cũng được thực thi, bị treo và thực thi trở lại trong mối liên quan đến execution context.
  • Function: Một đối tượng người sử dụng hàm là execution context có mức giá trị (hoặc bằng null nếu context là một script hoặc module)
  • Realm: Một tập các đối tượng người sử dụng bên trong, một môi trường tự nhiên toàn cục ECMAScript (global environment), tất cả những mã ECMAScript được đưa vào trong phạm vi (scope) của môi trường tự nhiên toàn cục đó và các tài nguyên, nguồn liên quan khác.
  • Lexical enviroment: Sử dụng để giải quyết và xử lý những tham chiếu định danh (identifier references) vào bên trong execution context.
  • Variable environment: EnvironmentRecord của Lexical enviroment chứa những ràng buộc được tạo bới VariableStatements trong execution context.

Nếu nghe có vẻ khó hiểu, bạn đừng lo lắng. Trong tất cả những biến này, biến lexical enviroment là một trong những điều thú vị của tất cả chúng ta bởi vì nó khái niệm một cách rõ ràng rằng nó giải quyết và xử lý các identifier references bởi mã trong execution context. Chúng ta cũng có thể nghĩ identifier tương tự như variable. Vì mục tiêu thuở đầu tất cả chúng ta tìm cách thần kỳ để truy cập tới những variable (biến) ngay cả những lúc function hoặc context đã được trả về. Lexical enviroment là những thứ tất cả chúng ta cần đào sâu nghiên cứu.

Xem Thêm : 5 bài học cuộc sống từ nghệ thuật Kintsugi của người Nhật

Lưu ý: Về mặt kỹ thuật, cả variable enviroment và lexical environment đều được sử để tùy chỉnh thiết lập closure. Nhưng để đơn giản, tất cả chúng ta sẽ nói chung nó thành một Environment. Để hiểu một cách cụ thể sự khác biệt giữa lexical environment và variabel evironment thì tất cả chúng ta có thể xem nội dung bài viết xuất sắc của Dr. Alex Rauschmayer.

Theo khái niệm, một lexical environment là một kiểu khái niệm được sử dụng để xác định mối liên hệ giữa các định danh (Identifier) với những biến (variable) cụ thể và các hàm (function) cơ bản dựa trên các cấu trúc lexical nesting của ECMAScript code. Một Lexical Environment gồm có một Environment Record và một possibly null tham chiếu đến đối tượng người sử dụng lexical environment bên phía ngoài. Thông thường, một lexical environment được liên kết với một cấu trúc cụ thể của của ECMAScript code như một FunctionDeclaration, một BlockStatement hoặc một Catch clause của một TryStatement và một lexical environment mới được tạo ra mỗi lần code được thực thi.

Hãy phân tích cụ thể

  • Được sử dụng để khái niệm liên kết các định danh: Mục tiêu của nhiều lexical là để quản lý tài liệu (tức là các định danh) trong code. Nói cách khác nó mang ý nghĩa cho những đinh danh. Giả sử ta có một đoạn code console.log(x/10), nó là vô nghĩa khi có một biến(hoặc định danh) x mà không có thứ gì cung cấp ý nghĩa cho biến đó. Lexical environment cung cấp ý nghĩa này (hoặc liên kết) thông qua Environment Record, hãy nhìn xuống tiếp sau đây.
  • Lexical Environment gồm có một Environment Record: Một Environment Record là một cách thú vị để nói rằng nó giữ một record của tất cả những định danh và các ràng buộc tồn tại trong Lexical Environment. Mỗi Lexical Environment có Environment Record riêng của nó.
  • Cấu trúc lexical nesting: Đây là một phần khá thú vị, về cơ bản thì môi trường tự nhiên bên trong tham chiếu đến môi trường tự nhiên bên phía ngoài xung quanh nó và môi trường tự nhiên bên phía ngoài này cũng có thể có môi trường tự nhiên bên phía ngoài của nó. Kết quả là, một môi trường tự nhiên có thể phục vụ như thể môi trường tự nhiên bên phía ngoài cho nhiều môi trường tự nhiên bên trong. Môi trường thiên nhiên toàn cục (global environment) là lexical environment duy nhất không có môi trường tự nhiên bên phía ngoài. Tiếng nói ở đây rất phức tạp, do đó tất cả chúng ta hãy sử dụng một phép ẩn dụ và nghĩ đến lexical environment như một lớp hành tây: global environment là lớp ngoài cùng của củ hành tây, mọi lớp tiếp theo phía bên dưới được lồng bên trong.

Tóm lại, môi trường tự nhiên sẽ giống như vậy này trong mã giả:

LexicalEnvironment = { EnvironmentRecord: { // Identifier bindings go here }, // Reference to the outer environment outer: < > };

  • Một Lexical Environment mới được tao ra mọi khi đoạn code được thực thi: Mỗi lần một function bên phía ngoài xung quanh nó được gọi, một Lexical Environment mới được tạo ra. Điều này rất quan trọng, tất cả chúng ta sẽ quay trở lại ở cuối nội dung bài viết. Lưu ý rằng: một function không phải là cách duy nhất để tạo một Lexical Environment, ta có thể dùng một khối lệnh hoặc một catch clause. Để đơn giản, trong nội dung bài viết này tất cả chúng ta chỉ tập trung vào evironment được tạo bởi function.

Nói một cách ngắn gọn, mỗi exection context có một Lexical Environment. Lexical Environment này thường chứa các biến, giá trị liên quan và cũng có mức giá trị tham chiếu đến giá trị bên phía ngoài của nó. Lexical Environment có thể là global environment, một module environment (có những ràng buộc cho những khai báo cấp rất tốt của module) hoặc một function environment (environment được tạo ra do sự gọi hàm).

Dựa vào khái niệm trên, tất cả chúng ta biết rằng một environment có quyền truy cập vào parent’s environment (môi trường tự nhiên cha) và parent environment của nó có quyền truy cập vào parent environment, vv…. Tập hợp các định danh mà mỗi environment có quyền truy cập gọi là scope (phạm vi). Tất cả chúng ta có thể xếp các scope vào trong 1 chuỗi các môi trường tự nhiên được gọi là “scope chain”.

Hãy nhìn một ví dụ về cấu trúc lồng:

var x = 10; function foo() { var y = 20; // miễn phí variable function bar() { var z = 15; // miễn phí variable return x + y + z; } return bar; }

Như bạn thấy, bar nest(lồng) trong foor. Để khiến cho bạn hình dung, tất cả chúng ta có thể xem sơ đồ sau: Scope chain hoặc chain of environments kết phù hợp với một function được lưu vào đối tượng người sử dụng hàm (function object) ở thời khắc nó được tạo. Nói cách khác, nó được khái niệm statically theo vị trí bên trong mã nguồn. Điều này còn được gọi là “lexical scope”. Tất cả chúng ta hãy đi nhanh hơn để hiểu sự khác biệt giữa dynamic scope và static scope, điều này sẽ làm rõ vì sao static scope(hoặc lexical scope) là cấp thiết để giành được closure.

Các tiếng nói có dynamic scope có “stack-based implementations”, có tức thị các biến cục bộ (local variable) và các đối số (argument) được lưu trên một ngăn xếp. Do đó, trạng thái chạy của lớp học ngăn xếp sẽ xác định đến thay đổi bạn đang đề cập đến là gì. Mặt khác, static scope là lúc các biến được tham chiếu trong một văn cảnh được ghi vào thời khắc tạo. Nói cách khác, cấu trúc của lớp học xác định những thay đổi bạn đang đề cập đến.

Thời điểm hiện tại, chúng ta cũng có thể tự hỏi là dynamic scope và static scope khác nhau thế nào. Sau đây là 2 ví dụ để giúp minh họa:

Ví dụ 1:

var x = 10; function foo() { var y = x + 5; return y; } function bar() { var x = 2; return foo(); } function main() { foo(); // Static scope: 15; Dynamic scope: 15 bar(); // Static scope: 15; Dynamic scope: 7 return 0; }

Tất cả chúng ta có thể nhìn thấy ở trên rằng static scope và dynamic scope trả về các giá trị khác nhau khi hàm bar được gọi. Với static scope, giá trị trả về của bar dựa trên giá trị của x tại thời khắc tạo foo. Điều này là vì cấu trúc static và lexical của lớp học, do đó x là 10 và kết quả là 15. Mặt khác, dynamic scope cho ta một ngăn xếp các biến được khái niệm theo thời kì chạy. Như vậy, x của tất cả chúng ta phụ thuộc vào phạm vi được khái niệm tự động hóa khi chạy. Khi chạy hàm bar, sẽ đẩy x = 2 vào đỉnh của ngăn xếp, làm foo trả lại giá trị 7.

Ví dụ 2:

var myVar = 100; function foo() { console.log(myVar); } foo(); // Static scope: 100; Dynamic scope: 100 (function () { var myVar = 50; foo(); // Static scope: 100; Dynamic scope: 50 })(); // Higher-order function (function (arg) { var myVar = 1500; arg(); // Static scope: 100; Dynamic scope: 1500 })(foo);

Tương tự, trong ví dụ dynamic scope của biến myVar sử dụng giá trị của tại nơi hàm này được gọi. Mặt khác, static scope của biến myVar được lưu trong phạm vi của 2 hàm lúc tạo ra. Như bạn thấy, dynamic scope thường làm tất cả chúng ta mơ hồ. Nhưng từ giờ tất cả chúng ta có thể làm rõ được phạm vi của miễn phí variable.

Một số điều giảng giải ở trên có thể làm cho bạn nghĩ này nội dung bài viết này sai chủ đề. Nhưng tôi đang đề cập đến những thứ giúp tất cả chúng ta nắm vững hơn về closure:

Mọi function đều sở hữu một execution context, chứa một environment mang đến ý nghĩa cho những biến và tham chiếu đến environment cha. Một tham chiếu đến enviroment cha làm cho tất cả những biến trong phạm vi cha có thể sẵn dùng cho hàm bên trong, bất kể hàm bên trong gọi ra ngoài hay bên trong phạm vi mà chúng được tạo ra. Do đó, sự xuất hiện của closure cứ thể như thể hàm nhớ được environment (hoặc scope) này bởi vì hàm tham có một chiếu đến environment và các biến được khái niệm trong environment đó.

Trở lại ví dụ cấu trúc lồng nhau:

var x = 10; function foo() { var y = 20; // miễn phí variable function bar() { var z = 15; // miễn phí variable return x + y + z; } return bar; } var test = foo(); test(); // 45

Dựa trên những hiểu biết cơ bản của tất cả chúng ta về hoạt động của environment, tất cả chúng ta nói theo cách khác rằng khái niệm environment sẽ trông giống như vậy này(lưu ý rằng đây chỉ là mã giả):

GlobalEnvironment = { EnvironmentRecord: { // built-in identifiers Array: ‘<funcvàgt;’, Object: ‘<funcvàgt;’, // etc.. // custom identifiers x: 10 }, outer: null }; fooEnvironment = { EnvironmentRecord: { y: 20, bar: ‘<funcvàgt;’ } outer: GlobalEnvironment }; barEnvironment = { EnvironmentRecord: { z: 15 } outer: fooEnvironment };

Xem Thêm : Apt, Suite, v.v. có nghĩa là gì?

Khi tất cả chúng ta gọi hàm test, tất cả chúng ta nhận được 45, đây là giá trị trả về từ việc gọi hàm bar (bởi vì foo trả về bar). bar có thể truy cập vào miễn phí avariable ngay cả những lúc hàm foo đã return bởi vì bar đã tham chiếu đến biến y thông qua outer environment của nó, đó là environment của foo. bar cũng có thể có thể truy cập vào global variable x bởi vì foo’s environment có thể truy cập vào global environment. Đây gọi là scope-chain lookup.

Quay trở về cuộc thảo luận của tất cả chúng ta về dynamic scope và static scope. Cho việc tùy chỉnh thiết lập closure, tất cả chúng ta không thể sử dụng dynamic scope thông qua dynamic stack để lưu trữ các biến của tất cả chúng ta. Lý do là bởi vì khi một hàm return, các biến sẽ bị đưa thoát khỏi stack và không còn nữa. Điều này là xích mích với khái niệm thuở đầu của tất cả chúng ta về closure. Điều gì sẽ xẩy ra khi tất cả chúng ta lưu tài liệu closure của parent context được lưu trong cái gọi là heap, được cho phép tài liệu tồn tại sau khoản thời gian chức năng được return (tức là trong cả sau khoản thời gian execution context bị đưa thoát khỏi stack).

Nghe có vẻ hay, một ý tưởng tốt. Thời điểm hiện tại để tất cả chúng ta nắm vững hơn, tất cả chúng ta hãy nhìn vào trong 1 vài ví dụ:

Ví dụ 1:

Tất cả chúng ta có một vòng lặp, sai trái khi tất cả chúng ta nỗ lực cố gắng liên kết biến đếm i trong vòng lặp for với một function trong vòng lặp for

var result = []; for (var i = 0; i < 5; i++) { result[i] = function () { console.log(i); }; } result[0](); // 5, expected 0 result[1](); // 5, expected 1 result[2](); // 5, expected 2 result[3](); // 5, expected 3 result[4](); // 5, expected 4

Trở lại những gì tất cả chúng ta vừa học, nó rất dễ dàng đế phát hiện ra sai làm ở đây. Tóm lại, ở đây environment sẽ trông như vậy này khi vòng lặp kết thúc

environment: { EnvironmentRecord: { result: […], i: 5 }, outer: null, }

Giả thiết sai ở đây là scope khác nhau cho tất cả 5 function trong mảng kết quả. Thay vào đó những gì thực sự diễn ra là environment (hoặc context/scope) là như nhau cho tất cả 5 function trong mảng kết quả. Do đó mọi khi biến i được tăng lên, nó sẽ update scope – được san sẻ bởi tất cả những function. Đó là lý do vì sao bất kỳ 1 trong 5 function đang cố truy cập i đều trả về là 5 (i bằng 5 khi thoát thoát khỏi vòng lặp)

Một phương pháp để sửa lỗi này là tạo ra một enclosing context thêm vào mỗi function để chúng giành được execution context/scope riêng:

var result = []; for (var i = 0; i < 5; i++) { result[i] = (function inner(x) { // additional enclosing context return function() { console.log(x); } })(i); } result[0](); // 0, expected 0 result[1](); // 1, expected 1 result[2](); // 2, expected 2 result[3](); // 3, expected 3 result[4](); // 4, expected 4

Một cách tiếp cận mới khá thông minh là sử dụng let thay vì var, vì let là một block-scoped do đó một ràng buộc định danh mới được tạo ra cho từng lần lặp trong vòng for.

var result = []; for (let i = 0; i < 5; i++) { result[i] = function () { console.log(i); }; } result[0](); // 0, expected 0 result[1](); // 1, expected 1 result[2](); // 2, expected 2 result[3](); // 3, expected 3 result[4](); // 4, expected 4

Ví dụ 2:

Trong ví dụ này, mọi khi tất cả chúng ta hiển thị lời gọi đến một function sẽ tạo ra một closure mới riêng biệt.

function iCantThinkOfAName(num, obj) { // This array variable, along with the 2 parameters passed in, // are ‘captured’ by the nested function ‘doSomething’ var array = [1, 2, 3]; function doSomething(i) { num += i; array.push(num); console.log(‘num: ‘ + num); console.log(‘array: ‘ + array); console.log(‘obj.value: ‘ + obj.value); } return doSomething; } var referenceObject = { value: 10 }; var foo = iCantThinkOfAName(2, referenceObject); // closure #1 var bar = iCantThinkOfAName(6, referenceObject); // closure #2 foo(2); /* num: 4 array: 1,2,3,4 obj.value: 10 */ bar(2); /* num: 8 array: 1,2,3,8 obj.value: 10 */ referenceObject.value++; foo(4); /* num: 8 array: 1,2,3,4,8 obj.value: 11 */ bar(4); /* num: 12 array: 1,2,3,8,12 obj.value: 11 */

Trong trường hợp này, tất cả chúng ta có thể thấy rằng mỗi lần gọi đến function iCantThinkOfAName sẽ tạo ra một closure mới, đó là foo và bar. Mỗi lần gọi closure function tiếp theo sẽ update closure variable num trong chính bản thân mình của closure, điều đó có tức thị biến num vẫn được sử dụng bởi function doSomething sau khoản thời gian iCantThinkOfAName đã return.

Ví dụ 3:

function mysteriousCalculator(a, b) { var mysteriousVariable = 3; return { add: function() { var result = a + b + mysteriousVariable; return toFixedTwoPlaces(result); }, subtract: function() { var result = a – b – mysteriousVariable; return toFixedTwoPlaces(result); } } } function toFixedTwoPlaces(value) { return value.toFixed(2); } var myCalculator = mysteriousCalculator(10.01, 2.01); myCalculator.add() // 15.02 myCalculator.subtract() // 5.00

Những gì tất cả chúng ta có thể nhìn thấy mysteriousCalculator là global scope, nó trả về 2 function. Tóm lại, environment trong ví dụ trên sẽ trông như vậy này:

GlobalEnvironment = { EnvironmentRecord: { // built-in identifiers Array: ‘<funcvàgt;’, Object: ‘<funcvàgt;’, // etc… // custom identifiers mysteriousCalculator: ‘<funcvàgt;’, toFixedTwoPlaces: ‘<funcvàgt;’, }, outer: null, }; mysteriousCalculatorEnvironment = { EnvironmentRecord: { a: 10.01, b: 2.01, mysteriousVariable: 3, } outer: GlobalEnvironment, }; addEnvironment = { EnvironmentRecord: { result: 15.02 } outer: mysteriousCalculatorEnvironment, }; subtractEnvironment = { EnvironmentRecord: { result: 5.00 } outer: mysteriousCalculatorEnvironment, };

Bởi vì function add và subtract của tất cả chúng ta đều tham chiếu đến environment của function mysteriousCalculator. Chúng sử dụng các biến trong environment để tính ra kết quả.

Ví dụ 4:

Ví dụ cuối cùng để chứng minh rằng việc sử dụng closure rất quan trọng: để duy trì một tham chiếu private đến một biến ở phạm vi bên phía ngoài (outer scope).

function secretPassword() { var password = ‘xh38sk’; return { guessPassword: function(guess) { if (guess === password) { return true; } else { return false; } } } } var passwordGame = secretPassword(); passwordGame.guessPassword(‘heyisthisit?’); // false passwordGame.guessPassword(‘xh38sk’); // true

Đây là một kỹ thuật khôn cùng mạnh mẽ, nó được cho phép function closure guessPassword có thể truy cập vào biến password và không được cho phép truy cập password từ bên phía ngoài

Tôi hi vọng nội dung bài viết này hữu ích và cho bạn một cách nhìn tổng quát về kiểu cách closure hoạt động trong Javascript. Như bạn thấy, sự hiểu biết những thành phần cấu thành giúp tất cả chúng ta dễ dàng hiểu được closure. Tất cả chúng ta sẽ không còn phải đau đầu trong những lần debug. Tôi cũng là một con người và cũng có thể có thể mắc sai trái, nếu như khách hàng thấy bất kỳ điểm sai nào trong nội dung bài viết của tôi xin hãy cho tôi biết. Nội dung bài viết này được dịch lại từ nguồn: https://medium.freecodecamp.org/lets-learn-javascript-closures-66feb44f6a44

You May Also Like

About the Author: v1000