[Swift] Nắm vững Phạm vi Biến số (Scope) để Code Sạch và Dễ Debug
Chào các bạn, hôm nay chúng ta sẽ cùng tìm hiểu về một khái niệm quan trọng trong Swift: Phạm vi biến số (Scope). Hiểu rõ Scope sẽ giúp code của bạn trở nên sạch sẽ, dễ đọc, dễ bảo trì và đặc biệt là dễ debug hơn rất nhiều!
Scope là gì?
Scope của một biến số hoặc hằng số xác định nơi nó có thể được truy cập và sử dụng trong code. Nói đơn giản, một biến số chỉ "sống" và "hoạt động" trong một vùng nhất định.
Có 2 loại phạm vi:
- Global scope (Phạm vi toàn cục): Biến số hoặc hằng số khai báo ở đây có thể được truy cập từ bất kỳ đâu trong chương trình.
- Local scope (Phạm vi cục bộ): Biến số hoặc hằng số khai báo ở đây chỉ có thể được truy cập bên trong khối code mà nó được khai báo, ví dụ như trong hàm, vòng lặp, hoặc câu lệnh
if
.
Tại sao Scope lại quan trọng?
Hãy tưởng tượng bạn đang xây một ngôi nhà. Mỗi phòng có chức năng riêng, và bạn sẽ không muốn để đồ đạc lung tung khắp nơi, phải không? Scope cũng tương tự như vậy, nó giúp tổ chức code, tránh xung đột và tăng tính dễ đọc.
Ví dụ 1:
var globalName = "John"
func sayHello() {
print("Xin chào, \(globalName)!") // Truy cập được globalName
}
sayHello()
Trong ví dụ này, globalName
được khai báo ngoài hàm, tức là trong global scope. Do đó, hàm sayHello()
có thể truy cập và sử dụng nó.
Ví dụ 2:
func greet() {
let localName = "Jane" // localName chỉ tồn tại trong hàm greet()
print("Chào \(localName)!")
}
greet()
print(localName) // Lỗi! localName không tồn tại ở đây
Trong ví dụ này, localName
được khai báo bên trong hàm greet()
, tức là trong local scope của hàm. Vì vậy, code bên ngoài hàm không thể truy cập được localName
.
Variable Shadowing: Che khuất biến số
Điều gì xảy ra khi bạn khai báo 2 biến số có cùng tên nhưng khác phạm vi?
let score = 100 // global scope
func updateScore() {
let score = 200 // local scope, che khuất score ở global scope
print("Score trong hàm: \(score)")
}
updateScore()
print("Score toàn cục: \(score)")
Kết quả:
Score trong hàm: 200
Score toàn cục: 100
Trong ví dụ này, score
trong hàm updateScore()
che khuất biến score
ở global scope. Khi code trong hàm tham chiếu đến score
, nó sẽ sử dụng score
được khai báo bên trong hàm.
Variable Shadowing trong Initializer
Một trường hợp đặc biệt của Variable Shadowing xảy ra trong initializer của struct
hoặc class
. Khi bạn khai báo tham số của initializer trùng tên với property của struct
hoặc class
, tham số đó sẽ "che khuất" property.
Để truy cập property trong trường hợp này, bạn cần sử dụng từ khóa self
.
Ví dụ:
struct Product {
var name: String
var price: Double
init(name: String, price: Double) {
self.name = name // self.name là property của Product
// name là tham số của initializer
self.price = price
}
}
Trong ví dụ này, self.name
và self.price
tham chiếu đến property của struct Product
, còn name
và price
là tham số của initializer.
Sử dụng self
trong trường hợp này giúp:
- Trình biên dịch Swift hiểu rõ code:
self
phân biệt rõ ràng property và tham số, tránh nhầm lẫn cho trình biên dịch. - Code dễ đọc hơn:
self
giúp người đọc code dễ dàng nhận biết bạn đang thao tác với property củastruct
hoặcclass
.
Variable Shadowing: Khi nào nên sử dụng?
Mặc dù Variable Shadowing có thể gây nhầm lẫn nếu lạm dụng, nó cũng có thể hữu ích trong một số trường hợp, giúp code gọn gàng và dễ đọc hơn:
1. Unwrap Optionals:
var userName: String? = "Anna"
if let userName = userName { // Sử dụng lại tên userName
print("Tên người dùng: \(userName)") // userName đã được unwrap
}
Trong trường hợp này, userName
bên trong if let
là một String
bình thường, không phải String?
, và nó "che khuất" optional userName
ở bên ngoài.
2. Câu lệnh Guard:
func processOrder(itemId: Int?) {
guard let itemId = itemId else { return } // Sử dụng lại tên itemId
// itemId đã được unwrap, có thể sử dụng trực tiếp ở đây
print("Xử lý đơn hàng \(itemId)")
}
Tương tự như if let
, guard let
kết hợp với variable shadowing giúp code gọn gàng và dễ hiểu hơn.
3. Phạm vi hẹp:
Nếu một biến số chỉ được sử dụng trong một phạm vi rất hẹp, ví dụ như trong một vòng lặp for, bạn có thể sử dụng variable shadowing để tránh phải nghĩ ra một cái tên khác cho biến số đó. Tuy nhiên, hãy cân nhắc kỹ trước khi sử dụng trong trường hợp này.
Các lưu ý
- Khai báo biến số trong phạm vi hẹp nhất có thể.
- Sử dụng tên biến rõ ràng và dễ hiểu.
- Sử dụng Variable Shadowing một cách cẩn thận, chỉ khi nó thực sự giúp code rõ ràng hơn. Bên cạnh việc sử dụng
self
trong initializer, bạn cũng cần chú ý đến Variable Shadowing trong các trường hợp khác như unwrap optional, câu lệnhguard
, và phạm vi hẹp. Luôn ưu tiên code rõ ràng, dễ hiểu và tránh gây nhầm lẫn cho người đọc.
Nắm vững Phạm vi biến số (Scope) và Variable Shadowing là bước quan trọng để viết code Swift sạch, dễ bảo trì và ít lỗi. Hãy luyện tập và áp dụng vào các dự án của bạn nhé!