Cơ chế hoạt động của hoisting trong Javascript
Trong bài này chúng ta sẽ tìm hiểu hoisting trong Javascript, qua đó sẽ giúp bạn hiểu khái niệm hosting là gì, và cơ chế hoạt động của hosting trong ngôn ngữ javascript.
Hoisting là vấn đề liên quan đến cách khai báo biến trong Javascript. Theo quy tắc chung của hầu hết các ngôn ngữ lập trình, để sử dụng một biến thì bạn phải khai báo nó trước. Nhưng javascript thì lại khác, có một số trường hợp bạn có thể sử dụng biến rồi mới khai báo sau. Lý do là gì thì chúng ta cùng tìm hiểu ngay nhé.
1. Hoisting trong javascript là gì?
Khi bạn thực thi một đoạn mã javascript thì trình biên dịch sẽ tạo ra một bối cảnh thực thi chung, ta gọi là global execution context.
Bối cảnh thực thi này sẽ có hai giai đoạn, thứ nhất là tạo và thứ hai là thực thi.
Bài viết này được đăng tại [free tuts .net]
Trong giai đoạn tạo thì javascript sẽ di chuyển các khai báo biến lên đầu của đoạn mã, giúp cho chương trình không bị lỗi khi sử dụng biến trước khi khai báo. Tính năng này ta gọi là tính năng lưu trữ trong javascript, và trong tiếng Anh gọi là Hoisting.
2. Biến hoisting trong javascript
Trong Javascript, bạn có thể định nghĩa một biến sau khi sử dụng nhờ tính năng hoisting.
Javascript sẽ di chuyển toàn bộ các khai báo biến lên đầu chương trình. Vì vậy, những dòng code có sử dụng biến mà chưa khai báo sẽ không bị lỗi.
// Gán nhưng chưa khai báo biến domain = 'freetuts.net'; // Kết quả: Domain là freetuts.net document.write("Domain là: " + domain); // Khai báo var domain; // Kết quả: Domain là freetuts.net document.write("<br/> Domain là: " + domain);
Nếu trong lúc khởi tạo mà bạn gán giá trị cho biến thì kết quả sẽ khác
// Gán nhưng chưa khai báo biến domain = 'freetuts.net'; // Kêt quả: Domain là freetuts.net document.write("Domain là: " + domain); // Khai báo var domain = 'techtuts.net'; // kêt quả: Domain là freetuts.net document.write("<br/> Domain là: " + domain);
Nếu khai báo biến trước khi sử dụng thì ta vẫn có kết quả giống nhau.
// Khai báo var domain; // Gán nhưng chưa khai báo biến domain = 'freetuts.net'; // Kết quả: Domain là freetuts.net document.write("Domain là: " + domain); // Kết quả: Domain là freetuts.net document.write("<br/> Domain là: " + domain);
Trong ví dụ thứ 3 này thì ta đã khai báo biến trước rồi mới sử dụng sau. Điều này tuân thủ theo quy tắc tạo biến rồi mới sử dụng của hầu hết các ngôn ngữ lập trình. Nó giúp chương trình trở nên sáng và logic hơn.
3. Từ khóa let và var trong hoisting
Việc sử dung từ khóa let và var để khai báo một biến sẽ có sự khác biệt trong hoisting.
Sử dụng var: Nếu biến chưa gán giá trị thì sẽ trả về undefined.
console.log(counter); // undefined var counter = 1;
Đoạn mã trên sẽ tương đương với đoạn mã dưới đây.
var counter; console.log(counter); // undefined counter = 1;
Sử dụng let: Nếu biến chưa gán giá trị thì sẽ xuất hiện thông báo lỗi Cannot access before initialization.
console.log(counter); let counter = 1;
Kết quả:
"ReferenceError: Cannot access 'counter' before initialization
Lỗi này muốn nói rằng biến counter đã được định nghĩa trong bộ nhớ heap nhưng chưa được khởi tạo.
4. Hàm hoisting trong javascript
Cũng giống như các biến, công cụ JavaScript cũng lưu trữ các khai báo hàm. Nó sẽ di chuyển các khai báo hàm lên đầu của chương trình.
let x = 20, y = 10; let result = add(x,y); console.log(result); function add(a, b){ return a + b; }
Trong ví dụ này thì mình đã sử dụng hàm add rồi mới khai báo sau. Tuy nhiên, javascript vẫn không trả về một lỗi nào cả. Lý do là cơ chế hoisting của javascript đã di chuyển các khai báo hàm lên đầu rồi mới thực thi chương trình.
Có nghĩa là đoạn code dưới đây sẽ tương đương:
function add(a, b){ return a + b; } let x = 20, y = 10; let result = add(x,y); console.log(result);
5. Hoisting trong biểu thức hàm
Biểu thức hàm hay còn gọi là function expressions. Nó là một hàm được khai báo bằng cách gán vào một biến.
var add = function(x, y) { return x + y; }
Hàm add chính là một function expressions.
Quay trở lại bài toán. Câu hỏi là chuyện gì sẽ xảy ra nếu ta sử dụng một function expressions trước rồi mới khai báo sau? Hãy xem ví dụ dưới đây.
let x = 20, y = 10; let result = add(x,y); console.log(result); var add = function(x, y) { return x + y; }
Chạy đoạn code này thì kết quả sẽ xuất hiện lỗi:
"TypeError: add is not a function
Lý do rất đơn giản. Javascript sẽ xem biết add là một biến thông thường, nên khi nó đưa lên đầu thì sẽ là một biến chứ không phải là một hàm. Vì vậy, khi sử dụng sẽ xuất hiện lỗi add is not a function.
6. Hoisting trong arrow function
Cú pháp tạo arrow function sẽ như sau:
var function_name = () => bieuthuc;
Chính vì vậy, nếu bạn sử dụng arrow function trước rồi mới khai báo thì sẽ bị lỗi function_name
không phải là một hàm.
7. Lời kết
Hoisting nghe có vẻ kì lạ và hay, nhưng riêng cá nhân mình thấy hoisting không thực sự tốt. Bởi theo sự logic của các ngôn ngữ lập trình là bạn phải sử dụng một biến trước khi khai báo chúng. Và Hoisting trong javascript đã phá vỡ đi quy tắc đó.