Bao đóng (lập trình máy tính)

Trong khoa học máy tính, bao đóng (closure) là một hàm hay một tham chiếu tới một hàm cùng với môi trường tham chiếu - một bảng chứa tham chiếu đến mỗi biến không phải cục bộ (hay còn gọi là biến tự do). Closure còn được gọi với tên là lexical closure (bao đóng hay function closure (bao đóng hàm). Bao đóng khác với một con trỏ hàm thuần túy ở chỗ nó cho phép một hàm có thể truy cập các biến không phải cục bộ ngay cả khi hàm này được gọi ngoài phạm vi của nó.
Ví dụ.
Đoạn mã Python dưới đây định nghĩa một hàm codice_1 với một biến cục bộ codice_2 và một hàm lồng (nested function) codice_3
def counter():
 x = 0
 def increment(y):
 nonlocal x
 x += y
 print(x)
 return increment
Bao đóng được trả lại bởi codice_1 có thể được gán cho một biến:
counter1_increment = counter()
counter2_increment = counter()
Khi chúng ta gọi hàm codice_3 thông qua bao đóng sẽ nhận được kết quả như sau:
counter1_increment(1) # in ra kết quả 1
counter1_increment(7) # in ra kết quả 8
counter2_increment(1) # in ra kết quả 1
counter1_increment(1) # in ra kết quả 9
Sự khác biệt về ngữ cảnh.
Phạm vi tác dụng.
Các ngôn ngữ lập trình khác nhau không phải lúc nào cũng thống nhất về lexical environment(tạm dịch: phạm vi tác dụng) và định nghĩa về bao đóng trong các ngôn ngữ vì thế cũng có thể khác nhau. Định nghĩa đơn giản nhất của lexical environment chỉ ra một tập các binding của các biến trong một phạm vi nào đó. Đó cũng chính là điều bao đóng muốn lưu giữ (capture) lại. 
Ý nghĩa binding (tạm dịch: gắn kết) trong các ngôn ngữ cũng khác nhau.
Trong các ngôn ngữ mệnh lệnh (imperative languages) các biến được gắn kết đến các vùng bộ nhớ lưu trữ giá trị của các biến đó. Việc lưu giữ lại các biến kiểu này được gọi là lưu giữ bằng tham chiếu (capturing by reference). Đoạn mã sau miêu tả một ví dụ trong ECMAScript
// ECMAScript
var f, g;
function foo() {
 var x = 0;
 f = function() { return ++x; };
 g = function() { return --x; };
 x = 1;
 alert('Bên trong foo, gọi hàm f(): ' + f()); // hiện ra giá trị "2"
foo();
alert('Gọi hàm g(): ' + g()); // hiện ra giá trị "1"
alert('Gọi hàm f(): ' + f()); // hiện ra giá trị "2"
Trong ví dụ trên hàm codice_6 và các bao đóng được tham chiếu bởi các biến codice_7 và biến codice_8 đều sử dụng chung một vùng bộ nhớ khi truy cập biến codice_2. Chính vì vậy giá trị của x đều thay đổi sau mỗi lần gọi hàm codice_6 hay gọi các hàm qua các bao đóng codice_7 và biến codice_8
Trong khi đó rất nhiều các ngôn ngữ lập trình khác, ví dụ như ngôn ngữ ML, việc gắn kết biến lại đi liền trực tiếp với giá trị của biến. Kiểu lưu giữ này được gọi là lưu giữ bằng giá trị (capturing by value)
Một số ngôn ngữ cho phép chúng ta lựa chọn kiểu lưu trữ theo ý muốn. Ví dụ trong ngôn ngữ C++11 hay PHP, chúng ta sử dụng từ khóa codice_13 để lưu trữ biến theo tham chiếu. Khi không dùng từ khóa đó, các biến sẽ được lưu trữ theo giá trị.
Thoát khỏi phạm vi tác dụng.
Các ngôn ngữ lập trình khác nhau cũng có sự khác nhau trong việc thoát khỏi phạm vi tác dụng xác định bởi các lệnh của ngôn ngữ đó, ví dụ: codice_14, codice_15 hay codice_16.
Ví dụ sau chỉ ra sự khác biệt trong hai ngôn ngữ Smalltalk và ECMAScript:
"Smalltalk"
foo
 | xs |
 xs:= #(1 2 3 4).
 xs do: [:x | ^x].
 ^0
bar
 Transcript show: (self foo printString) "in ra kết quả 1"
// ECMAScript
function foo() {
 var xs = [1, 2, 3, 4];
 xs.forEach(function (x) { return x; });
 return 0;
alert(foo()); // in ra kết quả 0
Các đoạn mã trên sẽ cho ra các kết quả khác nhau vì toán tử codice_17 của Smalltalk thực thi khác với toán tử codice_14 trong EMCAScript. Toán tử codice_14 trong EMCAScript sẽ thoát ra khỏi bao đóng phía trong và bắt đầu một vòng lặp mới của vòng lặp codice_20 trong khi toán tử codice_17 thực thi trên biến codice_2 (lệnh codice_23 sẽ kết thúc vòng lặp codice_24 và trả lại chương trình từ hàm codice_6.