01/10/2018, 16:15
Vấn đề closure trong javascript
mọi người giải thích cho em về closure với. em đi pv toàn bị hỏi về closure trong javascript mà ko biết trả lời như nào. em có đọc tài liệu rồi mà không vào a.
Bài liên quan
Định nghĩa closure thế này
Đầu tiên bạn phải hiểu “lexical scope” là gì? Nếu không xét cú pháp ES6 thì chỉ có 1 cách tạo scope trong JavaScript là sử dụng function definition để tạo scope.
Ban đầu có 2 block như thế này:
JavaScript không hỗ trợ lexical scope theo block scope, mà chỉ có function mới tạo được scope. Để tạo scope thì với mỗi block được bọc lại bằng 1 function definition. Cách này đi ngược lại tư duy thông thường là tạo function trước rồi viết block cho function sau. Bây giờ tạo block trước rồi viết function sau.
Với block
{ ... }
, thêm đằng trước{
bằng chuỗifunction function_name() { ...
inner()
tạo scope choscope 2
,outer()
tạo scope choscope 1
Giả sử Lúc JavaScript engine thực thi (chạy) câu lệnh
console.log(a + b)
thì trình tự như thế này, như đoạn hội thoại giữa 2 người: Engine (thực thi lệnh) và Compiler (lưu trữ tất cả các variable declaration)Bước đầu: Compiler đọc từ nguyên tất cả các code 1 lần từ trên xuống dưới, Compiler lưu variable declaration cho từng biến như sau:
Bước thứ hai: Engine chạy lại code 1 lần nữa từ đầu đến cuối, nhưng hiện tại chỉ xét Engine đang thực hiện lệnh
console.log(a + b)
. Lúc này bắt đầu cuộc hội thoại (conversation):console
trong scope 2? Không có. Để mình tìm tiếp scope 1 là outer scope của scope 2. Hmm, scope cũng không cóconsole
. Thôi ra global scope vậy. A ha, tìm thấy rồi, giá trịconsole
đây Engine.log
, à, property accessor, mò object thôi, có log (mình skip phần này). Tìm tiếp. Biếna
? Engine ơi? Biến a có giá trị gì vậy?a
. Giá trịa
đây Engine.b
.b
là gì vậy Compiler?b
á. May quá,b
ở ngay scope 2 luôn, giá trị củab
nè.b
rồi. Thanks, thế là trong câu lệnhconsole.log
. Chạy lệnh tiếp theo thôi.Trở lại định nghĩa, trong định nghĩa có 2 ý, ý đầu tiên là:
Theo định nghĩa (1):
Từ 2 yếu tố trên closure phải là có dạng như function
inner()
, định nghĩa trong function definition, hoặc là top-function có 1 outer scope duy nhất là global.Nhưng theo định nghĩa (2), global luôn luôn thực thi (execution) nên trường hợp top-function bị loại.
Vậy closure phải là 1 inner function trong 1 function khác, như
inner()
.Để thoả mãn định nghĩa (2) thì phải có cách để khi Engine thực hiện function call
inner()
thì lexical scope lúc thực hiện function call không thấy variable declaration biếnb
củaouter()
Để giải quyết được thì xem lại về
Types
của JavaScript.function
là subtype củaobject
. Những gì làm được với object thì cũng làm được vớifunction
Trở lại ví dụ outer và inner, bây giờ
outer()
trả về functioninner()
, hay trả function nameinner
. Return type củaouter()
là function object, không phải là return type củainner()
làundefined
Lúc này
inner()
là closure theo định nghĩaSau đó Engine gọi function call tới
outer()
, nhận giá trị function object gán vào biếnclosure
. Sau đó Engine gọi function call đếnclosure
. Vìclosure
là function object, hay callble object nên có thể đặt()
vào sau tên củaclosure
thànhclosure()
để tạo function call.Engine chạy
outer()
gáninner
vàoclosure
.outer()
đã chạy xong.Lúc này
closure
ở global scope, chỉ thấyouter
,closure
, nhưng không thấya
ở scope 1,outer scope
Lúc gọi
closure()
. Doclosure
là reference (pointer) đếninner
, nên thực chất lệnhclosure()
thànhinner()
. Engine thực hiện các lệnh tronginner()
vẫn thấya
ởouter()
(theo lexical scope), nhưng closure thì không thấya
(cũng theo lexical scope).Bây giờ tiếp tục Engine thực hiện
closure()
theo đoạn code sau:Lúc này, Compiler có các variable declarations:
Engine bắt đầu cuộc hội thoại, vị trí Engine thực hiện ở global scope:
closure()
nào, biến closure đâu?closure
ở global scope, giá trị là function object đây Engine.closure
, có function call()
kế bên.closure
là function object, thực thi function call thôi.closure()
rồi, chạy lệnh tiếp theo,a = 2
. Compiler ơi?a
là gì vậy?a
không có trong global scope, để tôi tạo biếna
cho. Ôi thôi, ‘use strict’ mode, không tạo được rồi. Engine ơi, tôi không tìm được, tôi gửi giá trịundefined
(Trường hợp không có strict mode xem tiếp phần bonus)a
nhậnundefined
, báo lỗi cho user biến,Reference Error
nè.b = 2
. Compiler ơi,b
là gì vậy?b
không thấy trong global scope. Tôi trả tiếpundefined
vậy.undefined
!! báo lỗi cho user tiếp,Reference Error
.Bonus:
Giả sử Engine đang bắt đầu thực thi lệnh
a = 2
sau lệnhclosure()
, nhưng lệnhuse strict;
bị xoá. Nghĩa là Engine đang thực thi lệnh ở chế độ non-strict mode.a
? Cho mình thông tin biếna
đi Compilera
ở global scope, để mình tạo biến global tên a vậy. … Engine ơi, mình tạo biến a rồi nè.a
thôi. (a
do Compiler tạo là global variable, không liên quan đến biếna
trong outer scope).b = 2
. Compiler, biến b đâu rồi?b
ở global scope, thôi tạo tiếp vậy… Mình tạo biếnb
rồi nè,b
ở global scope nhé Compilerb
rồi, bán giá trị 2 vàob
. (b
do Compiler tạo cũng là global variable, không liên quan đếnb
của inner scope).Rút gọn từ You don’t know JS: Scope and Closures
Nếu có thể bạn đọc nguyên cuốn sách để hiểu toàn diện.
đoạn code cuối mình chạy thử vẫn ra đúng kết quả chứ có báo lỗi đâu nhỉ?
Thank bạn, do mình để
use strict
sai chỗ, chính xác là ở đầu file hoặc script.Trường hợp không có strict mode, thì sau 2 lệnh assignment,
a
vàb
đều trở thành global variables.à, mình hiểu rồi, bạn đang để 2 biến a, b ở dưới kia để chỉ rằng nó không liên quan gì đến biến a, b trong closure đúng không.
vậy mà nãy giờ mình cứ nghĩ bạn để 2 biến đó để xem nó ảnh hưởng đến kết quả của
closure()
(yaoming)Yeah, nó không có liên quan. Mình cũng thêm phần xử lý khi không có “strict mode”, chương trình kết thúc không báo lỗi.
Thực ra định viết ngắn thôi, ôn lại những gì đã học. Ai ngờ nó dài thế này luôn
đùa chứ bác đọc kiến thức bác chia sẻ lại càng hoang mang. em tưởng closure là func khai báo trong 1 scope nào đó có tác dụng để func đó sử dụng các biến của scope bao nó. mà h đọc của bác thấy nó rộng lớn quá
thế thì bạn đang “tưởng” sai rồi, có ng chữa cho bạn rồi đó