Thông thường chúng ta cần thực hiện một hành động tương tự ở nhiều nơi của kịch bản(script).
Ví dụ: chúng ta cần hiển thị một thông báo đẹp mắt khi khách truy cập đăng nhập, đăng xuất và có thể ở một nơi khác.
Các hàm(function) là các khối code, nó góp phần chính trong xây dựng các khối code của chương trình. Chúng cho phép code được gọi nhiều lần mà không lặp lại.
Chúng ta đã thấy các ví dụ về các hàm có sẵn, như alert(message)
, prompt(message, default)
và confirm(question)
. Nhưng chúng ta cũng có thể tạo ra các hàm của riêng mình.
Nội dung chính
1. Khai báo hàm
Để tạo một hàm chúng ta có thể sử dụng một khai báo hàm .
Nó trông như thế này:
function showMessage() {
alert( 'Cafedev Hello everyone!' );
}
Từ khóa function
đi trước, sau đó đi các tên của hàm , sau đó một danh sách các thông số giữa các dấu ngoặc đơn (bằng dấu phẩy, khoảng trống trong ví dụ trên) và cuối cùng là đoạn code của hàm, cũng được gọi tên là “body của hàm”.
function name(parameters) {
...body...
}
Hàm của chúng ta có thể được gọi bằng tên của nó : showMessage()
.
Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
function showMessage() {
alert( 'Cafedev Hello everyone!' );
}
showMessage();
showMessage();
Gọi showMessage()
thực thi code trong hàm. Ở đây chúng ta sẽ thấy thông điệp hai lần.
Ví dụ này thể hiện rõ một trong những mục đích chính của các hàm: để tránh trùng lặp code.
Nếu chúng ta cần thay đổi thông báo hoặc cách nó được hiển thị, thì chỉ cần sửa đổi code ở một nơi là đủ
2. Biến cục bộ trong hàm
Là một biến được khai báo bên trong một hàm chỉ hiển thị bên trong hàm đó.
Ví dụ:
function showMessage() {
let message = "Hello, I'm JavaScript!"; // local variable
alert( message );
}
showMessage(); // Hello, I'm JavaScript!
alert( message ); // <-- Error! The variable is local to the function
3. Biến ngoài hàm
Một hàm cũng có thể truy cập vào một biến ở ngoài, ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let userName = 'John';
function showMessage() {
let message = 'Hello, ' + userName;
alert(message);
}
showMessage(); // Hello, John
Hàm toàn quyền truy cập vào biến ngoài. Nó có thể sửa đổi biến đó.
Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let userName = 'John';
function showMessage() {
userName = "Bob"; // (1) changed the outer variable
let message = 'Hello, ' + userName;
alert(message);
}
alert( userName ); // John before the function call
showMessage();
alert( userName ); // Bob, the value was modified by the function
Biến ngoài chỉ được sử dụng nếu không có biến cục bộ.
Nếu một biến cùng tên được khai báo bên trong hàm thì nó sẽ bỏ qua biến ngoài. Ví dụ, trong code bên dưới hàm sử dụng cục bộ userName
. Cái bên ngoài bị bỏ qua:
let userName = 'John';
function showMessage() {
let userName = "Bob"; // declare a local variable
let message = 'Hello, ' + userName; // Bob
alert(message);
}
// the function will create and use its own userName
showMessage();
alert( userName ); // John, unchanged, the function did not access the outer variable
4. Biến toàn cục
Các biến được khai báo bên ngoài bất kỳ hàm nào, chẳng hạn như userName
trong code ở trên, được gọi là biến toàn cục .
Các biến toàn cục có thể được nhìn thấy từ bất kỳ hàm nào (trừ khi bị che bởi các biết cục bộ).
Một cách code tốt đó là giảm thiểu việc sử dụng các biến toàn cục. Code hiện đại có ít hoặc không có biến toàn cục. Hầu hết các biến nằm trong hàm của họ. Đôi khi, chúng có thể hữu ích để lưu trữ dữ liệu cấp dự án.
5. Tham số(Parameters)
Chúng ta có thể truyền dữ liệu tùy ý cho các hàm bằng các tham số (còn được gọi là đối số hàm).
Trong ví dụ dưới đây, hàm có hai tham số: from
và text
.
function showMessage(from, text) { // arguments: from, text
alert(from + ': ' + text);
}
showMessage('Cafedev', 'Hello!'); // Ann: Hello! (*)
showMessage('Cafedev', "What's up?"); // Ann: What's up? (**)
Khi hàm được gọi trong các dòng (*)
và (**)
, các giá trị đã cho sẽ được sao chép vào các biến cục bộ from
và text
. Sau đó, hàm sẽ sử dụng chúng.
Dưới đây là một ví dụ nữa: chúng ta có một biến from
và truyền nó cho hàm. Xin lưu ý: hàm thay đổi from
, nhưng không thấy sự thay đổi bên ngoài, bởi vì một hàm luôn nhận được một bản sao của giá trị:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
function showMessage(from, text) {
from = '*' + from + '*'; // make "from" look nicer
alert( from + ': ' + text );
}
let from = "Ann";
showMessage(from, "Hello"); // *Ann*: Hello
// the value of "from" is the same, the function modified a local copy
alert( from ); // Ann
6. Giá trị mặc định
Nếu một tham số không được cung cấp, thì giá trị của nó sẽ trở thành undefined
.
Chẳng hạn, hàm đã nói ở trên showMessage(from, text)
có thể được gọi với một đối số duy nhất:
showMessage("Cafedev");
Đó không phải là một lỗi. Gọi như vậy sẽ xuất ra "Cafedev: undefined"
. Không có text
, vì vậy nó giả định rằng text === undefined
.
Nếu chúng ta muốn sử dụng một chế độ mặc định trong trường hợp này, thì chúng ta có thể chỉ định nó sau dấu=
:
function showMessage(from, text = "no text given") {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: no text given
Bây giờ nếu tham số text
không được truyền giá trị, nó sẽ nhận được giá trị"no text given"
"no text given"
là một chuỗi, nhưng nó có thể là một biểu thức phức tạp hơn, nó chỉ được ước tính và gán nếu tham số bị thiếu. Vì vậy, điều này cũng có thể:
function showMessage(from, text = anotherFunction()) {
// anotherFunction() only executed if no text given
// its result becomes the value of text
}
Chọn các tham số mặc định
Trong JavaScript, một tham số mặc định được Chọn mỗi khi hàm được gọi mà không có tham số tương ứng.
Trong ví dụ trên, anotherFunction()
được gọi mỗi lần showMessage()
được gọi mà không có tham số text
.
7. Các tham số mặc định thay thế
Đôi khi nó có ý nghĩa để đặt các giá trị mặc định cho các tham số không nằm trong khai báo hàm, nhưng ở giai đoạn sau, trong quá trình thực thi.
Để kiểm tra một tham số bị bỏ qua, chúng ta có thể so sánh nó với undefined
:
function showMessage(text) {
if (text === undefined) {
text = 'empty message';
}
alert(text);
}
showMessage(); // empty message
Hoặc chúng ta có thể sử dụng ||
toán tử:
// if text parameter is omitted or "" is passed, set it to 'empty'
function showMessage(text) {
text = text || 'empty';
...
}
Các công cụ JavaScript hiện đại hỗ trợ toán tử kết hợp nullish ??
, sẽ tốt hơn khi các giá trị giả như 0
, được truyền vào:
// if there's no "count" parameter, show "unknown"
function showCount(count) {
alert(count ?? "unknown");
}
showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown
8. Return giá trị
Một hàm có thể trả về một giá trị nào đó cho người gọi nó.
Ví dụ đơn giản nhất sẽ là một hàm tính tổng hai giá trị:
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
alert( result ); // 3
Lệnh return
có thể ở bất kỳ vị trí nào của hàm. Khi thực hiện đến nó, hàm dừng lại và giá trị được trả về cho người đã gọi nó(được gán vào result
ở trên).
Có thể có nhiều lần xuất hiện return
trong một hàm. Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
function checkAge(age) {
if (age >= 18) {
return true;
} else {
return confirm('Do you have permission from your parents?');
}
}
let age = prompt('How old are you?', 18);
if ( checkAge(age) ) {
alert( 'Access granted' );
} else {
alert( 'Access denied' );
}
Có thể sử dụng return
mà không có giá trị. Điều đó khiến hàm thoát ra ngay lập tức.
Ví dụ:
function showMovie(age) {
if ( !checkAge(age) ) {
return;
}
alert( "Showing you the movie" ); // (*)
// ...
}
Trong đoạn code trên, nếu checkAge(age)
trả về false
, thì showMovie
sẽ không tiến hành alert
.
Một hàm rỗng hoặc không có trả vềundefined
Nếu một hàm không trả về một giá trị, thì nó cũng giống như khi nó trả về undefined
:
function doNothing() { /* empty */ }
alert( doNothing() === undefined ); // true
Một return
trống cũng giống như return undefined
:
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
Không bao giờ thêm một dòng mới giữa return
và giá trị
Đối với một biểu thức dài trong return
, nó có thể hấp dẫn để đặt nó trên một dòng riêng biệt, như thế này:
return
(some + long + expression + or + whatever * f(a) + f(b))
Điều đó không hiệu quả, bởi vì JavaScript giả sử một dấu chấm phẩy sau return
. Điều đó sẽ làm việc giống như:
return;
(some + long + expression + or + whatever * f(a) + f(b))
Vì vậy, nó thực sự return trống rỗng.
Nếu chúng ta muốn biểu thức được trả về bao quanh nhiều dòng, chúng ta nên bắt đầu nó ở cùng một dòng với return
. Hoặc ít nhất là đặt dấu ngoặc đơn mở ở đó như sau:
return (
some + long + expression
+ or +
whatever * f(a) + f(b)
)
Và nó sẽ hoạt động như chúng ta mong đợi.
9. Đặt tên cho hàm
Hàm là hành động. Vì vậy, tên của họ thường là một động từ. Nó phải ngắn gọn, chính xác nhất có thể và mô tả hàm làm gì, để ai đó đọc code nhận được chỉ dẫn về hàm đó làm gì.
Trong thực tế hiện này để bắt đầu một hàm với tiền tố bằng lời nói mô tả mơ hồ một hành động nào đó. Phải có một thỏa thuận trong nhóm về ý nghĩa của các tiền tố.
Chẳng hạn, các hàm bắt đầu bằng "show"
thường hiển thị một cái gì đó.
Hàm bắt đầu với…
"get…"
– trả về một giá trị,"calc…"
– tính toán một cái gì đó,"create…"
– tạo ra một cái gì đó,"check…"
– kiểm tra một cái gì đó và trả lại một boolean, vv
Ví dụ về các tên như vậy:
showMessage(..) // shows a message
getAge(..) // returns the age (gets it somehow)
calcSum(..) // calculates a sum and returns the result
createForm(..) // creates a form (and usually returns it)
checkPermission(..) // checks a permission, returns true/false
Với các tiền tố được đặt đúng chỗ, việc lướt qua một tên hàm cho bạn hiểu loại công việc đó làm và loại giá trị mà nó trả về.
Một hàm – một hành động
Một hàm nên làm chính xác những gì được đề xuất bởi tên của nó.
Hai hành động độc lập thường ứng với hai hàm, ngay cả khi chúng thường được gọi cùng nhau (trong trường hợp đó chúng ta có thể thực hiện hàm thứ 3 gọi hai hàm đó).
Một vài ví dụ về việc phá vỡ quy tắc này:
getAge
– sẽ là xấu nếu nó hiển thịalert
với độ tuổi (chỉ nên nhận).createForm
– sẽ là xấu nếu nó sửa đổi tài liệu, thêm một biểu mẫu cho nó (chỉ nên tạo nó và trả lại).checkPermission
– sẽ là xấu nếu nó hiển thị thông báoaccess granted/denied
(chỉ nên thực hiện kiểm tra và trả lại kết quả).
Những ví dụ này giả định ý nghĩa phổ biến của tiền tố. Bạn và nhóm của bạn được tự do đồng ý về các ý nghĩa khác, nhưng thường thì chúng không khác nhau nhiều. Trong mọi trường hợp, bạn nên có một sự hiểu biết vững chắc về ý nghĩa của tiền tố, hàm tiền tố có thể và không thể làm gì. Tất cả các hàm có cùng tiền tố phải tuân theo các quy tắc. Và nhóm nên chia sẻ kiến thức.
Tên hàm Cực ngắn
Các hàm được sử dụng rất thường xuyên đôi khi có tên rất ngắn.
Ví dụ, framework jQuery xác định một hàm với $
. Các thư viện Lodash có các hàm cốt lõi của nó được đặt với tên _
.
Đây là những ngoại lệ. Tên hàm thường phải ngắn gọn.
10. Hàm = với một comment
Các hàm nên ngắn và làm chính xác một điều. Nếu điều đó là lớn, có lẽ nó đáng để chia hàm thư viện thành một vài hàm nhỏ hơn. Đôi khi tuân theo quy tắc này có thể không dễ dàng, nhưng đó chắc chắn là một điều tốt.
Một hàm riêng biệt không chỉ dễ dàng hơn để kiểm tra và gỡ lỗi – chính sự tồn tại của nó là một comment tuyệt vời!
Ví dụ, so sánh hai hàm showPrimes(n)
dưới đây. Đều cho ra số nguyên tố.
Biến thể đầu tiên sử dụng nhãn(nextPrime):
function showPrimes(n) {
nextPrime: for (let i = 2; i < n; i++) {
for (let j = 2; j < i; j++) {
if (i % j == 0) continue nextPrime;
}
alert( i ); // a prime
}
}
Biến thể thứ hai sử dụng một hàm isPrime(n)
để kiểm tra tính nguyên thủy:
function showPrimes(n) {
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
alert(i); // a prime
}
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
Biến thể thứ hai dễ hiểu hơn phải không? Thay vì đoạn code chúng ta thấy một tên của hành động ( isPrime
). Đôi khi mọi người gọi code đó như là tự mô tả chính nó.
Vì vậy, các hàm có thể được tạo ngay cả khi chúng ta không có ý định sử dụng lại chúng. Làm vậy cấu trúc code rất dễ đọc.
11. Tóm lược
Một khai báo hàm trông như thế này:
function name(parameters, delimited, by, comma) {
/* code */
}
- Các giá trị được truyền cho một hàm khi các tham số được sao chép vào các biến cục bộ của nó.
- Một hàm có thể truy cập các biến ngoài. Nhưng nó chỉ hoạt động từ trong ra ngoài. Code bên ngoài hàm không thấy các biến cục bộ của nó.
- Một hàm có thể trả về một giá trị. Nếu nó không, thì kết quả của nó là
undefined
.
Để làm cho code sạch và dễ hiểu, nên sử dụng chủ yếu các biến và tham số cục bộ trong hàm, không phải các biến ngoài.
Đặt tên hàm:
- Một tên nên mô tả rõ ràng những gì hàm làm. Khi chúng ta thấy một hàm gọi trong code, một cái tên hay ngay lập tức cho chúng ta hiểu nó làm gì và trả về.
- Hàm là một hành động, vì vậy tên hàm thường bằng lời nói.
- Có tồn tại nhiều tiền tố hàm nổi tiếng như
create…
,show…
,get…
,check…
và vân vân. Sử dụng chúng để gợi ý những gì một hàm đang làm.
Hàm là các khối code chính của các kịch bản. Bây giờ chúng tôi đã trình bày những điều cơ bản, vì vậy chúng tôi thực sự có thể bắt đầu tạo và sử dụng chúng. Nhưng đó chỉ là khởi đầu của con đường. Chúng tôi sẽ trở lại với nó nhiều lần, đi sâu hơn vào các tính năng nâng cao của nó.
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!