1. Tổng quan về hàm trong JavaScript

Hàm là một hay nhiều đoạn mã được viết ra để thực thi một hoặc nhiều hành động khi gọi nó. Hàm(Function) gọi chung là subprogram (chương trình con) có thể được gọi ở bên ngoài hoặc bên trong chính nó. Nó bao gồm tập hợp các câu lệnh gọi là function body. Các giá trị có thể truyền đến một hàm, và một hàm có thể trả về giá trị. Bây giờ, với các ứng dụng hiện đại, các function có thể là một chương trình hoàn chỉnh, chứ không phải là khái niệm tổng quát như ”subprogram” nữa. Có sự khác nhau giữa function và procedure (thủ tục) rằng sự lý tưởng của function nên trả về một giá trị còn procedure thì không ( bây giờ điều này có thể thay đổi theo ngôn ngữ lập trình). Code bên trong function không được biên dịch cho tới khi được gọi đến. Chính vì vậy khi sử dụng function sẽ giúp chương trình được linh hoạt hơn.

Một số đặc điểm của hàm trong Js:

  • Hàm có thể không có tham số, 1 tham số hay thậm chí là nhiều tham số
  • Nội dung của hàm phải được đặt trong cặp dấu ngoặc nhọn ({}), dù cho không có câu lệnh nào hoặc nhiều câu lệnh bên trong nó.
  • Hàm có thể có giá trị trả về hoặc không có giá trị trả về.
  • Kiểu của tham số hay giá trị trả về trong hàm là không được quy định. Do đó, bạn thường phải kiểm tra kiểu của giá trị truyền vào hàm. Nếu không, bạn sẽ rất dễ bị sai.

2. Khai báo hàm trong JavaScript

Để khai báo hàm, chúng ta dùng từ khóa function, theo sau nó là:

  • Tên hàm
  • Danh sách các tham số truyền vào hàm, được đặt trong ngoặc đơn và cách nhau bởi dấu phẩy.
  • Các câu lệnh của JavaScript để tạo ra một hàm, được đặt trong ngoặc nhọn {…}.

2.1. Hàm cơ bản

Đây là dạng cơ bản nhất của hàm trong javascript, có cú pháp như sau:

function funName() {
//code
}

Trong đó: funName là tên của hàm mà các bạn muốn đặt còn function là từ khóa bắt buộc.

Ví dụ:

function getWebsite() {
document.write('https://laptrinhtudau.com');
}

2.2. Hàm có tham số truyền vào

Đây là một dạng hàm rất hay được sử dụng trong dự án. Hàm có tham số là loại hàm mà khi gọi hàm ta phải truyền giá trị vào cho nó. Tùy vào giá trị được truyền mà hàm sẽ thực thi và cho ra kết quả khác nhau.

Cú pháp :

function funName(param_1, ..., pram_n) {
//code
}

Trong đó:

  • funName là tên của hàm các bạn muốn đặt.
  • param_1,…,pram_n là các tham số mà các bạn muốn truyền vào hàm(không giới hạn số lượng)

Ví dụ: tạo hàm tính tổng của 2 số bất kỳ

function getSum(a, b) {
document.write('Tổng: ' + (a + b));
}

2.3. Hàm có tham số mặc định

Đây thực ra là dạng hàm có truyền tham số và đồng thời xét luôn giá trị mặc định cho các tham số đó. Cú pháp của nó gần tương tự việc khai báo hàm có tham số truyền vào:

function funName(param_1 = value_1, ..., pram_n = value_2) {
//code
}

Trong đó:

  • funName là tên của hàm các bạn muốn đặt.
  • param_1,…,pram_n là các tham số mà các bạn muốn truyền vào hàm(không giới hạn số lượng).
  • value_1,…,value_n là các giá trị tương ứng với các pram.

Ví dụ:

function getSum(a = 1, b = 2) {
document.write('Tổng: ' + (a + b));
}

2.4. Hàm có giá trị trả về và không

Trong javascript có hai loại hàm,đó là hàm có giá trị trả về và hàm không có giá trị trả về:

  • Đối với hàm có giá trị trả về thì phải sử dụng từ khóa return
  • Và ngược lại đối với hàm không có giá trị trả về thì không có từ khóa return.

Ví dụ:

function getSum(a = 1, b = 2) {
return a + b;
}

3. Cách gọi hàm trong JavaScript

Sau khi khai báo được hàm thì ta sẽ gọi hàm để sử dụng. Việc gọi hàm cũng khá là đơn giản thôi!

3.1. Gọi hàm cơ bản

Cú pháp:

funName();

Trong đó: funname là tên hàm mà bạn muốn gọi.

Ví dụ:

getWebsite();

3.2. Hàm có tham số truyền vào

Để gọi dạng hàm này thì các bạn cũng dùng cú pháp như hàm cơ bản, nhưng đồng thời các bạn truyền thêm các param vào theo cú pháp:

funName(param_1, ..., param_n);

Ví dụ:

<script type="text/javascript">
function getSum(a,b){
document.write('Tổng: ' + (a + b));
}
getSum(5,6);
</script>

3.3. Hàm có tham số mặc định

Để gọi dạng hàm này chúng ta có thể sử dụng cách gọi hàm cơ bản nếu không muốn truyền tham số, và cách gọi hàm có tham số truyền vào nếu muốn truyền tham số cho hàm.

Ví dụ:

<script type="text/javascript">
function getSum(a = 1,b = 2){
document.write('Tổng: ' + (a + b));
}
getSum();
//tạo khoảng ngăn cách cho các bạn dễ nhì
document.write('<br>__________________________________<br>');
getSum(4,5);
</script>

3.4. Hàm có giá trị trả về và không

Hàm này cũng tương tự các hàm trên nên mình sẽ lấy ví dụ cụ thể thôi cho các bạn dễ hiểu nhé:

<script type="text/javascript">
function getSum(a = 1,b = 2){
return a + b;
}
var kq1 = getSum();
document.write(kq1);
//tạo khoảng ngăn cách cho các bạn dễ nhìn
document.write('<br>__________________________________<br>');
var kq2 = getSum(4,5);
document.write(kq2);
</script>

4. Các lưu ý về hàm trong JavaScript

4.1. Cách đặt tên hàm

Javascript cũng giống như các ngôn ngữ khác nó cũng có các ràng buộc về tên hàm sau đây:

  • Tên hàm phải được bắt đầu bằng chữ cái (a-z,A-Z) hoặc ký tự _.
  • Tên hàm không được bắt đầu bằng số, các ký tự khác ký tự _.

4.2. Gọi hàm

Các hàm phải đặt trong phạm vi (in scope) khi nó được gọi, nhưng việc khai báo hàm có thể được hoisted (câu lệnh khai báo hàm xuất hiện bên dưới dòng gọi hàm trong đoạn code), như ví dụ này:

console.log(getAreaOfSquare(5));
/* ... */
function getAreaOfSquare(n) {
return n*n;
}

Phạm vi (scope) của một hàm là khoảng không gian bên trong hàm mà nó được khai báo (hoặc là cả chương trình, nếu nó được khai bảo ở top level, tức là nó không nằm trong hàm nào khác).

Lưu ý

Điều này chỉ đúng khi định nghĩa một hàm bằng cách sử dụng các cú pháp ở trên (ví dụ function funcName(){}). Điều này có nghĩa rằng function hoisting chỉ hoạt động với cách khai báo hàm thông thường (function declarations) – function hoisting không hoạt động đối với hàm được khai báo bằng biểu thức hàm (function expression). Ví dụ, đoạn code dưới đây sẽ không hoạt động

console.log(getAreaOfSquare); // ReferenceError: getAreaOfSquare không được xác định
console.log(getAreaOfSquare(5)); // ReferenceError: getAreaOfSquare không được xác định
getAreaOfSquare = function (n) {
return n * n;
}

4.3. Phạm vi của hàm – Function Scope

Các biến được định nghĩa bên trong một hàm không thể được truy cập từ nơi nào khác bên ngoài hàm, bởi vì biến đó được định nghĩa chỉ trong phạm vi của hàm. Tuy nhiên, một hàm có thể truy cập đến mọi biến và mọi hàm khác trong cùng phạm vi mà nó được định nghĩa.

Nói cách khác, một hàm được định nghĩa trong phạm vi global có thể truy cập tới tất cả các biến đã được định nghĩa trong phạm vi global. Một hàm được định nghĩa bên trong một hàm khác có thể truy cập đến tất cả biến được định nghĩa bên trong hàm cha của nó, và bất cứ biến nào khác mà hàm cha của nó có quyền truy cập đến. Các bạn có thể tham khảo ví dụ dưới đây:

<script type="text/javascript">
// Các biến sau được định nghĩa trong phạm vi global scope
var num1 = 20,
num2 = 3,
name = "Khoa";
// Hàm này được định nghĩa trong phạm vi global scope
function multiply() {
return num1 * num2;
}
multiply(); // Returns 60
// Một ví dụ hàm lồng nhau
function getScore () {
var num1 = 50,
num2 = 50;
function add() {
return name + " scored " + (num1 + num2);
}
return add();
}
getScore(); // Returns "Khoa scored 100"
</script>

5. Sự phát sinh hàm trong chương trình

Khi viết chương trình, thông thường sẽ có 2 khả năng khiến một hàm được sinh ra:

  • Một đoạn chương trình được lặp đi, lặp lại nhiều lần. Việc gom chúng lại thành một hàm sẽ giúp chương trình ngắn gọn hơn, dễ hiểu hơn. Và khi cần thay đổi, bạn chỉ cần sửa một lần trong nội dung hàm, thay vì phải sửa nhiều chỗ.
  • Khi viết chương trình, bạn cần tách toàn bộ chương trình thành những phần nhỏ hơn. Hay còn gọi là module hoá. Lúc này, bạn có thể xây dựng logic cho toàn bộ chương trình trước khi thật sự định nghĩa nội dung hàm. Tại những công ty lớn, thường bạn sẽ chỉ được làm những module nhỏ trong toàn bộ một project lớn. Sau đó, team leader hay project leader… sẽ là người ghép những module nhỏ đó thành một chương trình hoàn chỉnh. Do đó, việc phân chia chương trình thành những hàm tốt là vô cùng quan trọng.

Vậy như thế nào là một hàm tốt? Đây là một câu hỏi rất khó để trả lời. Theo mình, hàm tốt là hàm thỏa mãn một số điều kiện sau:

  • Một hàm chỉ thực hiện một chức năng duy nhất
  • Độc lập với các yếu tố bên ngoài (biến toàn cục, môi trường thực thi,…)
  • Dễ dàng tái sử dụng

Tuy nhiên, không phải lúc nào bạn cũng có thể thoả mãn những yêu cầu trên. Hoặc không nhất thiết phải thoả mãn tất cả những yêu cầu trên.

6. Cách tham số của hàm trong JavaScript

6.1. Default parameters

Trong Js các tham số của hàm mặc định là undefined. Tuy nhiên, trong một số trường hợp nó có thể hữu ích để thiết lập một giá trị mặc định khác. Đây chính xác là điều mà default parameters sẽ làm.

  • Khi không có default parameters (trước ES6):

Trong quá khứ, cách thông thường để thiết lập các giá trị mặc định là kiểm định giá trị của các tham số bên trong body của function và gán giá trị cho nó nếu nó là undefined. Trong ví dụ sau, nếu không có giá trị nào được truyền cho b, giá trị của nó sẽ là undefined khi thực hiện tính toán a*b, và việc gọi hàm multiply sẽ trả về NaN. Tuy nhiên, điều này bị ngăn chặn bởi dòng thứ 2 trong ví dụ này:

function multiply(a, b) {
b = typeof b !== 'undefined' ? b : 1;
return a*b;
}
multiply(5); // 5
  • Khi có default parameters (sau ES6)

Với default parameters, việc kiểm tra thủ công bên trong body của function không còn cần thiết. Bạn có thể đơn giản chỉ là đặt 1 vào làm giá trị mặc định cho b ngay tại head của function:

function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5

Có một số trường hợp bạn muốn một tham số nào đó có thể được truyền hoặc không cần truyền vào đều được, lúc này chúng ta phải sử dụng nó như một tham số mặc định. Javascript không có cú pháp gán giá trị mặc định như PHP mà thay vào đó chúng ta sử dụng toán tử || .

Ví dụ: Hàm hiển thị một thông báo:

function showMessage(message)
{
// Nếu message không được truyền vào hoặc giá trị nó là rỗng
// thì sẽ được thay thế bằng giá trị 'Không có tin nhắn'
message = message || 'Không có tin nhắn <br/>';
document.write(message);
}
// Cách 1: không truyền tham số
showMessage();
// Cách 2: Truyền tham số
showMessage('Chào mừng bạn đến với laptrinhtudau.com');

Toán tử || gồm hai vế, trong đó nếu vế TRÁI có giá trị rỗng (undefined, null, false, …) kết quả sẽ trả về vế PHẢI, ngược lại nó sẽ trả về vế TRÁI.

6.2. Rest parameters

Cú pháp rest parameter cho phép chúng ta dùng 1 mảng để đại diện cho số lượng vô hạn các đối số. Trong ví dụ sau, hàm multiply sử dụng rest parameters để thu thập các đối số kể từ đối số hứ hai trở về đến hết. Hàm này sau đó sẽ nhân những đối số này với đối số đầu tiên.

Ví dụ:

function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]