Hướng dẫn javascript get function

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

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

1. Khai báo hàm

Để 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 {...}.

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:

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.

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ự _.
  • Tên hàm không được bắt đầu bằng số, các ký tự khác ký tự _.

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

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

function getSum(a, b) {
	console.log("Tổng: " + (a + b));
}

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

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

function getSum(a = 5, b = 10) {
	console.log("Tổng: " + (a + b));
}

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

  • Đố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

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

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

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.

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

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

sayHello(); // Gọi hàm

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:

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 is not defined
console.log(getAreaOfSquare(5)); // ReferenceError: getAreaOfSquare is not defined
getAreaOfSquare = function (n) {
  return n * n;
}

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:

function factorial(n){
  if ((n == 0) || (n == 1))
    return 1;
  else
    return (n * factorial(n - 1));
}

Bạn có thể tính giai thừa của 1 tới 3 như sau:

var a, b, c;
a = factorial(1); // a gets the value 1
b = factorial(2); // b gets the value 2
c = factorial(3); // c gets the value 6

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 apply(), có thể được dùng để đạt được mục tiêu này.

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:

// 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"

Các tham số của Function

Kể từ ES6, xuất hiện 2 dạng tham số mới: default parameters và rest parameters

1. Default parameters

Trong JavaScript, các tham số của function được 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

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.

function multiply(multiplier, ...theArgs) {
  return theArgs.map(x => multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

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

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

1. 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 ;

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

//ES5 
var phraseSplitterEs5 = function phraseSplitter(phrase) { 
    return phrase.split(' '); 
}; 
 
//ES6 
var phraseSplitterEs6 = phrase => phrase.split(" "); 
 
console.log(phraseSplitterEs6("Love Codelearn"));  // ["Love", "Codelearn"]

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

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

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

hello(); // Hello World

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

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ề Closures

VD1:

function numberGenerator() {
  // Local “free” variable that ends up within the closure
  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 num và checkNumber() (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.

VD2:

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!’

Chú ý, 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.

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

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!

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!