Tìm hiểu sự khác biệt giữa function declarations (khai báo hàm) và function expressions (biểu thức hàm) trong JavaScript.
Đây là function expression:
// Function expression let excuteMe = function () { console.log('Function Expression') }
Đây là function declaration:
// Function declaration function excuteMe() { console.log('Function Declaration') }
Function expressions và declarations về cơ bản làm cùng việc. Trong cả hai ví dụ, bạn gọi chúng như executeMe() và chúng thực thi mã bên trong khối code {..}.
Nhưng có sự khác biệt, không phải ở việc chúng làm gì, mà ở cách chúng được thực thi — hoặc đúng hơn, thứ tự chúng được đọc và thực thi — và điều này quan trọng.
Hàm này sẽ chạy tốt:
// Gọi function declaration executeMe() // Function declaration function excuteMe() { console.log('Function Declaration') }
Tuy nhiên, hàm này sẽ không chạy tốt (sẽ ném lỗi):
// Gọi function executeMe() // Function expression let excuteMe = function () { console.log('Function Expression') }
Để hiểu tại sao ví dụ đầu (function declaration) chạy được, nhưng ví dụ hai (function expression) không, bạn phải học hoisting (nâng lên) trong JavaScript, rồi mọi thứ sẽ bắt đầu có nghĩa.
Hoisting (Nâng lên)
Trong JavaScript, mã được thực thi từ trên xuống dưới. Thông thường, bạn chỉ dùng biến sau khi khai báo nó trong file script (theo thứ tự), tuy nhiên, function declarations được hoisted (nâng lên) lên đầu bởi trình duyệt, trước khi bất kỳ mã nào chạy.
Hoisting nghĩa là trình thông dịch mã của trình duyệt luôn luôn biết (đã đọc) function declarations của bạn trước khi chúng được gọi (bảo thực thi).
Vì vậy bạn có thể gọi và thực thi function declaration từ bất kỳ đâu trong file JavaScript, ngay cả trước khi function declaration tồn tại vật lý trong file script, theo thứ tự. Điều này làm function declarations độc đáo.
Function declarations (Khai báo hàm)
Trước khi bất kỳ mã nào chạy, trình duyệt tải (đọc) toàn bộ file JS script của bạn, và vì function declarations được di chuyển lên đầu (hoisted) của thứ tự thực thi, mã của bạn thực tế được thực thi theo thứ tự này bởi trình thông dịch:
// 1. Function declaration function excuteMe() { console.log('Function Declaration') } // 2. Gọi function declaration executeMe()
Dù mã của bạn vật lý viết trong file script theo thứ tự ngược lại:
// Gọi function declaration executeMe() // Function declaration function excuteMe() { console.log('Function Declaration') }
Đó là lý do không quan trọng bạn đặt gọi function declaration trước hay sau khi nó được định nghĩa trong file script. Cả hai cách đều chạy vì JavaScript luôn load function declarations trước khi chạy function calls đến những function declarations đó.
Function Expressions không hoist
Function expressions không được hoisted (không đọc trước). Function expressions chỉ được đọc khi trình thông dịch đến dòng mã đó trong file script. Vì vậy nếu function expression vật lý tồn tại sau function call (theo thứ tự) thì trình thông dịch JavaScript chạy trong trình duyệt không thể thực thi nó.
Vì vậy cái này sẽ không chạy:
// Gọi function expression executeMe() // Function expression let excuteMe = function () { console.log('Function Expression') }
Mã trên sẽ báo lỗi vì bạn đang cố chạy hàm (executeMe()) trước khi trình thông dịch trình duyệt biết (đọc) về sự tồn tại của function expression.
Đổi thứ tự mã và nó sẽ chạy:
// Function expression let excuteMe = function () { console.log('Function Expression') } // Gọi function expression executeMe()
Mã trên chạy nhờ thứ tự mã. Trình duyệt sẽ đọc dòng đầu (từ trên xuống) chứa biến let executeMe chứa function expression — rồi sau đó được yêu cầu thực thi nó với executeMe().
Nên dùng cái nào?
Quan trọng không phải bạn dùng function declarations hay function expressions. Cái nào cũng không làm vỡ sự nghiệp. Nhất quán quan trọng hơn nhiều. Chọn một phong cách, và kiên trì, ít nhất trong cùng dự án.
Quan trọng hơn, nếu bạn tham gia team dùng function expressions, thì bạn nên tuân theo quy tắc của họ để giữ codebase nhất quán, bất kể sở thích cá nhân.
Một lý do tốt cho function expressions là chúng ép bạn dùng cấu trúc code chặt chẽ hơn, làm code dễ dự đoán hơn. Function declarations tha thứ hơn vì bạn có thể gọi từ bất kỳ đâu trong script, và điều đó có thể dẫn đến cấu trúc code không nhất quán, đặc biệt khi nhiều dev làm cùng dự án.
Một lý do tốt (có tranh cãi) cho function declarations là nếu bạn có hàm gọi hàm khác, nhưng bối rối về thứ tự và vì lý do nào đó không thể làm function expressions chạy do nhầm lẫn thứ tự.