Định nghĩa hàm Function trong JavaScript

Javascript nâng cao | by Học Javascript

Trong lập trình JavaScript, hàm (Function) là một phần quan trọng giúp tổ chức và quản lý mã nguồn một cách hiệu quả. Hàm cho phép chúng ta nhóm các dòng lệnh lại với nhau để thực hiện một nhiệm vụ cụ thể, giúp mã dễ đọc, dễ bảo trì và có thể tái sử dụng nhiều lần.

JavaScript cung cấp nhiều cách để định nghĩa hàm, từ cách khai báo truyền thống đến các phương pháp hiện đại như arrow function hay callback function. Ngoài ra, phạm vi biến trong hàm, cơ chế hoisting, và các phương thức xử lý hàm như call(), apply(), bind() cũng là những khía cạnh quan trọng giúp lập trình viên kiểm soát luồng thực thi trong chương trình.

Bài viết này sẽ giúp bạn hiểu rõ về khái niệm hàm trong JavaScript, cách khai báo, sử dụng, cũng như các tính năng nâng cao liên quan đến hàm.

Giới thiệu về Hàm (Function) trong JavaScript

Trong JavaScript, hàm (function) là một khối mã được thiết kế để thực hiện một nhiệm vụ cụ thể. Hàm giúp lập trình viên viết mã nguồn gọn gàng hơn, tránh trùng lặp, tăng tính tái sử dụng và dễ bảo trì.

Hàm là một tập hợp các câu lệnh thực hiện một chức năng cụ thể và có thể được gọi nhiều lần trong chương trình. Hàm có thể nhận tham số (parameters) làm đầu vào và có thể trả về giá trị (return value) làm kết quả đầu ra.

Ví dụ về một hàm đơn giản:

function sayHello() {
    console.log("Xin chào!");
}
sayHello(); // Gọi hàm và hiển thị "Xin chào!"

Tầm quan trọng của hàm trong lập trình

Giúp tái sử dụng mã nguồn (Code Reusability)

  • Khi một đoạn mã cần được sử dụng nhiều lần, thay vì viết lại, ta có thể đặt nó vào một hàm và gọi lại bất cứ khi nào cần.

Ví dụ, thay vì viết lệnh cộng hai số nhiều lần, ta có thể tạo một hàm như sau:

function add(a, b) {
    return a + b;
}
console.log(add(5, 10)); // Kết quả: 15
console.log(add(20, 30)); // Kết quả: 50

Giúp tổ chức mã tốt hơn (Code Organization)

  • Chia chương trình thành các phần nhỏ giúp dễ hiểu và dễ quản lý.

  • Ví dụ, thay vì viết tất cả mã vào một nơi, ta có thể chia thành các hàm nhỏ để xử lý từng nhiệm vụ cụ thể.

Dễ bảo trì (Maintainability)

  • Khi cần sửa đổi hoặc nâng cấp chương trình, chỉ cần chỉnh sửa trong hàm mà không ảnh hưởng đến toàn bộ mã nguồn.

  • Giả sử ta muốn thay đổi cách hiển thị lời chào, chỉ cần sửa trong hàm sayHello().

Giúp kiểm thử dễ dàng hơn (Testing & Debugging)

  • Hàm giúp kiểm tra từng phần riêng lẻ thay vì phải kiểm tra toàn bộ chương trình.

Tăng hiệu suất và tối ưu hóa chương trình

  • Sử dụng hàm giúp tối ưu bộ nhớ và tránh lặp lại công việc không cần thiết.

Nhờ những ưu điểm này, hàm đóng vai trò quan trọng trong lập trình JavaScript, giúp code trở nên rõ ràng, dễ đọc và dễ bảo trì hơn.

Định nghĩa hàm trong JavaScript

Trong JavaScript, hàm (function) là một khối mã được thiết kế để thực hiện một nhiệm vụ cụ thể. Hàm có thể được gọi nhiều lần mà không cần viết lại mã, giúp tăng tính tái sử dụng, tổ chức mã tốt hơndễ bảo trì.

Cấu trúc cơ bản của một hàm trong JavaScript

Một hàm trong JavaScript có thể có:

  • Tên hàm (optional, tùy thuộc vào cách khai báo).

  • Tham số (parameters): Dữ liệu đầu vào truyền vào hàm.

  • Khối lệnh: Tập hợp các câu lệnh thực thi khi hàm được gọi.

  • Giá trị trả về (return value): Giá trị kết quả của hàm (nếu có).

Ví dụ về một hàm cơ bản:

function greet(name) { 
    return `Xin chào, ${name}!`; 
}
console.log(greet("John")); // Output: Xin chào, John!

Các cách khai báo hàm trong JavaScript

JavaScript cung cấp nhiều cách để khai báo hàm, mỗi cách có đặc điểm riêng.

Hàm khai báo (Function Declaration)

Cú pháp:

function tenHam(thamSo1, thamSo2) {
    // Khối lệnh
    return giaTri;
}

Đặc điểm:

  • Có thể gọi trước khi khai báo do hoisting (cơ chế kéo lên đầu).

  • Cấu trúc dễ đọc, phù hợp cho các hàm cần sử dụng nhiều lần.

Ví dụ:

console.log(sum(5, 10)); // Gọi trước khi khai báo vẫn hoạt động (hoisting)

function sum(a, b) {
    return a + b;
}

Hàm biểu thức (Function Expression)

Cú pháp:

const tenHam = function(thamSo1, thamSo2) {
    // Khối lệnh
    return giaTri;
};

Đặc điểm:

  • Không thể gọi trước khi khai báo (do không được hoisting như Function Declaration).

  • Được lưu trữ trong một biến, có thể sử dụng như một giá trị.

  • Thường dùng trong callback hoặc truyền vào các hàm khác.

Ví dụ:

console.log(multiply(5, 2)); // Lỗi: Cannot access 'multiply' before initialization

const multiply = function(a, b) {
    return a * b;
};

console.log(multiply(5, 2)); // Output: 10

Hàm mũi tên (Arrow Function - ES6)

Cú pháp:

const tenHam = (thamSo1, thamSo2) => {
    // Khối lệnh
    return giaTri;
};

Đặc điểm:

  • Ngắn gọn hơn so với Function Expression.

  • Không có this riêngthis sẽ lấy từ phạm vi bên ngoài (lexical this).

  • Không thể sử dụng làm constructor function.

Ví dụ:

const add = (a, b) => a + b; // Viết ngắn gọn khi có một câu lệnh return
console.log(add(3, 7)); // Output: 10

Hàm ẩn danh (Anonymous Function)

Cú pháp:

const functionExample = function() {
    console.log("Hàm ẩn danh!");
};

Đặc điểm:

  • Hàm không có tên.

  • Thường được sử dụng trong callback hoặc các hàm xử lý sự kiện.

Ví dụ sử dụng trong setTimeout:

setTimeout(function() {
    console.log("Hàm ẩn danh được thực thi sau 2 giây!");
}, 2000);

Hàm tức thời (Immediately Invoked Function Expression - IIFE)

Cú pháp:

(function() {
    console.log("Hàm IIFE được gọi ngay sau khi khai báo!");
})();

Đặc điểm:

  • Được gọi ngay lập tức sau khi khai báo.

  • Thường được sử dụng để tránh ô nhiễm phạm vi toàn cục.

Ví dụ:

(function() {
    let secret = "Đây là một biến private!";
    console.log(secret); // Output: Đây là một biến private!
})();

// console.log(secret); // Lỗi: secret is not defined

Tham số và giá trị trả về trong hàm trong JavaScript

Tham số trong hàm (Function Parameters)

  • Một hàm trong JavaScript có thể nhận một hoặc nhiều tham số khi được gọi.

  • Các tham số được sử dụng để truyền dữ liệu vào hàm để thực hiện các tác vụ cụ thể.

Ví dụ về hàm có tham số:

function greet(name) {
    return `Xin chào, ${name}!`;
}
console.log(greet("John")); // Output: Xin chào, John!

Giá trị mặc định cho tham số

  • JavaScript cho phép gán giá trị mặc định cho tham số trong trường hợp không có giá trị truyền vào.

  • Điều này giúp tránh lỗi khi gọi hàm mà không cung cấp đầy đủ tham số.

Ví dụ về tham số có giá trị mặc định:

function greet(name = "User") {
    return `Xin chào, ${name}!`;
}
console.log(greet()); // Output: Xin chào, User!
console.log(greet("Alice")); // Output: Xin chào, Alice!

Toán tử Rest (...args) để nhận nhiều tham số

  • Sử dụng toán tử ... (rest parameter) để gom nhiều đối số vào một mảng.

  • Hữu ích khi không biết trước số lượng tham số truyền vào.

Ví dụ về toán tử rest:

function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // Output: 10
console.log(sum(5, 10, 15)); // Output: 30

Giá trị trả về từ hàm (Return Value)

  • Hàm có thể sử dụng từ khóa return để trả về giá trị kết quả.

  • Nếu không có return, hàm sẽ trả về undefined.

Ví dụ về hàm có return:

function multiply(a, b) {
    return a * b;
}
console.log(multiply(5, 3)); // Output: 15

Ví dụ về hàm không có return:

function sayHello() {
    console.log("Hello, world!");
}
console.log(sayHello()); // Output: Hello, world! + undefined

Phạm vi (Scope) và Hoisting hàm trong JavaScript

Phạm vi của biến trong hàm (Scope in JavaScript)

Phạm vi (Scope) quyết định nơi một biến có thể được truy cập trong chương trình.

Phạm vi toàn cục (Global Scope)

  • Biến được khai báo ngoài tất cả các hàm sẽ có phạm vi toàn cục.

  • Có thể được truy cập từ bất kỳ đâu trong chương trình.

Ví dụ:

let globalVar = "Tôi là biến toàn cục";

function showGlobalVar() {
    console.log(globalVar); // Có thể truy cập
}
showGlobalVar(); // Output: Tôi là biến toàn cục
console.log(globalVar); // Output: Tôi là biến toàn cục

hạm vi cục bộ (Local Scope)

  • Biến khai báo bên trong một hàm chỉ có thể truy cập trong hàm đó.

  • Không thể truy cập từ bên ngoài hàm.

Ví dụ:

function myFunction() {
    let localVar = "Tôi là biến cục bộ";
    console.log(localVar); // Output: Tôi là biến cục bộ
}
myFunction();

// console.log(localVar); // Lỗi: localVar is not defined

Closure trong JavaScript

  • Closure là một hàm có thể truy cập biến của phạm vi cha ngay cả khi phạm vi cha đã kết thúc.

Ví dụ về Closure:

function outerFunction(outerValue) {
    return function innerFunction(innerValue) {
        return `Outer: ${outerValue}, Inner: ${innerValue}`;
    };
}

const myClosure = outerFunction("Hello");
console.log(myClosure("World")); // Output: Outer: Hello, Inner: World

Ở ví dụ trên:

  • outerFunction trả về innerFunction.

  • innerFunction vẫn có thể truy cập outerValue ngay cả khi outerFunction đã kết thúc.

Hoisting trong hàm

Hàm khai báo (Function Declaration) được hoisting

  • Hàm được định nghĩa bằng Function Declaration sẽ được hoisted lên trên, có thể gọi trước khi khai báo.

Ví dụ:

console.log(hello()); // Output: Xin chào!

function hello() {
    return "Xin chào!";
}

Hàm biểu thức (Function Expression) không được hoisting

  • Hàm được gán vào biến không được hoisted → Gây lỗi nếu gọi trước khi khai báo.

Ví dụ:

console.log(sayHi()); // Lỗi: Cannot access 'sayHi' before initialization

const sayHi = function() {
    return "Chào bạn!";
};
Khái niệm Mô tả Ví dụ
Tham số trong hàm Truyền giá trị vào hàm khi gọi function greet(name) { return "Hello " + name; }
Giá trị mặc định Nếu không truyền giá trị, sử dụng giá trị mặc định function greet(name = "User") { ... }
Toán tử Rest (...args) Gom nhiều tham số vào mảng function sum(...nums) { return nums.reduce(...) }
Giá trị trả về (return) Kết quả của hàm return a + b;
Phạm vi toàn cục Biến có thể truy cập từ mọi nơi let globalVar = "Hello";
Phạm vi cục bộ Biến chỉ tồn tại trong hàm function myFunc() { let localVar = "Hi"; }
Closure Hàm lồng nhau có thể truy cập biến cha function outer() { return function inner() { ... } }
Hoisting - Function Declaration Có thể gọi trước khi khai báo function hello() { return "Hi"; }
Hoisting - Function Expression Không thể gọi trước khi khai báo const hello = function() { return "Hi"; };

Callback Function và Higher-Order Function trong JavaScript

Callback Function

Định nghĩa Callback Function

  • Callback Function là một hàm được truyền vào một hàm khác làm đối số và được thực thi bên trong hàm đó.

  • Callback giúp xử lý bất đồng bộ, tái sử dụng mã và làm cho code linh hoạt hơn.

Ví dụ đơn giản về Callback Function:

function greet(name, callback) {
    console.log("Xin chào, " + name);
    callback();
}

function sayGoodbye() {
    console.log("Tạm biệt!");
}

greet("Alice", sayGoodbye);
// Output:
// Xin chào, Alice
// Tạm biệt!

Callback Function trong Xử lý Bất đồng bộ (Async Callback)

  • Trong JavaScript, các tác vụ như đọc file, gọi API, xử lý sự kiện là bất đồng bộ.

  • Callback giúp đảm bảo hàm chỉ thực hiện sau khi tác vụ hoàn thành.

Ví dụ: Callback trong setTimeout()

console.log("Bắt đầu");

setTimeout(() => {
    console.log("Đây là một Callback Function");
}, 2000);

console.log("Kết thúc");
// Output:
// Bắt đầu
// Kết thúc
// (Sau 2 giây) Đây là một Callback Function

Higher-Order Function

Định nghĩa Higher-Order Function

Higher-Order Function (HOF) là hàm:

  • Nhận một hoặc nhiều hàm làm tham số.

  • Trả về một hàm khác như kết quả.

HOF giúp viết code ngắn gọn, linh hoạt và dễ bảo trì hơn.

Ví dụ: Hàm nhận một hàm khác làm tham số

function operate(a, b, callback) {
    return callback(a, b);
}

function add(x, y) {
    return x + y;
}

console.log(operate(5, 3, add)); // Output: 8

Các Higher-Order Function phổ biến trong JavaScript

map(): Biến đổi từng phần tử của mảng thành giá trị mới

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // Output: [2, 4, 6, 8, 10]

filter(): Lọc các phần tử thỏa mãn điều kiện

const scores = [45, 80, 90, 60, 75];
const passed = scores.filter(score => score >= 60);
console.log(passed); // Output: [80, 90, 60, 75]

reduce(): Tính tổng hoặc gộp mảng thành một giá trị duy nhất

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // Output: 15

Các phương thức hữu ích liên quan đến hàm trong JavaScript

call(), apply(), bind() – Thay đổi this khi gọi hàm

  • Trong JavaScript, this có thể bị thay đổi tùy thuộc vào cách gọi hàm.

  • call(), apply(), bind() giúp kiểm soát giá trị của this khi gọi hàm.

Ví dụ về call()

const person = {
    name: "John",
    greet: function() {
        console.log("Hello, " + this.name);
    }
};

const anotherPerson = { name: "Alice" };
person.greet.call(anotherPerson); // Output: Hello, Alice

Ví dụ về apply() (giống call(), nhưng tham số là mảng)

function introduce(age, city) {
    console.log(`Tôi là ${this.name}, ${age} tuổi, đến từ ${city}`);
}

const person = { name: "David" };
introduce.apply(person, [25, "Hà Nội"]);
// Output: Tôi là David, 25 tuổi, đến từ Hà Nội

setTimeout() và setInterval() – Hẹn giờ thực thi hàm

setTimeout(): Thực hiện hàm sau một khoảng thời gian

setTimeout(() => {
    console.log("Hàm này chạy sau 3 giây");
}, 3000);
setInterval(): Lặp lại hàm sau một khoảng thời gian
let count = 0;
const interval = setInterval(() => {
    count++;
    console.log(`Lần thứ ${count}`);
    if (count === 5) clearInterval(interval); // Dừng sau 5 lần
}, 1000);
Khái niệm Mô tả Ví dụ
Callback Function Hàm được truyền làm tham số vào hàm khác setTimeout(() => console.log("Hello"), 2000);
Higher-Order Function Hàm nhận hoặc trả về một hàm khác arr.map(num => num * 2);
map() Biến đổi từng phần tử trong mảng [1,2,3].map(x => x * 2);
filter() Lọc phần tử thỏa mãn điều kiện [1,2,3,4].filter(x => x > 2);
reduce() Gộp mảng thành một giá trị duy nhất [1,2,3].reduce((a,b) => a+b, 0);
call() Gọi hàm và thay đổi this greet.call(obj);
apply() Giống call(), nhưng truyền tham số dưới dạng mảng greet.apply(obj, [param1, param2]);
bind() Tạo một hàm mới với this cố định const newFunc = greet.bind(obj);
setTimeout() Chạy hàm sau một khoảng thời gian setTimeout(() => console.log("Hello"), 2000);
setInterval() Gọi hàm lặp lại sau mỗi khoảng thời gian setInterval(() => console.log("Tick"), 1000);

Kết bài

Hàm (Function) là một phần cốt lõi trong JavaScript, giúp tổ chức mã một cách logic, tái sử dụng và tối ưu hiệu suất. Bằng cách hiểu rõ các loại hàm như Hàm khai báo, Hàm biểu thức, Hàm mũi tên, cũng như các khái niệm nâng cao như Callback Function, Higher-Order Function, bạn có thể viết code ngắn gọn, dễ đọc và dễ bảo trì hơn.

Ngoài ra, các phương thức hữu ích như call(), apply(), bind() giúp kiểm soát this linh hoạt, trong khi setTimeout(), setInterval() hỗ trợ xử lý các tác vụ theo thời gian.

Việc nắm vững và áp dụng hiệu quả hàm trong JavaScript sẽ giúp bạn phát triển các ứng dụng mạnh mẽ, tối ưu và chuyên nghiệp hơn. Hãy tiếp tục thực hành để nâng cao kỹ năng lập trình của mình!

Bài viết liên quan