Cơ chế hoisting là gì

Mục Lục
  • 1. Hoisting là gì?
  • 2. So sánh thứ tự ưu tiên trong cơ chế hoisting
  • 3. Đối phó với vấn đề hoisting như thế nào?
  • Tổng kết


Là một vấn đề cản bước rất nhiều lập trình viên nắm giữ JavaScript. Hoistring là gì? Hoisting hoạt động như thế nào? Cùng theo dõi bài viết với mình nhé.


1. Hoisting là gì?


Hôi à không hoisting là cơ chế của JavaScript cho phép các khai báo biến hoặc hàm được dời lên trên đầu phạm vi của chúng trước khi thực thi đoạn code.

Điều này có nghĩa là bất kể hàm và biến được khai báo ở đâu, chúng đều được chuyển lên đầu phạm vi của chúng, bất kể là toàn cục hay cục bộ.

> Lưu ý: Là cơ chế này chỉ di chuyển phần khai báo mà thôi còn các phần khác giữ nguyên không đụng gì đến nó hết.


1.1. Hành vi hoisting đối với biến


Trong JavaScript, ta có thể sử dụng biến trước khi khai báo nó, như thế này:


//GánchochuỗiNIITchobiếnthuongHieu
thuongHieu="NIIT";
//SửdụngbiếnthuongHieu
console.log[thuongHieu];
//KhaibáothuongHieu
varthuongHieu;

Kết quả:


NIIT

Và JavaScript chỉ lưu trữ các khai báo, không lưu trữ các khởi tạo:


//KhaibáovàkhởitạobiếnthuongHieu
varthuongHieu="NIIT";

//Inrathôngtinbiến
console.log[thuongHieu+"thànhlậpnăm:"+namThanhLap];

//KhaibáovàkhởitạobiếnnamThanhlap
varnamThanhLap=2002;

Nếu như bạn nghĩ kết quả nhận được: NIIT thành lập năm: 2002 thì xin lỗi...

Kết quả nhận được như thế này cơ:



NIIT thành lập năm: undefined

Bởi vì, ngay khi chạy chương trình thì JavaScript sẽ thực hiện khai báo một lượt các biến chưa sử dụng là lưu trữ nó.

Tuy nhiên, các biến này dù có được bạn khởi tạo thì JavaScript trước tiên vẫn gán cho nó giá trị mặc định là undefined.

Để hiểu hoạt động của hoisting đối với biến thì hãy cùng tìm hiểu tiếp ví dụ:



functionhoi[]{
console.log[message];
varmessage="LậptrìnhJavaScriptcănbản";
}
hoi[];

Kết quả ta được:


undefined

Tại sao hồi nãy nói là hoisting sẽ chuyển phần khai báo lên trên đầu phạm vi khi thực thi mà nó lại ra kết quả undefined?

Thực ra thì nó chuyển phần khai báo như này nè:



functionhoi[]{
varmessage;
console.log[message];
message="LậptrìnhJavaScriptcănbản";
}

Như bạn đã biết, khi khai báo biến mà không gán dữ liệu thì mặc định nó có giá trị là undefied

Như vậy thì khi thực thi chương trình:


  • Phần khai báo sẽ được chuyển lên đầu hàm hoi[]
  • Nhưng phần gán giá trị sẽ không được chuyển nên khi in ra sẽ là in ra undefined.

Các bạn lưu ý phần này nhé.

Để tránh những trường hợp như vậy xuất hiện về sau thì bạn nên khai báo và khởi tạo cùng một lúc như sau:



functionhoi[]{
//Vừakhaibáo,vừakhởitạo
//trướckhisửdụng
varmessage="LậptrìnhJavaScriptcănbản";
console.log[message];
}
hoi[];

1.2. Hành vi hoisting đối với hàm


Đối với hàm thì chúng ta biết là trong JavaScript có hai dạng hàm đó là khai báo hàm[function declarations] và biểu thức hàm [function expression].

> Quên thì xem lại bài Hàm trong JavaScript ngay

+ Đối với Khai báo hàm:

Mình vẫn sẽ lấy ví dụ hàm hoi[] để các bạn tiện theo dõi. Đối với khai báo hàm thì hành vi hoisting cũng giống như biến JavaScript.

Việc khai báo sẽ được chuyển lên trên đầu.



//Gọihàmtrướckhikhaibáo
hoi[];

//Khaibáohàm
functionhoi[]{
varmessage="LậptrìnhJavaScriptcănbản";
document.write[message];
}

Hàm trên tương đương với:


//Khaibáohàm
functionhoi[]{
varmessage="LậptrìnhJavaScriptcănbản";
document.write[message];
}
//Gọihàmsaukhikhaibáo
hoi[];

Như vậy thì chúng ta có thể hiểu đơn giản là cũng như biến, hàm khai báo cũng sẽ được gọi trước khi chúng ta đăng ký hàm đó.

Đó chính là cơ chế hoisting.


+ Đối với Biểu thức hàm:

Đối với biểu thức hàm ngược lại.

Vì biểu thức hàm bản chất là hàm được gán cho một biến. Do đó, nó cũng giống như vừa khai báo và vừa khởi tạo biến.

Thế nên, cơ chế hoisting không áp dụng cho biểu thức hàm.

Ví dụ:



//Gọihàmtrước
hoi[];

//Khaibáo,khởitạobiểuthứchàm
varhoi=function[]{
varmessage="LậptrìnhJavaScriptcănbản";
document.write[message];
};

Kết quả trình biên dịch JavaScript sẽ ném lỗi vào mặt bạn:


Uncaught TypeError: hois is not a function

Đối với cách khai báo và sử dụng như dưới đây cũng không được chấp nhận:


//Gọihàm
hois[];
hoi[];
//Biểuthứchàm
varhois=functionhoi[]{
varmessage="LậptrìnhJavaScriptcănbản";
document.write[message];
};

2. So sánh thứ tự ưu tiên trong cơ chế hoisting


2.1. Gán biến ưu tiên hơn khai báo hàm


Trong cơ chế hoisting, phép gán biến nó sẽ có độ ưu tiên cao hơn khai báo hàm:


DOCTYPEhtml>
//Khaibáovàkhởitạobiếnmessage
varmessage="Đâylàbiếnchuỗi";

//Khaibáohàmmessage[trùngtên]
functionmessage[]{
document.write["Đâylàhàm"];
}
//inrakiểucủamessage
document.write[typeofmessage];


Khi chạy trên trình duyệt, ta sẽ được kết quả là string.

Điều này chứng to việc cố gắng ghi đè biến message bằng Khai báo hàm đã không thành công.


2.2. Biểu thức hàm ưu tiên hơn gán biến


Ta cố tính khai báo và khởi tạo một biến message.

Sau đó lại tạo ra một biểu thức hàm có tên là message.



DOCTYPEhtml>
//Khaibáobiến
varmessage="Đâylàbiến";
//Khaibáohàm
varmessage=function[]{
document.write["Đâylàhàm"];
}

//inrakiểucủamessage
document.write[typeofmessage];


Trong trường hợp Khai báo hàm thì kết quả là string.

Còn khi là Biểu thức hàm như ví dụ này thì sao?

Khi chạy trên trình duyệt ta có kết quả: function


2.3. Khai báo hàm ưu tiên hơn khai báo biến


Trong cơ chế hoisting, nó phép khai báo hàmcó độ ưu tiên cao hơn khai báo biến, ví dụ:

DOCTYPEhtml>
//Khaibáobiến
varmessage;
//Khaibáohàm
functionmessage[]{
document.write["Đâylàhàm"];
}
//inrakiểucủamessage
document.write[typeofmessage];


Kết quả sẽ in ra là function, chứng tỏ khai báo hàm ưu tiên hơn là khai báo biến.

3. Đối phó với vấn đề hoisting như thế nào?


Bởi vì cơ chế hoisting khiến chúng ta quản lý code JS khá là khó khăn.

Và khó có thể hiểu được thực sự vấn đề đang gì xảy ra nếu bạn quản lý một dự án lớn, phức tạp.

Và để đối phó với vấn đề này cũng khá đơn giản.


  • Cách #1: Bạn phải hiểu rõ về nó [Cách này hơi hơi khó thì phải :v]
  • Cách #2: Hạn chế hành vi hoisting

Để hạn chế hành vi hoisting trong JavaScript bạn chỉ cần bật chế độ use strict.

> Đọc thêm: Strict Mode trong JavaScript

Hoặc sử dụng từ khóa let hoặc const khi khai báo bất kỳ một biến nào.

Mặc dù biến khai báo bằng từ khóa let hoặc const vẫn được đưa lên trên đầu. Nhưng nó không cho phép sử dụng cho đến khi được khai báo.

Bởi vì, khi sử dụng biến let hoặc const trước khi nó được khai báo ngay lập tức dẫn đến một lỗiReferenceError.



//Sửdụngtrướckhikhaibáo
console.log[thuongHieu];

//Khaibáobiếnsửdụngtừkhóalet
letthuongHieu;

Kết quả:


Uncaught ReferencError: Cannot access 'thuongHieu' before initialization

Vì thế, bạn không thể sử dụng biến một cách lung tung được.

Đi vào nề nếp, quy tắc thì sau đó bạn không cần phải nhớ bạn đã làm gì.

Cứ theo quy tắc để mà lần ra thôi.

> Ghi chú: Vấn đề này sẽ không gặp phải đối với một ngôn ngữ chặt chẽ như JAVA. Bởi vì ngay từ đầu JAVA đã được thiết kế để dành cho những dự án lớn, thiết kế tốt cho việc duy trì, mở rộng dự án trong tương lai. HỌC JAVA ngay nếu bạn thích những thứ gì chặt chẽ, quy củ.


Tổng kết


Như vậy là hôm nay mình đã giúp bạn tìm hiểu về hoisting trong JavaScript. Đây là một vấn đề rất quan trọng, hiểu và nắm giữ nó thì bạn mới biết thực sự JavaScript hoạt động như thế nào.

Việc nắm bắt hoisting trong JS cũng khá khó. Nhưng hãy luyện tập, THỬ - SAI để ngộ ra dần dần bạn nhé.

Hẹn gặp lại bạn ở bài học tiếp theo.

Chủ Đề