Javascript Hoisting là gì?

ant-weight-lifting-javascript-hoistingMột đặc điểm nhỏ của javascript nhưng đôi lúc khiến lập trình viên gặp những rắc rối khó hiểu.

Giới thiệu

Hoisting là khái niệm chỉ việc mọi khai báo biến (với từ khóa var) sẽ được chuyển lên trên cùng của hàm.

Để kiểm tra, tôi thử ví dụ sau trong Chrome Developer Tools:

(function(){
	console.log(foo);
})();

>> ReferenceError: foo is not defined

Oops! Tất nhiên là nó sẽ ra lỗi ?!@#$% Hãy thử một ví dụ khác:

(function(){
	console.log(foo);
	var foo = "hello";
})();
>> undefined

‘undefined’ thay vì lỗi? Nguyên nhân là do việc khai báo biến foo đã được chuyển lên trên cùng. Đoạn code trên thực tế được chuyển thành như sau khi chạy:

(function(){
	var foo;
	console.log(foo);
	foo = "hello";
})();
>> undefined

Tất nhiên chỉ có khai báo biến được chuyển lên đầu hàm, còn các biểu thức gán sẽ vẫn nằm tại vị trí cũ. Đối với các trường hợp biến được khai báo trong các khối lệnh như if, for, while, switch,… cũng không ngoại lệ. Đó là kết quả (hoặc cũng có thể là nguyên nhân) của việc:

javascript không có block-level scope.

(function(){
	console.log(foo);
    if(false){
		var foo = "hello";
	}
})();
>> undefined

Function Declaration và Function Expression

Trước tiên ta cần phân biệt hai khái niệm này:

// Function Declaration:
function foo(){
    // code
}
// Function Expression:
var foo = function(){
	// code
};

Cả hai phương pháp tạo hàm này đều bị ảnh hưởng bởi “hoisting”, tuy nhiên có một điểm khác nhau quan trọng giữa chúng.
Function Declaration: toàn bộ hàm sẽ được di chuyển lên đầu. Nghĩa là bạn có thể sử dụng được hàm trước khi nó được định nghĩa.
Function Expression: chỉ có phần khai báo được di chuyển lên đầu.

Và …

what does it matter meme

Để trả lời, bạn có thể xem và dự đoán đoạn code sau sẽ in gì ra màn hình:

(function(){
	var foo = 1;
	(function(){
		console.log(foo);
		var foo = 2;
	})();
	console.log(foo);
})();

Nếu chưa biết về hoisting, bạn có thể đưa ra kết quả sai. Câu trả lời chính xác là ‘undefined’. Nguyên nhân là do bạn khai báo lại (với từ khóa var) biến foo bên trong hàm con. Nếu bạn không khai báo lại, hàm con sẽ sử dụng biến foo của ngữ cảnh bên ngoài.

Một ví dụ khác với hàm:

(function(){
    console.log(foo()); // 3

    var foo = function() {
        return 1;
    };
    function foo() {
        return 2;
    };
    function foo() {
        return 3;
    };
})();
>> 3

Kết quả xuất ra có thể nằm ngoài dự đoán của bạn do hàm được định nghĩa cuối cùng sẽ ghi đè lên các hàm cùng tên được định nghĩa trước nó.

Hãy tưởng tượng bạn có một đoạn javascript rất dài, bạn khai báo biến tại những chỗ mà bạn bắt đầu dùng nó, bạn quên rằng tên biến này đã được dùng hoặc nhầm lẫn giữa các scope với nhau. Kết quả là nó nhảy ra một lỗi hoặc tệ hơn là nó … âm thầm chờ đợi và xuất hiện khi đưa cho khách hàng. Vậy một best practice mà bạn nên tập từ bây giờ là:

Luôn khai báo biến/hàm trên cùng thay vì để trình duyệt làm giùm bạn.

YinYangIt’s Blog

3 bình luận về “Javascript Hoisting là gì?

  1. Pingback: Note lại – temp – Note và share lại kiến thức

Đã đóng bình luận.