Recursion (Đệ Quy) trong JavaScript

Ngày đăng: 15/04/2021   -    Cập nhật: 17/04/2021
Trong hướng dẫn này, mình sẽ hướng dẫn bạn sử dụng kỹ thuật Đệ quy (Recursion) để phát triển một hàm đệ quy (Recursive Function) trong JavaScript.


Recursive trong JavaScript (Recursion Function)


Về cơ bản, Hàm đệ quy là các hàm tự gọi lại chính nó.


Giới thiệu về các hàm đệ quy JavaScript



Một hàm đệ quy là một hàm gọi chính nó cho đến khi không thể gọi. Kỹ thuật này được gọi là Đệ quy (Recursion).


Giả sử, bạn có một hàm được gọi là recurse(). Hàm recurse() này sẽ là một hàm đệ quy (Recursive Function) nếu nó gọi chính nó bên trong phần thân của nó, như thế này:




// Hàm đệ quy
function recurse() {
    // ...

    // Tự gọi lại chính nó
    recurse();
    // ...
}
 


Tuy nhiên, phải lưu ý là, một hàm đệ quy luôn phải có điều kiện ngừng gọi chính nó, nếu không, nó sẽ tự gọi lại vô hạn.


Vì thế, một hàm đệ quy thường có thêm điều kiện ngừng trông giống như sau:




// Hàm đệ quy
function recurse() {

    // Kiểm tra điều kiện
    // Đúng: Ngừng gọi lại chính nó
    // Sai: Gọi lại chính nó
    if(condition) {
        // Code ngừng gọi lại
    } else {
        recurse();
    }
}
 


Nói chung, các hàm đệ quy được sử dụng để chia một vấn đề lớn thành những vấn đề nhỏ hơn.


Bạn sẽ thường thấy nó trong các cấu trúc dữ liệu như cây nhị phân, đồ thị và các thuật toán như tìm kiếm nhị phân (binary serarch) và thuật toán sắp xếp nhanh (quicksort).



Ví dụ về Hàm Đệ quy


Hãy cùng làm một vài ví dụ để tìm hiểu về cách sử dụng hàm đệ quy trong JavaScript


VD1: Hàm đệ quy đơn giản



Giả sử rằng bạn cần tạo ra một hàm đếm ngược từ một số được chỉ định đến 1. Ví dụ: Đếm ngược từ 5 đến 1


Đầu tiên, ta tạo hàm countDown() như sau:




function countDown(fromNumber) {
    console.log(fromNumber);
}

countDown(5);
 


Nhưng như thế thì hàm countDown() này luôn chỉ hiển thị kết quả là 5.


Và để đếm ngược từ 5 đến 1 bạn cần phải:



  • Gọi countDown(5) và log ra 5
  • Gọi countDown(4) và log ra 4
  • Gọi countDown(3) và log ra 3
  • Gọi countDown(2) và log ra 2
  • Gọi countDown(1) và log ra 1


Ok, chúng ta tiến thêm một bước nữa, sửa đoạn code trên thành như sau:



function countDown(fromNumber) {

    console.log(fromNumber);

    // Gọi lại hàm countDown
    // với fromNumber đi 1
    countDown(fromNumber-1);
}

countDown(5);
 


Chạy chương trình trên ta được kết quả:


...
Uncaught RangeError: Maximun call stack size exceeded.
 


Hàm vẫn chạy đếm ngược, nhưng nó không dừng lại, dẫn đến lỗi.


Vì thế chúng ta phải thêm điều kiện để dừng lại khi số tiếp theo là 0:



function countDown(fromNumber) {
    console.log(fromNumber);

    // Tính số tiếp theo
    let nextNumber = fromNumber - 1;
    
    // Kiểm tra số tiếp theo
    // Lớn hơn 0 thì in nó ra console
    if (nextNumber > 0) {
        countDown(nextNumber);
    }
}

countDown(5);
 


Kết quả chạy lại chương trình này ta được:



5
4
3
2
1
 


Đến đây, countDown() có vẻ như là đã hoạt động đúng với ý định của chúng ta.


Tuy nhiên, tên của hàm là một tham chiếu đến đối tượng hàm thực.


Nếu ở đâu đó trong code, tên hàm được đặt thành null, thì hàm đệ quy sẽ ngừng hoạt động.


Ví dụ, đoạn code sau sẽ dẫn đến lỗi:




let newYearCountDown = countDown;

// Ở đâu đó trong code
// ai đó đặt countDown thành null
countDown = null;

// Thế nên khi gọi hàm sẽ gây ra lỗi
newYearCountDown(10);
 


Lỗi:



Uncaught TypeError: countDown is not a function
 


Giải thích:


  • Đầu tiên, gán tên hàm countDown cho biến newYearCountDown.
  • Thứ hai, đặt tham chiếu hàm countDown thành null.
  • Thứ ba, gọi hàm newYearCountDown.


Mã gây ra lỗi vì phần thân của hàm countDown() tham chiếu đến tên hàm countDown đã được đặt thành null tại thời điểm gọi hàm.


Để khắc phục, bạn có thể sử dụng một biểu thức hàm được đặt tên như sau:




// Hàm đệ quy
let countDown = function f(fromNumber) {
    console.log(fromNumber);

    // Tính số tiếp theo
    let nextNumber = fromNumber - 1;
    
    // Kiểm tra số tiếp theo
    // Lớn hơn 0 thì in nó ra console
    if (nextNumber > 0) {
        f(nextNumber);
    }
}

let newYearCountDown = countDown;

// Cố tình (hay vô ý) đặt countDown thành null
countDown = null;

// Gọi hàm đệ quy đếm ngược chào năm mới từ 5
newYearCountDown(5);
 


Kết quả khi chạy chương trình trên ta được:



5
4
3
2
1
 


VD2: Tính tổng các chữ số từ một số



Bài toàn đặt ra: Đưa cho một số (VD: 456), tính tổng từ các chữ số trong số đó như: 4 + 5 + 6 = 15


Để áp dụng kỹ thuật đệ quy, chúng ta phân tích (tách ra và tính %10):



  • 456 bằng 450 + 6
  • 450 % 10 bằng 45 => Tương đương với 40 + 5
  • 40 % 10 bằng 4 => Tương đương với 4 + 0


Tương đương với:


  • Lần 1: f(456) = 6 + f(45)
  • Lần 2: f(456) = 6 + 5 + f(4)
  • Lần 3: f(456) = 6 + 5 + 6


Ta sử dụng giải pháp sau:


  • Đầu tiên, số phải khác 0. Nếu bằng 0 thì ngừng gọi hàm
  • Tìm số cuối cùng bằng phép chia lấy phần dư cho 10, ví dụ: 456 % 10 = 6
  • Sử dụng phép tính chia cho 10 và làm tròn để tìm phần đầu, ví dụ: 456 / 10 = 45.6. Ta sử dụng làm tròn số: Math.floor(num / 10) ta được 45


Code:



function sumOfDigits(num) {
    if (num == 0) {
        return 0;
    }
    return num % 10 + sumOfDigits(Math.floor(num / 10));
}
 


Bây giờ, chạy thử chương trình:



console.log(sumOfDigits(456));
 


Kết quả:



15
 


VD3: Sử dụng Recursion để làm phẳng mảng lồng nhau trong JS



Ta có một mảng lồng nhau như thế này:



let x = [123, [456, [78]], 9];
 


Ý tưởng:


  • Lặp qua từng phần tử trong mảng. Nếu phần tử đó không phải là mảng thì thêm nó vào cuối một mảng mới
  • Nếu là một mảng thì tiếp tục lặp qua từng phần tử trong đó và lại kiểm tra xem có phải là mảng không. Nếu không thì thêm nó vào cuối mảng mới (đã tạo trước đó)


Code:



function flatten() {
    var flat = [];
    for (var i = 0i < arguments.lengthi++) {
        if (arguments[iinstanceof Array) {
            flat.push.apply(flatflatten.apply(thisarguments[i]));
        } else {
            flat.push(arguments[i]);
        }
    }
    return flat;
}
 


> Lưu ý: Sử dụng flatten.apply(this, arguments[i]) để truyền phần tử của mảng (chứ không phải là mảng gốc) (Xem thêm: hàm apply() trong JS)


Kết quả chạy chương trình làm phẳng mảng lồng nhau:



console.log(flatten(x));
 


Ta được:



[1, 2, 3, 4, 5, 6, 7, 8, 9]
 


Để tối ưu hơn, chúng ta có thể sử dụng kết hợp hàm reduce():



// Sử dụng đệ quy để làm phẳng mảng lồng nhau
function flatten(items) {
    const flat = [];

    items.forEach(item => {
        if (Array.isArray(item)) {
            flat.push(...flatten(item));
        } else {
            flat.push(item);
        }
    });

    return flat;
}
 


Tổng kết về Recursion trong JavaScript



Như vậy, trong bài viết này bạn đã được tìm hiểu về Recursion trong JavaScript thông qua một số ví dụ cụ thể.


> Nếu bạn yêu thích JavaScript và muốn đi chuyên sâu. Hãy tham gia ngay KHÓA HỌC FRONT END với sự hướng dẫn của chuyên gia doanh nghiệp để học nhanh hơn, chuẩn hơn, đi thực tập / đi làm sớm hơn.



---
HỌC VIỆN ĐÀO TẠO CNTT NIIT - ICT HÀ NỘI
Học Lập trình chất lượng cao (Since 2002). Học thực tế + Tuyển dụng ngay!
Đc: Tầng 3, 25T2, N05, Nguyễn Thị Thập, Cầu Giấy, Hà Nội
SĐT: 02435574074 - 0383.180086
Email: hello@niithanoi.edu.vn
Fanpage: https://facebook.com/NIIT.ICT/
 
#niit #icthanoi #niithanoi #niiticthanoi #hoclaptrinh #khoahoclaptrinh #hoclaptrinhjava #hoclaptrinhphp
Bình luận Facebook
Khóa học liên quan đến bài viết

KHÓA HỌC LẬP TRÌNH FRONT END VỚI REACT.JS

56 giờ
Học Lập trình Front end hiện đại với ReactJS. Học làm chủ HTML, CSS, JS và thư viện JavaScript phổ biến nhất hiện nay. Sẵn sàng đi thực tập / đi làm ngay sau khóa học.

Khóa học PHP Full stack [2023] cho người mới bắt đầu

96 giờ
Khóa học Lập trình PHP Full stack, phiên bản cập nhật lần thứ 8. Dạy Lập trình PHP bài bản từ Front end đến Back end + Laravel. Hướng dẫn làm 2 Dự Án Web lớn

KHÓA HỌC PYTHON HƯỚNG ĐỐI TƯỢNG

50 giờ
Khóa học giúp học viên sử dụng thành thạo ngôn ngữ Lập trình Python (3x). Hiểu và phát triển được Ứng dụng Web với Django Framework. Học thực hành với Giảng viên cao cấp.

Khóa học Java Full stack (IJFD)

104 giờ
Học lập trình Java Fullstack với khóa học được xây dựng theo lộ trình bài bản, từ JAVA CƠ BẢN đến JAVA WEB và nâng cao về JAVA FRAMEWORK như: Spring Boot, Hibernate
Mục lục
Đăng ký tư vấn
Nhân viên gọi điện tư vấn miễn phí sau khi đăng ký
Được cập nhật các ưu đãi sớm nhất
Hotline: 0383180086
Tên không được để trống
Số điện thoại không được để trống
Email không được để trống
Hãy đăng ký để nhận những thông tin mới nhất về học bổng mới nhất tại NIIT - ICT Hà Nội
top
Đóng lại Đăng ký học tại NIIT - ICT Hà Nội
6260+ học viên đã theo học tại NIIT - ICT Hà Nội và có việc làm tốt trong ngành lập trình. Nắm lấy cơ hội ngay hôm nay!
Chọn khóa học
  • KHÓA HỌC LẬP TRÌNH FRONT END VỚI REACT.JS
  • KHÓA HỌC LẬP TRÌNH PHP WEB
  • Khóa học PHP Full stack [2023] cho người mới bắt đầu
  • Khóa học BIG DATA với Hadoop và Spark
  • Khóa học Lập trình Android tại Hà Nội
  • [Tuyển sinh 2023] Lập trình viên Quốc tế DigiNxt
  • Khóa học Tiền lương & Phúc lợi (C&B Excel) tại Hà Nội
  • LẬP TRÌNH GAME
    • Khóa học Lập trình Game Unity
  • LẬP TRÌNH WEB FRONT END
    • KHÓA HỌC PYTHON HƯỚNG ĐỐI TƯỢNG
    • KHÓA HỌC ANGULAR & TYPESCRIPT (FRONT END)
  • LẬP TRÌNH WEB BACK END
    • LẬP TRÌNH JAVA WEB VỚI FRAME WORK
    • Lập trình Web với Django
    • Lập trình PHP với Laravel Framework
  • CHƯƠNG TRÌNH ĐÀO TẠO ỨNG DỤNG CÔNG NGHỆ
    • Khóa học Tiền lương & Phúc lợi (C&B Excel) tại TP HCM
  • LẬP TRÌNH WEB FULL STACK
    • Khóa học Java Full stack (IJFD)
  • LẬP TRÌNH MOBILE
    • FRONT-END VỚI REACTJS VÀ REACT NATIVE
    • Lập trình Android Nâng cao
  • ĐÀO TẠO CHO DOANH NGHIỆP
    • KHÓA HỌC BUSINESS ANALYSIC TỪ CƠ BẢN ĐẾN NÂNG CAO 2023
    • Khóa học Magento: Làm chủ CMS TMĐT lớn nhất
    • Khóa học IOT: Xây dựng Sản phẩm IOT với Raspberry Pi
    • Khóa học Automation Testing Chuyên nghiệp
  • KHÓA HỌC DỰ ÁN
    • Học sử dụng bộ Office: Word, Excel, Power Point, Mail chuyên nghiệp
  • KHÓA HỌC KHÁC
    • VBA Excel Toàn Tập (Cơ Bản - Nâng Cao)
    • VBA Excel Nâng cao
    • Khóa học JMeter: Performance Testing
    • Khóa học Tester đạt chuẩn Quốc tế ISTQB Foundation Level
    • Khoá Học Tester đạt chuẩn quốc tế ISTQB Advanced Level
Bạn chưa chọn khóa học cần đăng ký
Tên không được để trống
Số điện thoại không được để trống
Email không được để trống
Đăng ký học thành công!
Cảm ơn bạn đã đăng ký học tại NIIT - ICT HÀ NỘI!