[Javascript] Nâng cấp kỹ thuật tạo Tham số trong Hàm JS
Bài đăng này đã không được cập nhật trong 2 năm
1. Khái niệm
Hàm là một khái niệm rất quen thuộc trong lập trình và nó có vai trò quan trọng trong việc tái sử dụng code, tăng tính module và giúp mã code trở nên dễ đọc hơn. Một hàm có thể có một hoặc nhiều tham số và chúng được sử dụng bên trong hàm, ngoài ra ta cũng có thể gán giá trị mặc định cho các tham số của hàm.
Có nhiều kỹ thuật tạo tham số trong hàm khác nhau và trong bài viết này mình sẽ giới thiệu tới anh em 3 kỹ thuật tạo tham số từ cơ bản đến nâng cao.
2. Kỹ thuật tạo Tham số trong Hàm Javascript
Ví dụ anh em mình được giao một task nhỏ: Tạo một hàm in ra thông tin người dùng.
Với yêu cầu này, anh em sẽ cùng mình áp dụng lần lượt 3 kỹ thuật tạo Tham số trong Hàm dưới đây.
2.1. Kỹ thuật "Truyền thống"
Đây là cách tạo tham số trong hàm sử dụng cú pháp đơn giản và quen thuộc nhất đối với tất cả anh em DEV.
// Khởi tạo giá trị mặc định cho trường age và address
function printPerson(name, age = 24, address = "Hà Nội") {
console.log(`Name: ${name}, Address: ${address}, Age: ${age}`);
}
printPerson("pdthien"); // Name: pdthien, Address: Hà Nội, Age: 24
Tuy nhiên, vấn đề bắt đầu phát sinh khi hàm printPerson đã được gọi ở nhiều nơi trong project. Khi ta cần thêm tham số mới vào hàm, đa phần để xử lý ổn thỏa, tránh ảnh hưởng tới project, ta thường thêm tham số mới đó vào cuối hàm.
VD1: Nghiệp vụ yêu cầu in thêm thông tin ngày sinh của người dùng, ta xử lý như sau:
// Bổ sung thêm tham số birthday
function printPerson(name, age = 24, address = "Hà Nội", birthday) {
console.log( `Name: ${name}, Address: ${address}, Age: ${age}, Birthday: ${birthday}`);
}
// Phải truyền lại giá trị cho trường age và address, mặc dù giá trị của 2 trường này không đổi
printPerson("pdthien", 24, "Hà Nội", "01/01/1999"); // Name: pdthien, Address: Hà Nội, Age: 24, Birthday: 01/01/1999
- Ưu điểm: Phù hợp với hàm có ít tham số.
- Nhược điểm: Khi gọi hàm, ta phải truyền đúng thứ tự và đầy đủ giá trị của tham số, mặc dù như VD1 phía trên, giá trị của trường age và address không hề thay đổi. Và câu chuyện càng trở nên rắc rối, khó quản lý hơn khi hàm của ta chứa quá nhiều tham số.
Để khắc phục nhược điểm "phải truyền đúng thứ tự và đầy đủ giá trị của tham số", "hàm chứa quá nhiều tham số", ta áp dụng kỹ thuật thứ 2: Kỹ thuật "Bỏ trứng vào cùng 1 giỏ"
2. Kỹ thuật "Bỏ trứng vào cùng 1 giỏ"
Ý tưởng của kỹ thuật này là tận dụng JSON Object để làm tham số truyền vào hàm và ta chỉ cần duy nhất một tham số để gắn thêm các trường mà ta cần.
var info = {
name: "pdthien"
};
function printPerson(info) {
// Khởi tạo giá trị mặc định cho trường age và address
let { name, age = 24, address = "Hà Nội" } = info;
console.log( `Name: ${name}, Address: ${address}, Age: ${age}`);
}
printPerson(info); // Name: pdthien, Address: Hà Nội, Age: 24
Nếu cần bổ sung thêm trường mới, ta chỉ việc gắn thêm trường đó vào tham số object của hàm.
VD2: Xử lý lại yêu cầu in thêm thông tin ngày sinh, ta sửa như sau:
// Truyền tham số mà không cần quan tâm đến thứ tự các trường
var info = {
birthday: "01/01/1999", // Thêm trường birthday
name: "pdthien"
};
function printPerson(info) {
// Khởi tạo giá trị mặc định cho trường age và address
let { name, age = 24, address = "Hà Nội", birthday } = info;
console.log(`Name: ${name}, Address: ${address}, Age: ${age}, Birthday: ${birthday}`);
}
printPerson(info); // Name: pdthien, Address: Hà Nội, Age: 24, Birthday: 01/01/1999
- Ưu điểm: Cải thiện được các nhược điểm của kỹ thuật "truyền thống": Không cần quan tâm thứ tự của các trường cần truyền và Không cần điền lại giá trị mặc định của một trường nếu không có sự thay đổi (như trường age và address ở trong VD2).
- Nhược điểm: Tuy nhiên, đối với người mới vào dự án, khi đọc hàm họ có thể không biết rõ các trường được bao gồm trong tham số object của hàm, gây ra việc mất thời gian phải đọc lại hàm để truyền đầy đủ các trường.
Để giải quyết tất các các nhược điểm của 2 kỹ thuật trên. Ta áp dụng một kỹ thuật rất hay có tên Destructuring JSON.
3. Kỹ thuật "Destructuring JSON"
Về ý tưởng, kỹ thuật này tận dụng cơ chế Destructuring đối với Object JSON của Javascript.
// Khởi tạo giá trị mặc định cho trường age và address
function printPerson({ name, age = 24, address = "Hà Nội"}) {
console.log(`Name: ${name}, Address: ${address}, Age: ${age}`);
}
printPerson({name: "pdthien"}); // Name: pdthien, Address: Hà Nội, Age: 24
VD3: Xử lý lại yêu cầu in thêm thông tin ngày sinh, ta sửa như sau
// Thêm trường birthday
function printPerson({ name, age = 24, address = "Hà Nội", birthday}) {
console.log(`Name: ${name}, Address: ${address}, Age: ${age}, Birthday: ${birthday}`);
}
// Truyền tham số mà không cần quan tâm đến thứ tự các trường
// Giá trị trường address và age không thay đổi nên không cần truyền
printPerson({birthday: "01/01/1999", name: "pdthien"}); // Name: pdthien, Address: Hà Nội, Age: 24, Birthday: 01/01/1999
Ưu điểm:
- Truyền tham số mà không cần quan tâm đến thứ tự các trường, giúp việc code trở nên linh hoạt.
- Không cần gán lại giá trị mặc định của một trường nếu không có sự thay đổi (như trường age và address ở trong VD3)
- Trong các công cụ lập trình (vd: VSCode), khi gọi hàm sẽ được gợi ý các trường cần truyền, giúp việc đọc tham số trong hàm tường minh hơn.
Trên đây là 3 kỹ thuật giúp cải tiến việc tạo Tham số trong Hàm mà mình hy vọng sẽ hữu ích cho anh em.
Nếu anh em có góp ý hay thảo luận thì comment ngay dưới bài viết nhé. Tks all !!!
All rights reserved
Bình luận
Quá bổ ích. A cám ơn e đã chia sẻ.
Cảm ơn anh đã đón nhận :vv
Quá tuyệt vời. Em rất thích ăn trứng ạ !
Anh em mình chia đôi trứng nhé em ơi :v
Quá tuyệt với, Cách tạo hàm truyền thống khi thêm tham số dạng optional ta có thể gọi hàm như trên C# ko em? VD: printPerson(name, birthday: '23/03/2023')
Cách tạo hàm truyền thống thì không được anh ơi. Nhưng cách thứ 3 "Destructuring JSON" đã có gắng hướng đến sự tương tự như trong C# đó anh ơii (printPerson( { name, birthday: '23/03/2023' } ))
Chào bạn, cám ơn bạn đã chia sẻ.
Mình có 1 chút thắc mắc thế này:
Chào Hà, mình xin trả lời thắc mắc của bạn như sau:
Câu hỏi 1. Có lưu ý gì để đổi từ kỹ thuật 1 sang kỹ thuật 2/3 không?
Nếu bạn muốn chuyển 1 hàm đã được viết từ trước bằng kỹ thuật 1 sang kỹ thuật 2, 3 thì hiện tại mình chưa có ý tưởng gì tuyệt vời cho bạn. Nếu chuyển kỹ thuật bạn phải refactor lại tất cả các chỗ đang gọi hàm.
Câu hỏi 2. Nên gom nhóm thế nào cho hợp lý hay tất cả trường hợp đều nên dùng kỹ thuật 3?
Mục đích mình viết bài này nhằm khuyến khích mọi người sử dụng kỹ thuật số 3. Tuy nhiên, để best practice nhất, theo mình thì nếu hàm của bạn có ít tham số (từ 3 tham số trở xuống) và bạn chắc chắn rằng các tham số trong hàm đó của bạn là không thay đổi về sau thì bạn nên sử dụng kỹ thuật số 1. Nếu hàm của bạn có từ 4 tham số trở lên hoặc bạn không thể chắc chắn rằng các tham số trong hàm của mình sẽ không đổi trong tương lai thì bạn nên sử dụng kỹ thuật số 3. Còn kỹ thuật số 2 bạn nên sử dụng với Typescript tạo Interface để tận dụng được tối đa sức mạnh của kỹ thuật này.
Câu hỏi 3. Câu hỏi này của bạn rất hay, bạn nói đúng, bạn có thể kết hợp các kỹ thuật này với nhau, trong 1 hàm những required params thì sử dụng kỹ thuật 1, những optional params thì sử dụng kỹ thuật 3. Tuy nhiên, nếu hàm của bạn có quá nhiều required params, kèm thêm cả optional params thì bạn có thể kết hợp bằng cách tạo 2 tham số object trong hàm, 1 object cho required params, 1 object cho optional params. Ví dụ: hàm printPerson của mình có 2 trường name + age là required, còn address + birthday là optional, mình có thể làm như sau:
function printPerson({name, age}, {address = "HN", birthday}) { console.log(
Name: ${name}, Address: ${address}, Age: ${age}, Birthday: ${birthday}
); }printPerson({name: "pdthien", age: 24}, {birthday: "01/01/1999"})
Cảm ơn Hà!
Cám ơn Thiện đã dành thời gian trả lời 3 thắc mắc của mình siêu chi tiết như này, đặc biệt là suggestions của bạn ở câu 2 và solutions cho câu 3. Đúng là trước đó mình lại chưa nghĩ đến chuyện gom cả các required params vào chung 1 object cho tiện 😅
À, và ngoài 3 kỹ thuật bạn đã nêu trong bài viết, mình muốn bổ sung thêm 1 kỹ thuật cũng khá thú vị nữa (dù hơi ít trường hợp cần dùng đến), là dùng kết hợp rest và spread của ES6. Kỹ thuật này thường được dùng khi mình không chắc chắn (và cũng không quy định cụ thể) về số lượng params có thể truyền vào khi định nghĩa hàm. Nó cũng có tác dụng tương tự "Bỏ trứng vào cùng 1 giỏ", nhưng "giỏ" này không phải object mà là 1 array.
Ví dụ hàm tính tổng n số:
Hy vọng sẽ có lúc kỹ thuật này hữu ích với mọi người! ^ ^
Cảm ơn Hà đã góp ý nha 😁
Bếch luôn làm PBI router
Đỉnh quá thần đồng unittest ơi :vv