7. Hàm mũi tên – Arrow Function trong JavaScript

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 – 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ự => , 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 this 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 this.

Ví dụ: trường hợp có nhiều tham số:

// (param1, param2, paramN) => expression
// ES5
var multiply = function(x, y) {
return x * y;
};
// ES6
var multiply = (x, y) => { return x * y };

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:

var multiply = (x, y) => x * y ;

Ví dụ: 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ố.
//ES5
var phraseSplitterEs5 = function phraseSplitter(phrase) {
return phrase.split(' ');
};

//ES6
var phraseSplitterEs6 = phrase => phrase.split(" ");
console.log(phraseSplitterEs6("Love Codelearn")); // ["Love", "Codelearn"]

Ví dụ trường hợp không có tham số:

// ES5
var hello = function sayHello() {
     console.log("Hello World");
}
// ES6
var hello = () => { console.log("Hello World"); }
hello(); // Hello World

8. Chức năng Closures trong JavaScript

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

Ví dụ:

function numberGenerator() {
// Biến cục bộ "miễn phí" kết thúc trong thời gian đóng
var num = 1;
function checkNumber() {
console.log(num);
}
num++;
return checkNumber;
}
var number = numberGenerator();
number(); // 2

Trong ví dụ trên, hàm numberGenerator() tạo ra một biến local numcheckNumber() (một hàm in ra num trong console). Hàm checkNumber() 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ì numberGenerator() là một closure. Do đó, nó có thể sử dụng biến num được khai báo trong numberGenerator() để log num trong console sau khi numberGenerator() được trả lại.

Một ví dụ khác:

function sayHello() {
var say = function() { console.log(hello); }
// Local variable that ends up within the closure
var hello = 'Hello, world!';
return say;
}
var sayHelloClosure = sayHello();
sayHelloClosure(); // ‘Hello, world!’

Biến hello được khai báo sau anonymous function nhưng vẫn có thể truy cập biến hello . Điều này là do biến hello đã đượ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.

9. Chức năng trả về – return trong JavaScript

9.1. Hàm trả về một giá trị

Khi thực thi đến một câu lệnh return, thì function sẽ không thực thi nữa. Một hàm sẽ trả về một giá trị hoặc không trả về giá trị nào.

Ví dụ: Ta sẽ chỉnh sửa một chút ở hàm tongHaiSo để nó trả về tổng của hai số a và b:

// Tạo một hàm tính tổng hai số
function tongHaiSo(a, b)
{
// Trả về tổng hai số a + b
return a + b;
}

Khi ta gọi hàm như thế này tongHaiSo(2, 3); thì ở phần trực quan thì chúng ta không thay đổi gì. Tuy nhiên để biết chúng có thay đổi gì hay không ta làm như sau:

// Gán giá trị trả về cho biến c
let c = tongHaiSo(2, 3);
// Kiểm tra giá trị của c
console.log(c);

Kết quả là 5. Như vậy khi sử dụng câu lệnh return, hàm tongHaiSo trả về một giá trị. Giá trị này chúng ta có thể sử dụng trong các chương trình tiếp theo, ví dụ:

let c = tongHaiSo(2, 3);
function xepHang(c)
{
if ( c >= 5 ) {
console.log("Điểm trên trung bình!");
} else {
console.log("Điểm dưới trung bình!");
}
}
xepHang(c);

Bởi vì bạn chưa học đến phần cấu trúc điều kiện if else nên có thể bạn chưa hiểu lắm. Nhưng bạn chỉ cần hiểu là chúng ta có thể có kết quả được trả về từ một hàm nào đó thông qua việc return. Sau đó, bạn muốn sử dụng giá trị đó để làm gì thì tùy bạn.

9.2. Hàm trả về hàm

Đây cũng là một điểm mạnh của JavaScript so với các ngôn ngữ khác. Khi mà trong một hàm sẽ có một hoặc nhiều hàm khác nó cho phép chúng ta trả về một hàm. Những function nằm trong function cha sẽ được gọi là nested function khi đó chúng chỉ hoạt động được trong function cha mà không gọi được ở bên ngoài fucntion đó.

Ví dụ:

function a() {
console.log('A');
}
let x = a(); // A
console.log(x); // undefined
function b() {
console.log('B');
return a;
}
let y = b(); // B
console.log(y); // return function a
function c() {
console.log('C');
return a();
}
let z = c(); // C A
console.log(z); // return undefined

Có vẻ hơi khó hiểu với ví dụ này nhưng không sao, các bạn chưa cần hiểu nó vội đâu. Bạn chỉ cần biết ví dụ này chạy nó và biết được rằng bạn có thể return một function trong Js là được.

10. Callback funtion trong JavaScript

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ộ đó..

Callback phải là một function Callback là một function nên bạn nhất định phải truyền vào là một function, nếu bạn truyền một type khác thì bạn sẽ nhận được error notice: “Callback is function” trong console. Từ khóa this trong callback Như đã nói ở trên thì callback là một hàm bình thường nên khi sử dụng từ khóa this trong hàm thì nó sẽ hiểu this lúc này chính là đối tượng Window, nếu bạn dùng debuger trong hàm callback rồi vào console gõ this, thì sẽ được Window {external: Object, chrome: Object, result: undefined,…… Vì vậy cho dù bạn định nghĩa hàm callback nằm trong một object thì không thể truy cập đến dữ liệu của object thông qua từ khóa this.

Ví dụ:

function dispatch (fn) { fn(); }
//Cách 1: khai báo một function để làm tham số.
var fn = () => { console.log( "Hello !"); }
// gọi function dispatch()
dispatch(fn); // Outputs "Hello !"
//Cách 2: Khai báo một anonymous function bên trong
dispatch (function () {
console.log("Hello !");
});
//METHOD 3: Định nghĩa một arrow function bên trong
dispatch (() => { console.log ("Hello !") });

11. Truyền tham số vào function trong JavaScript

Trong C/C++, chúng ta phải định nghĩa rõ ràng kiểu tham số, số lượng tham số của hàm. Và khi gọi hàm thì phải gọi đúng như vậy. Nếu muốn đa dạng các kiểu giá trị thì bạn có thể dùng template. Nếu không muốn truyền một tham số nào vào hàm thì bạn có thể dùng giá trị mặc định (default). Điều này giúp cho C/C++ trở nên rất chặt chẽ. Ngược lại, JavaScript cung cấp cho chúng ta cách thức sử dụng function rất linh động. Bạn không cần phải quy định kiểu giá trị truyền vào hay trả về. Bạn có thể đưa vào số lượng tham số bằng, nhiều hơn hay ít hơn so với định nghĩa.

Nếu bạn đưa vào nhiều tham số hơn so với định nghĩa thì nó sẽ bỏ qua thành phần thừa. Còn nếu bạn đưa vào ít tham số hơn thì những thành phần thiếu mặc định sẽ là undefined.

Ví dụ về hàm tích lũy thừa:

function power(base, exponent) {
if (exponent == undefined) exponent = 2;
var result = 1;
for (var count = 0; count < exponent; count++) result *= base;
return result;
}
console.log(power(4));
// => 16
console.log(power(4, 3));
// => 64
console.log(power(3, 2, 1));
// => 9

Trường hợp bạn không truyền giá trị cho tham số exponent thì mặc định exponent bằng undefined. Khi đó, mình gán exponent bằng 2. Ngược lại, mình sẽ tính như bình thường.

12. Hàm đệ quy – Recursion trong JavaScript

Recursion hay còn gọi là hàm đệ quy. Recursion cho phép một hàm có thể gọi đến chính nó, miễn sao không bị tràn stack.

Ví dụ:

function power(base, exponent) {
if (exponent == 0) return 1;
else return base * power(base, exponent - 1);
}
console.log(power(2, 3));
// => 8

Ví dụ trên miêu tả cách tính luỹ thừa sử dụng đệ quy. Và bạn có thể thấy rằng cách biểu diễn này rất gần với định nghĩa mà chúng ta đã học trong toán học.

a^b = 1, nếu b = 0

a^b = a * a^(b-1), nếu b > 0

Khi sử dụng recursion trong một số bài toán, bạn chỉ cần chuyển từ công thức toán học sang ngôn ngữ lập trình một cách đơn giản. Do đó, sử dụng đệ quy thường ngắn gọn và dễ dàng hơn so với sử dụng vòng lặp.

Nhưng trong JavaScript, việc sử dụng đệ quy sẽ chậm hơn khoảng 10 lần so với sử dụng vòng lặp. Do đó, bạn nên cân nhắc trước khi quyết định sử dụng phương án này. Cụ thể, bạn nên so sánh giữa cách triển khai và tốc độ thực hiện chương trình. Cách triển khai có thể hiểu là tính rõ ràng, dễ hiểu khi viết code. Thông thường, hai yếu tố này sẽ tỉ lệ nghịch với nhau. Và bạn sẽ phải tìm ra một điểm cân bằng.