Hướng dẫn javascript function without return - hàm javascript không có trả về

Tất nhiên bạn có thể. Tất cả các câu trả lời khác đều nói về:

Show
  1. Gán một giá trị cho một biến trong phạm vi cha mẹ.
  2. Gán một giá trị cho một thuộc tính của một đối tượng trong phạm vi cha mẹ.

Tuy nhiên, có một phương pháp thứ ba và phương pháp này rất mạnh mẽ: tiếp tục.CONTINUATIONS.

Trong một khoảnh khắc, hãy giả sử rằng

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
0 là một chức năng. Ví dụ:

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

Bây giờ nếu bạn không muốn sử dụng

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
0 thì sao? Chúng ta có thể làm điều gì đó như thế này:

function add(a, b, ret) {
    ret(a);
}

Nói một cách đơn giản là một chức năng được sử dụng thay cho câu lệnh trả lại. Chính thức hơn:

Tiếp tục là một hàm được gọi bởi một chức năng khác. Điều cuối cùng chức năng khác làm là gọi tiếp tục. Nó không làm gì khác sau đó.

Do đó, việc tiếp tục giống như tương đương chức năng của câu lệnh

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
0.

Tiếp tục có rất nhiều lợi thế:

  1. Bạn có thể chuyển nhiều giá trị trả về cho tiếp tục.
  2. Bạn có thể viết các chức năng đệ quy thay vì lặp đi lặp lại.
  3. Bạn có thể chọn không gọi tiếp tục (trong trường hợp đó bạn đang thay đổi luồng điều khiển).
  4. Bạn có thể chọn gọi một trong nhiều lần tiếp tục (như tiếp tục
    Function.prototype.async = function () {
        return setTimeout.bind(null, this, 0).apply(null, arguments);
    };
    
    function fib(n, k) {
        switch (n) {
        case 0:  k(0); break;
        case 1:  k(1); break;
        default:
            fib.async(n - 1, function (x) {
                fib.async(n - 2, function (y) {
                    k(x + y);
                });
            });
        }
    }
    
    fib(20, console.log); // 6765
    
    3).

Tuy nhiên, các tiếp tục có hai nhược điểm chính:

  1. Nếu bạn viết các chương trình theo phong cách tiếp tục thì bạn sẽ kết thúc với rất nhiều chức năng lồng nhau.
  2. JavaScript không loại bỏ cuộc gọi đuôi. Do đó các chức năng đệ quy tăng kích thước ngăn xếp.

Ví dụ:

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded

Bạn có thể sử dụng

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
4 để loại bỏ đệ quy cuộc gọi đuôi:

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765

Bạn có thể sử dụng backcalls từ Livescript để làm cho mã của bạn trông đẹp hơn:

Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765

Bạn có thể tự mình thử trên trang web http://livescript.net/. Chỉ cần sử dụng một giá trị nhỏ hơn

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
5. Tốt nhất là
Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
6.

Các hàm (Function) là một trong những nền tảng cơ bản trong Javascript. Vậy bạn đã nắm hết những kiến thức về Function trong Javascript chưa?

Function(Hàm) là gì?

Function (hàm, chức năng), 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ó. (hàm, chức năng), 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ị. 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).

1. Khai báo hàm

Để khai báo hàm, chúng ta dùng từ khóa

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
7 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 
    Function.prototype.async = function () {
        return setTimeout.bind(null, this, 0).apply(null, arguments);
    };
    
    function fib(n, k) {
        switch (n) {
        case 0:  k(0); break;
        case 1:  k(1); break;
        default:
            fib.async(n - 1, function (x) {
                fib.async(n - 2, function (y) {
                    k(x + y);
                });
            });
        }
    }
    
    fib(20, console.log); // 6765
    
    8.

Ví dụ, để định nghĩa một hàm in ra chữ "Hello World" ở console:

function sayHello () {
  console.log("Hello World");
}

2. Biểu thức hàm (Hàm trong biến)

Trong khi việc khai báo hàm ở trên là một câu lệnh về mặt cú pháp, các hàm cũng có thể tạo ra bằng một biểu thức hàm (function expression). Một hàm như vậy có thể nặc danh; nó không cần phải có tên. Ví dụ, hàm sayHello ở trên có thể được khai báo như sau:function expression). Một hàm như vậy có thể nặc danh; nó không cần phải có tên. Ví dụ, hàm sayHello ở trên có thể được khai báo như sau:

const sayHello = function() {
     console.log("Hello World");
}

Tuy nhiên, một cái tên có thể được cung cấp trong một biểu thức hàm. Việc cung cấp tên cho phép hàm có thể chạy chính nó, hoặc có thể sử dụng hệ thống debug để nhận dạng hàm trong stack traces.có thể được cung cấp trong một biểu thức hàm. Việc cung cấp tên cho phép hàm có thể chạy chính nó, hoặc có thể sử dụng hệ thống debug để nhận dạng hàm trong stack traces.

const hello = function sayHello() {
     console.log("Hello World");
}

3. Các ràng buộc về 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ự 
    Function.prototype.async = function () {
        return setTimeout.bind(null, this, 0).apply(null, arguments);
    };
    
    function fib(n, k) {
        switch (n) {
        case 0:  k(0); break;
        case 1:  k(1); break;
        default:
            fib.async(n - 1, function (x) {
                fib.async(n - 2, function (y) {
                    k(x + y);
                });
            });
        }
    }
    
    fib(20, console.log); // 6765
    
    9. 
    Function.prototype.async = function () {
        return setTimeout.bind(null, this, 0).apply(null, arguments);
    };
    
    function fib(n, k) {
        switch (n) {
        case 0:  k(0); break;
        case 1:  k(1); break;
        default:
            fib.async(n - 1, function (x) {
                fib.async(n - 2, function (y) {
                    k(x + y);
                });
            });
        }
    }
    
    fib(20, console.log); // 6765
    
    9.
  • Tên hàm không được bắt đầu bằng số, các ký tự khác ký tự 
    Function.prototype.async = function () {
        return setTimeout.bind(null, this, 0).apply(null, arguments);
    };
    
    function fib(n, k) {
        switch (n) {
        case 0:  k(0); break;
        case 1:  k(1); break;
        default:
            fib.async(n - 1, function (x) {
                fib.async(n - 2, function (y) {
                    k(x + y);
                });
            });
        }
    }
    
    fib(20, console.log); // 6765
    
    9.
    Function.prototype.async = function () {
        return setTimeout.bind(null, this, 0).apply(null, arguments);
    };
    
    function fib(n, k) {
        switch (n) {
        case 0:  k(0); break;
        case 1:  k(1); break;
        default:
            fib.async(n - 1, function (x) {
                fib.async(n - 2, function (y) {
                    k(x + y);
                });
            });
        }
    }
    
    fib(20, console.log); // 6765
    
    9.

Các loại hàm

1. Hàm cơ bản

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

function doSomeThing() {
     // Do Something
}

Trong đó: doSomeThing là tên của hàm bạn muốn đặt và

Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
1 là từ khóa bắt buộc.

Ví dụ: Tạo hàm in ra tên website codelearn ở console

function getWebsite() {
     console.log("https://codelearn.io/");
}

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, cú pháp có dạng như sau:

function add(a, b, ret) {
    ret(a);
}
0

Trong đó:

  • funName là tên của hàm các bạn muốn đặt.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). 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).

VD: Tạo hàm tính tổng 2 số Tạo hàm tính tổng 2 số

function add(a, b, ret) {
    ret(a);
}
1

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:

function add(a, b, ret) {
    ret(a);
}
2

Trong đó::

  • funName là tên của hàm các bạn muốn đặt.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). 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).
  • VD: Tạo hàm tính tổng 2 số là các giá trị tương ứng với các pram.

3. Hàm có tham số mặc định: với hàm getSum ở trên mình sẽ xét tham số mặc định cho nó.

function add(a, b, ret) {
    ret(a);
}
3

Đâ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:

value_1,...,value_n là các giá trị tương ứng với các pram.

  • VD: với hàm getSum ở trên mình sẽ xét tham số mặc định cho nó.
  • 4. Hàm có và không trả về giá trị

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ề.: khai báo hàm getSum ở trên là hàm có giá trị trả về.

function add(a, b, ret) {
    ret(a);
}
4

Đối với hàm có giá trị trả về thì phải sử dụng từ khóa Function.prototype.async = function () { return setTimeout.bind(null, this, 0).apply(null, arguments); }; function fib(n, k) { switch (n) { case 0: k(0); break; case 1: k(1); break; default: fib.async(n - 1, function (x) { fib.async(n - 2, function (y) { k(x + y); }); }); } } fib(20, console.log); // 6765 0

Và ngược lại đối với hàm không có giá trị trả về thì không có từ khóa

Function.prototype.async = function () {
    return setTimeout.bind(null, this, 0).apply(null, arguments);
};

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib.async(n - 1, function (x) {
            fib.async(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // 6765
0 một hàm sẽ không thực thi nó. Định nghĩa một hàm đơn giản chỉ là đặt tên cho hàm và chỉ định những việc cụ thể sẽ làm khi hàm đó được gọi.

VD: khai báo hàm getSum ở trên là hàm có giá trị trả về.

function add(a, b, ret) {
    ret(a);
}
5

Gọi hàm

Việc định nghĩa một hàm sẽ không thực thi nó. Định nghĩa một hàm đơn giản chỉ là đặt tên cho hàm và chỉ định những việc cụ thể sẽ làm khi hàm đó được gọi.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:

Ví dụ, nếu bạn định nghĩa hàm sayHello, bạn có thể gọi nó như sau:

Câu lệnh trên gọi hàm sayHello, kết quả trả về chữ "Hello World" tại console.

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:Đ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::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
4). Đ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:

function add(a, b, ret) {
    ret(a);
}
7

function add(a, b, ret) { ret(a); } 6

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).

function add(a, b, ret) {
    ret(a);
}
8

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::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
4). Đ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:
Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
5 tới
Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
6
 như sau:

function add(a, b, ret) {
    ret(a);
}
9

Một hàm có thể gọi chính nó

Ví dụ, đây là một hàm tính giai thừa đệ quy:

Bạn có thể tính giai thừa của Function::async = -> setTimeout.bind(null, @, 0) ... fib = (n, k) --> | n == 0 => k 0 | n == 1 => k 1 | otherwise => x <- fib.async n - 1 y <- fib.async n - 2 k x + y fib 20, alert # 6765 5 tới Function::async = -> setTimeout.bind(null, @, 0) ... fib = (n, k) --> | n == 0 => k 0 | n == 1 => k 1 | otherwise => x <- fib.async n - 1 y <- fib.async n - 2 k x + y fib 20, alert # 6765 6 như sau:

Có những cách khác để gọi hàm. Có nhiều trường hợp mà tại đó một hàm cần phải được gọi một cách tự động, hoặc làm thay đổi số lượng đối số truyền vào một hàm, hoặc trong trường hợp mà việc gọi hàm cần được gắn với một object nhất định được quyết định tại thời điểm runtime.

Điều đó lại hóa ra là các hàm tự bản thân chúng là các object, và kết quả là, những object này có các phương thức. Một trong số chúng, phương thức 

Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
7, có thể được dùng để đạt được mục tiêu này.

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
0

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.default parameters và rest parameters

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:

Các tham số của Function

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à 

Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
8.
Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
8.

Trong ví dụ sau, nếu không có giá trị nào được truyền cho 

function sayHello () {
  console.log("Hello World");
}
0, giá trị của nó sẽ là 
Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
8 khi thực hiện tính toán 
function sayHello () {
  console.log("Hello World");
}
2, và việc gọi hàm 
function sayHello () {
  console.log("Hello World");
}
3 sẽ trả về 
function sayHello () {
  console.log("Hello World");
}
4. Tuy nhiên, điều này bị ngăn chặn bởi dòng thứ 2 trong ví dụ này:

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
1

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 

Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
5 vào làm giá trị mặc định cho 
function sayHello () {
  console.log("Hello World");
}
0 ngay tại head của function: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 
Function::async = -> setTimeout.bind(null, @, 0) ...

fib = (n, k) -->
    | n == 0    => k 0
    | n == 1    => k 1
    | otherwise =>
        x <- fib.async n - 1
        y <- fib.async n - 2
        k x + y

fib 20, alert # 6765
5 vào làm giá trị mặc định cho 
function sayHello () {
  console.log("Hello World");
}
0 ngay tại head của function:

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
2

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ố.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 

function sayHello () {
  console.log("Hello World");
}
3 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.

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
3

Arrow Function

Trong ES6, arrow function là một cú pháp mới dùng để viết các hàm trong JavaScript. Nó giúp tiết kiệm thời gian phát triển và đơn giản hóa phạm vi function (function scope).arrow function là một cú pháp mới dùng để viết các hàm trong JavaScript. Nó giúp tiết kiệm thời gian phát triển và đơn giản hóa phạm vi function (function scope).

Arrow function - còn được gọi là "fat arrow", là cú pháp được mượn từ CoffeeScript (một ngôn ngữ chuyển tiếp), cú pháp này là cách ngắn gọn hơn dùng để viết function. Ở đây sử dụng kí tự 

function sayHello () {
  console.log("Hello World");
}
8, trông giống như một mũi tên "béo". Arrow function là một hàm vô danh và nó thay đổi cách 
function sayHello () {
  console.log("Hello World");
}
9 bind đến function. Arrow function làm code của ta trông ngắn gọn hơn, giúp đơn giản hóa function scoping cũng như từ khóa 
function sayHello () {
  console.log("Hello World");
}
9.
- còn được gọi là "fat arrow", là cú pháp được mượn từ CoffeeScript (một ngôn ngữ chuyển tiếp), cú pháp này là cách ngắn gọn hơn dùng để viết function. Ở đây sử dụng kí tự 
function sayHello () {
  console.log("Hello World");
}
8, trông giống như một mũi tên "béo". Arrow function là một hàm vô danh và nó thay đổi cách 
function sayHello () {
  console.log("Hello World");
}
9 bind đến function. Arrow function làm code của ta trông ngắn gọn hơn, giúp đơn giản hóa function scoping cũng như từ khóa 
function sayHello () {
  console.log("Hello World");
}
9.

Dưới đây là 1 vài ví dụ về việc sử dụng Arrow fucntion trong JavascriptArrow fucntion trong Javascript

1. Trường hợp có nhiều tham số

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
4

Ví dụ trên cho cùng một kết quả, tuy nhiên cú pháp với arrow function tốn ít dòng mã hơn. Trong trường hợp chỉ có một biểu thức thì không cần tới dấu ngoặc nhọn: Ví dụ trên có thể viết lại như sau:

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
5

2. Trường hợp có 1 tham số

Dấu ngoặc đơn là không bắt buộc khi chỉ có 1 tham số.

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
6

3. Trường hợp không có tham số

Dấu ngoặc đơn là bắt buộc khi không có tham số.

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
7

Closures

Closures là một trong những chức năng quyền lực nhất của JavaScript. JavaScript cho phép lồng các function vào nhau, và cấp quyền cho function con, để function con có toàn quyền truy cập vào tất cả các biến và function được định nghĩa bên trong function cha (và tất cả biến và function mà function cha được cấp quyền truy cập đến). là một trong những chức năng quyền lực nhất của JavaScript. JavaScript cho phép lồng các function vào nhau, và cấp quyền cho function con, để function con có toàn quyền truy cập vào tất cả các biến và function được định nghĩa bên trong function cha (và tất cả biến và function mà function cha được cấp quyền truy cập đến).

Tuy nhiên, function cha không có quyền truy cập đến các biến và function được định nghĩa bên trong function con. Điều này tạo nên một dạng bảo mật khép kín cho các biến của function con.

Bên cạnh đó, vì function con có quyền truy cập đến scope của function cha, các biến và function được định nghĩa bên trong function cha sẽ vẫn tồn tại dù việc thực thi function cha đã kết thúc, nếu function con xoay sở để tồn tại lâu hơn thời gian sống của function cha. Một closure được tạo ra khi một function con bằng cách nào đó trở nên khả dụng với bất kỳ scope nào bên ngoài function cha.

Hãy xem các ví dụ sau đây để hiểu hơn về ClosuresClosures

VD1:

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
8

Trong ví dụ trên, hàm 

const sayHello = function() {
     console.log("Hello World");
}
1 tạo ra một biến local 
const sayHello = function() {
     console.log("Hello World");
}
2 và 
const sayHello = function() {
     console.log("Hello World");
}
3 (một hàm in ra num trong console). Hàm 
const sayHello = function() {
     console.log("Hello World");
}
3 không có bất kỳ biến local nào trong nó. Tuy nhiên, nó có quyền truy cập vào các biến bên ngoài function, bởi vì 
const sayHello = function() {
     console.log("Hello World");
}
1 là một closure. Do đó, nó có thể sử dụng biến 
const sayHello = function() {
     console.log("Hello World");
}
2 được khai báo trong 
const sayHello = function() {
     console.log("Hello World");
}
1 để log 
const sayHello = function() {
     console.log("Hello World");
}
2 trong console sau khi 
const sayHello = function() {
     console.log("Hello World");
}
1 được trả lại.
const sayHello = function() {
     console.log("Hello World");
}
1 tạo ra một biến local 
const sayHello = function() {
     console.log("Hello World");
}
2 và 
const sayHello = function() {
     console.log("Hello World");
}
3 (một hàm in ra num trong console). Hàm 
const sayHello = function() {
     console.log("Hello World");
}
3 không có bất kỳ biến local nào trong nó. Tuy nhiên, nó có quyền truy cập vào các biến bên ngoài function, bởi vì 
const sayHello = function() {
     console.log("Hello World");
}
1 là một closure. Do đó, nó có thể sử dụng biến 
const sayHello = function() {
     console.log("Hello World");
}
2 được khai báo trong 
const sayHello = function() {
     console.log("Hello World");
}
1 để log 
const sayHello = function() {
     console.log("Hello World");
}
2 trong console sau khi 
const sayHello = function() {
     console.log("Hello World");
}
1 được trả lại.

VD2:

function fib(n, k) {
    switch (n) {
    case 0:  k(0); break;
    case 1:  k(1); break;
    default:
        fib(n - 1, function (x) {
            fib(n - 2, function (y) {
                k(x + y);
            });
        });
    }
}

fib(20, console.log); // maximum recursion depth exceeded
9

Chú ý, biến 

const hello = function sayHello() {
     console.log("Hello World");
}
0 được khai báo sau anonymous function nhưng vẫn có thể truy cập biến 
const hello = function sayHello() {
     console.log("Hello World");
}
0. Điều này là do biến 
const hello = function sayHello() {
     console.log("Hello World");
}
0 đã được khai báo trong function scope tại thời điểm được tạo ra, làm cho nó có sẵn khi anonymous function được thực thi.
const hello = function sayHello() {
     console.log("Hello World");
}
0 được khai báo sau anonymous function nhưng vẫn có thể truy cập biến 
const hello = function sayHello() {
     console.log("Hello World");
}
0. Điều này là do biến 
const hello = function sayHello() {
     console.log("Hello World");
}
0 đã được khai báo trong function scope tại thời điểm được tạo ra, làm cho nó có sẵn khi anonymous function được thực thi.

Callback Function

Callback function có thể được hiểu nôm na như sau: callback tức là ta truyền một đoạn code (Hàm A) này vào một đoạn code khác (Hàm B). Tới một thời điểm nào đó, Hàm A sẽ được hàm B gọi lại (callback). Javascript là một ngôn ngữ lập trình hướng sự kiện và bất đồng bộ nên callback function đóng vai trò rất quan trọng, bạn sẽ truyền một callback function vào các sự kiện và xử lý bất đồng bộ đó.. có thể được hiểu nôm na như sau: callback tức là ta truyền một đoạn code (Hàm A) này vào một đoạn code khác (Hàm B). Tới một thời điểm nào đó, Hàm A sẽ được hàm B gọi lại (callback). Javascript là một ngôn ngữ lập trình hướng sự kiện và bất đồng bộ nên callback function đóng vai trò rất quan trọng, bạn sẽ truyền một callback function vào các sự kiện và xử lý bất đồng bộ đó..

Về Callback Function thì mình cũng đã có viết 1 bài riêng để chúng ta cùng tìm hiểu về nó. Các bạn có thể đọc thêm ở đây.

Tạm kết

Như vậy trong bài viết này, chúng ta đã cùng nhau tìm hiểu những vấn đề cơ bản về Function trong Javascript. Bạn thấy thế nào về JS, hãy đưa ra những ý kiến trong quá trình sử dụng js nhé. Nếu các bạn thấy bài viết hữu ích hãy rate 5* và share cho mọi người tham khảo!Bạn thấy thế nào về JS, hãy đưa ra những ý kiến trong quá trình sử dụng js nhé. Nếu các bạn thấy bài viết hữu ích hãy rate 5* và share cho mọi người tham khảo!

Hãy để lại comment để mình có thể hoàn thiện bản thân hơn trong tương lai. Cám ơn các bạn!