Lưu trữ các giá trị trong các biến là một khái niệm cơ bản trong lập trình. “Phạm vi” của một biến xác định khi nào nó có và không có sẵn trong chương trình của bạn. Hiểu phạm vi biến trong JavaScript là một trong những chìa khóa để xây dựng nền tảng vững chắc cho ngôn ngữ này
Bài viết này sẽ giải thích cách hệ thống phạm vi của JavaScript hoạt động. Bạn sẽ tìm hiểu về các cách khác nhau để khai báo biến, sự khác biệt giữa phạm vi cục bộ và phạm vi toàn cầu, và về một thứ gọi là “hoisting” — một mẹo vặt trong JavaScript có thể biến một khai báo biến trông có vẻ ngây thơ thành một lỗi tinh vi
Phạm vi biến đổi
Trong JavaScript, phạm vi của một biến được kiểm soát bởi vị trí khai báo biến và nó xác định phần chương trình mà một biến cụ thể có thể truy cập được
Hiện tại, có 3 cách khai báo biến trong JavaScript. bằng cách sử dụng từ khóa
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1 cũ và bằng cách sử dụng từ khóa var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3 mới. Trước ES6, sử dụng từ khóa var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1 là cách duy nhất để khai báo một biến, nhưng bây giờ chúng ta có thể sử dụng var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3, những từ khóa này có quy tắc chặt chẽ hơn và giúp mã ít bị lỗi hơn. Chúng ta sẽ khám phá sự khác biệt giữa cả ba từ khóa bên dướiQuy tắc phạm vi khác nhau giữa các ngôn ngữ. JavaScript có hai phạm vi. toàn cầu và địa phương. Phạm vi địa phương có hai biến thể. phạm vi chức năng cũ và phạm vi khối mới được giới thiệu với ES6. Điều đáng chú ý là phạm vi chức năng thực sự là một loại phạm vi khối đặc biệt
Phạm vi toàn cầu
Trong một tập lệnh, phạm vi ngoài cùng là phạm vi toàn cầu. Bất kỳ biến nào được khai báo trong phạm vi này đều trở thành biến toàn cục và có thể truy cập từ mọi nơi trong chương trình
// Global Scope
const name = "Monique";
function sayHi[] {
console.log[`Hi ${name}`];
}
sayHi[];
// Hi Monique
Như ví dụ đơn giản này cho thấy, biến
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
7 là toàn cầu. Nó được xác định trong phạm vi toàn cầu và có thể truy cập được trong suốt chương trìnhNhưng điều này có vẻ tiện dụng, nhưng việc sử dụng các biến toàn cục không được khuyến khích trong JavaScript. Ví dụ, điều này là do chúng có khả năng bị ghi đè bởi các tập lệnh khác hoặc từ nơi khác trong chương trình của bạn
Phạm vi địa phương
Bất kỳ biến nào được khai báo bên trong một khối đều thuộc về khối cụ thể đó và trở thành biến cục bộ
Hàm trong JavaScript xác định phạm vi cho các biến được khai báo bằng cách sử dụng
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1, var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3. Bất kỳ biến nào được khai báo trong hàm đó chỉ có thể truy cập được từ hàm đó và mọi hàm lồng nhauMột khối mã [
function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
1, function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
2, v.v. ] chỉ xác định phạm vi cho các biến được khai báo bằng từ khóa var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3. Từ khóa var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1 được giới hạn trong phạm vi chức năng, nghĩa là phạm vi mới chỉ có thể được tạo bên trong các chức năngTừ khóa
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3 có phạm vi khối, tạo phạm vi cục bộ mới cho bất kỳ khối nào mà chúng được khai báo. Bạn cũng có thể xác định các khối mã độc lập trong JavaScript và chúng cũng phân định phạm vi tương tự{
// standalone block scope
}
Phạm vi chức năng và khối có thể được lồng vào nhau. Trong tình huống như vậy, với nhiều phạm vi lồng nhau, một biến có thể truy cập được trong phạm vi của chính nó hoặc từ phạm vi bên trong. Nhưng bên ngoài phạm vi của nó, biến không thể truy cập được
Một ví dụ đơn giản để giúp trực quan hóa phạm vi
Để làm cho mọi thứ rõ ràng, hãy sử dụng một phép ẩn dụ đơn giản. Mọi quốc gia trên thế giới của chúng ta đều có biên giới. Tất cả mọi thứ bên trong các biên giới này thuộc về phạm vi của đất nước. Ở mỗi quốc gia đều có nhiều thành phố và mỗi thành phố đều có phạm vi thành phố riêng. Các quốc gia và thành phố giống như các hàm hoặc khối JavaScript. Họ có phạm vi địa phương của họ. Điều này cũng đúng với các lục địa. Mặc dù chúng có kích thước khổng lồ, nhưng chúng cũng có thể được định nghĩa là địa phương
Mặt khác, các đại dương trên thế giới không thể được định nghĩa là có phạm vi cục bộ, bởi vì chúng thực sự bao bọc tất cả các đối tượng cục bộ — lục địa, quốc gia và thành phố — và do đó, phạm vi của chúng được định nghĩa là toàn cầu. Hãy hình dung điều này trong ví dụ tiếp theo
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
Xem cây bút
Phạm vi biến đổi. 1 bởi SitePoint [@SitePoint]
trên CodePen
Ở đây, biến
function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
8 có sẵn từ hàm function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
9, vì nó đã được xác định trong phạm vi bên ngoài của hàm function testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
0. Nếu hoán đổi biến function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
8 và câu lệnh console, chúng ta sẽ nhận được function testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
2, vì chúng ta không thể tiếp cận phạm vi bên trong từ phạm vi bên ngoàiBây giờ chúng ta đã hiểu phạm vi cục bộ và phạm vi toàn cầu là gì và cách chúng được tạo, đã đến lúc tìm hiểu cách trình thông dịch JavaScript sử dụng chúng để tìm một biến cụ thể
Quay lại phép ẩn dụ đã cho, giả sử tôi muốn tìm một người bạn tên là Monique. Tôi biết rằng cô ấy sống ở Paris, vì vậy tôi bắt đầu tìm kiếm từ đó. Khi tôi không thể tìm thấy cô ấy ở Paris, tôi lên một cấp và mở rộng tìm kiếm của mình trên toàn nước Pháp. Nhưng một lần nữa, cô ấy không có ở đó. Tiếp theo, tôi mở rộng tìm kiếm của mình một lần nữa bằng cách tăng cấp độ khác. Cuối cùng, tôi tìm thấy cô ấy ở Ý, trong trường hợp của chúng tôi là phạm vi địa phương của Châu Âu
Trong ví dụ trước, bạn tôi Monique được đại diện bởi biến
function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
8. Ở dòng cuối cùng, chúng ta gọi hàm function testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
4, gọi hàm function testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
5 và cuối cùng khi hàm function testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
6 được gọi, quá trình tìm kiếm bắt đầu. Trình thông dịch JavaScript hoạt động từ phạm vi hiện đang thực thi và hoạt động theo cách của nó cho đến khi tìm thấy biến được đề cập. Nếu không tìm thấy biến trong bất kỳ phạm vi nào, một ngoại lệ sẽ được đưa raLoại tra cứu này được gọi là. Cấu trúc tĩnh của chương trình xác định phạm vi biến. Phạm vi của một biến được xác định bởi vị trí của nó trong mã nguồn và các hàm lồng nhau có quyền truy cập vào các biến được khai báo trong phạm vi bên ngoài của chúng. Bất kể một hàm được gọi từ đâu, hoặc thậm chí nó được gọi như thế nào, phạm vi từ vựng của nó chỉ phụ thuộc vào nơi hàm được khai báo
Bây giờ hãy xem phạm vi khối mới hoạt động như thế nào
function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
Xem cây bút
Phạm vi biến đổi. 2 bởi SitePoint [@SitePoint]
trên CodePen
Trong ví dụ này, chúng ta có thể thấy rằng các biến
function testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
7 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
7 được khai báo với var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 không thể truy cập được bên ngoài khối function testScope[n] {
if [true] {
const greeting = 'Hello';
let name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: ReferenceError: greeting is not defined
}
testScope['David'];
1Bây giờ, hãy thay thế
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3 và var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 bằng var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1 và xem điều gì sẽ xảy rafunction testScope[n] {
if [true] {
var greeting = 'Hello';
var name = n;
console.log[greeting + " " + name]; // output: Hello [name]
}
console.log[greeting + " " + name]; // output: Hello [name]
}
testScope['David'];
Xem cây bút
Phạm vi biến đổi. 3 bởi SitePoint [@SitePoint]
trên CodePen
Như bạn có thể thấy, khi chúng ta sử dụng từ khóa
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1, các biến có thể truy cập được trong toàn bộ phạm vi chức năngTrong JavaScript, các biến có cùng tên có thể được chỉ định ở nhiều lớp phạm vi lồng nhau. Trong tình huống như vậy, các biến cục bộ được ưu tiên hơn các biến toàn cục. Nếu bạn khai báo một biến cục bộ và một biến toàn cục có cùng tên, biến cục bộ sẽ được ưu tiên khi bạn sử dụng nó bên trong một hàm hoặc khối. Loại hành vi này được gọi là bóng tối. Nói một cách đơn giản, biến bên trong phủ bóng bên ngoài
Đó là cơ chế chính xác được sử dụng khi trình thông dịch JavaScript đang cố tìm một biến cụ thể. Nó bắt đầu ở phạm vi trong cùng đang được thực thi vào thời điểm đó và tiếp tục cho đến khi tìm thấy kết quả khớp đầu tiên, bất kể có các biến khác có cùng tên ở các cấp bên ngoài hay không. Hãy xem một ví dụ
var test = "I'm global";
function testScope[] {
var test = "I'm local";
console.log [test];
}
testScope[]; // output: I'm local
console.log[test]; // output: I'm global
Xem cây bút
Phạm vi biến đổi. 4 bởi SitePoint [@SitePoint]
trên CodePen
Ngay cả khi có cùng tên, biến cục bộ không ghi đè lên biến toàn cục sau khi thực thi hàm
var test = "I'm global";
function testScope[] {
var test = "I'm local";
console.log [test];
}
testScope[]; // output: I'm local
console.log[test]; // output: I'm global
6. Nhưng đây không phải là luôn luôn như vậy. Hãy xem xét điều nàyvar test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
Xem cây bút
Phạm vi biến đổi. 5 bởi SitePoint [@SitePoint]
trên CodePen
Lần này, biến cục bộ
var test = "I'm global";
function testScope[] {
var test = "I'm local";
console.log [test];
}
testScope[]; // output: I'm local
console.log[test]; // output: I'm global
7 ghi đè lên biến toàn cục có cùng tên. Khi chúng tôi chạy mã bên trong hàm var test = "I'm global";
function testScope[] {
var test = "I'm local";
console.log [test];
}
testScope[]; // output: I'm local
console.log[test]; // output: I'm global
6, biến toàn cục được gán lại. Nếu một biến cục bộ được gán mà không được khai báo trước với từ khóa var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1, nó sẽ trở thành biến toàn cục. Để tránh những hành vi không mong muốn như vậy, bạn phải luôn khai báo các biến cục bộ trước khi sử dụng chúng. Bất kỳ biến nào được khai báo bằng từ khóa var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1 bên trong hàm đều là biến cục bộ. Nó được coi là phương pháp hay nhất để khai báo các biến của bạnGhi chú. ở chế độ nghiêm ngặt, sẽ có lỗi nếu bạn gán giá trị cho biến mà không khai báo biến trước
cẩu
Trình thông dịch JavaScript thực hiện nhiều thao tác ở hậu trường và một trong số đó là “hoisting”. Nếu bạn không biết về hành vi “ẩn” này, nó có thể gây ra nhiều nhầm lẫn. Cách tốt nhất để suy nghĩ về hành vi của các biến JavaScript là luôn hình dung chúng bao gồm hai phần. khai báo và khởi tạo/gán
var state; // variable declaration
state = "ready"; // variable assignment
var state = "ready"; // declaration plus assignment
Trong đoạn mã trên, trước tiên chúng ta khai báo biến
var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
1, sau đó chúng ta gán giá trị var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
2 cho nó. Và trong dòng mã cuối cùng, chúng tôi thấy rằng hai bước này có thể được kết hợp. Nhưng điều bạn cần lưu ý là, mặc dù chúng có vẻ giống như một câu lệnh, nhưng trên thực tế, công cụ JavaScript coi câu lệnh đơn lẻ đó là hai câu lệnh riêng biệt, giống như trong hai dòng đầu tiên của ví dụChúng ta đã biết rằng bất kỳ biến nào được khai báo trong một phạm vi đều thuộc phạm vi đó. Nhưng điều chúng ta chưa biết là, bất kể biến được khai báo ở đâu trong một phạm vi cụ thể, tất cả các khai báo biến đều được chuyển lên đầu phạm vi của chúng [toàn cục hoặc cục bộ]. Điều này được gọi là nâng lên, vì các khai báo biến được nâng lên đầu phạm vi. Lưu ý rằng cẩu chỉ di chuyển khai báo. Bất kỳ nhiệm vụ được để lại tại chỗ. Hãy xem một ví dụ
console.log[state]; // output: undefined
var state = "ready";
Xem cây bút
Phạm vi biến đổi. 6 bởi SitePoint [@SitePoint]
trên CodePen
Như bạn có thể thấy, khi chúng tôi ghi lại giá trị của
var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
1, đầu ra là var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
4, bởi vì chúng tôi tham chiếu giá trị này trước khi gán thực tế. Bạn có thể đã mong đợi một var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
5 được ném ra, bởi vì var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
1 chưa được khai báo. Nhưng điều bạn không biết là biến được khai báo và khởi tạo với giá trị mặc định var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
4 đằng sau hậu trường. Đây là cách mã được giải thích bởi một công cụ JavaScriptvar state; // moved to the top
console.log[state];
state = "ready"; // left in place
Điều quan trọng cần lưu ý là biến không được di chuyển về mặt vật lý. Hoisting chỉ là một mô hình mô tả những gì công cụ JS thực hiện đằng sau hậu trường
Bây giờ, hãy xem cách hoisting hoạt động với biến
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2{
// standalone block scope
}
0Xem cây bút
Phạm vi biến đổi. 7 của SitePoint [@SitePoint]
trên CodePen
Trong ví dụ này, đầu ra của bảng điều khiển không phải là
var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
4, nhưng một lỗi tham chiếu được đưa ra. Tại sao? . Chúng chỉ được khởi tạo đầy đủ khi chúng thực sự được khai báo trong mã. Vì vậy, khai báo biến var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 được nâng lên nhưng không được khởi tạo với giá trị var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
4, đó là trường hợp của biến var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1. Phần từ đầu khối đến phần khai báo biến thực được gọi là Vùng chết tạm thời. Đây là một cơ chế đảm bảo thực hành viết mã tốt hơn, buộc bạn phải khai báo một biến trước khi sử dụng nó. Nếu chúng ta di chuyển câu lệnh console ra khỏi TDZ, chúng ta sẽ nhận được kết quả như mong đợi. var state; // variable declaration
state = "ready"; // variable assignment
var state = "ready"; // declaration plus assignment
5{
// standalone block scope
}
1Xem cây bút
Phạm vi biến đổi. 8 bởi SitePoint [@SitePoint]
trên CodePen
Các biến được khai báo với từ khóa
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3 có hành vi tương tự như biến var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2Chức năng
Hoisting cũng ảnh hưởng đến việc khai báo hàm. Nhưng trước khi xem một số ví dụ, trước tiên chúng ta hãy tìm hiểu sự khác biệt giữa khai báo hàm và biểu thức hàm
{
// standalone block scope
}
2Cách dễ nhất để phân biệt một khai báo hàm với một biểu thức hàm là kiểm tra vị trí của từ
var state; // variable declaration
state = "ready"; // variable assignment
var state = "ready"; // declaration plus assignment
8 trong câu lệnh. Nếu var state; // variable declaration
state = "ready"; // variable assignment
var state = "ready"; // declaration plus assignment
8 là điều đầu tiên trong câu lệnh, thì đó là một khai báo hàm. Mặt khác, đó là một biểu thức chức năngKhai báo chức năng được nâng lên hoàn toàn. Điều này có nghĩa là toàn bộ phần thân của hàm được chuyển lên trên cùng. Điều này cho phép bạn gọi một hàm trước khi nó được khai báo
{
// standalone block scope
}
3Xem cây bút
Phạm vi biến đổi. 9 bởi SitePoint [@SitePoint]
trên CodePen
Lý do mã trước hoạt động là vì công cụ JavaScript di chuyển phần khai báo của hàm
console.log[state]; // output: undefined
var state = "ready";
0 và tất cả nội dung của nó đến đầu phạm vi. Mã được giải thích như thế này{
// standalone block scope
}
4Như bạn có thể nhận thấy, chỉ phần khai báo hàm được nâng lên, còn biểu thức hàm thì không. Khi một hàm được gán cho một biến, các quy tắc cũng giống như đối với việc cẩu biến [chỉ di chuyển phần khai báo, còn phần gán được giữ nguyên]
Trong đoạn mã trên, chúng ta đã thấy rằng khai báo hàm được ưu tiên hơn khai báo biến. Và trong ví dụ tiếp theo, chúng ta sẽ thấy rằng khi chúng ta có một khai báo hàm so với một phép gán biến, cái cuối cùng sẽ được ưu tiên
{
// standalone block scope
}
5Xem cây bút
Phạm vi biến đổi. 10 bởi SitePoint [@SitePoint]
trên CodePen
Lần này, chúng tôi gọi hàm
console.log[state]; // output: undefined
var state = "ready";
0 ở dòng cuối cùng của mã, điều này sẽ thay đổi tình hình. Bây giờ chúng tôi nhận được đầu ra console.log[state]; // output: undefined
var state = "ready";
2. Đây là giao diện của nó khi được giải thích bởi công cụ JavaScript{
// standalone block scope
}
6Ghi chú. các hàm mũi tên hoạt động giống hệt với các biểu thức hàm
Các lớp học
Các khai báo lớp cũng được cẩu lên theo cách tương tự như các biến được khai báo bằng câu lệnh
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2{
// standalone block scope
}
7Xem cây bút
Phạm vi biến đổi. 11 bởi SitePoint [@SitePoint]
trên CodePen
Trong ví dụ này, chúng ta có thể thấy rằng việc sử dụng lớp
console.log[state]; // output: undefined
var state = "ready";
4 trước khi khai báo sẽ tạo ra một lỗi tham chiếu tương tự như trong các biến var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2. Để khắc phục điều này chúng ta phải sử dụng lớp console.log[state]; // output: undefined
var state = "ready";
4 sau phần khai báo{
// standalone block scope
}
8Xem cây bút
Phạm vi biến đổi. 12 bởi SitePoint [@SitePoint]
trên CodePen
Các lớp cũng có thể được tạo bằng cách sử dụng một biểu thức lớp, bằng cách sử dụng các câu lệnh khai báo biến
var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
1, var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
2 hoặc var locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
3{
// standalone block scope
}
9Xem cây bút
Phạm vi biến đổi. 13 bởi SitePoint [@SitePoint]
trên CodePen
Trong ví dụ này, chúng ta có thể thấy rằng lớp
console.log[state]; // output: undefined
var state = "ready";
4 được cẩu lên như một biểu thức hàm, nhưng nó không thể được sử dụng vì giá trị của nó là var test = "I'm global";
function testScope[] {
test = "I'm local";
console.log[test];
}
console.log[test]; // output: I'm global
testScope[]; // output: I'm local
console.log[test]; // output: I'm local [the global variable is reassigned]
4. Một lần nữa, để khắc phục điều này, chúng ta phải sử dụng lớp console.log[state]; // output: undefined
var state = "ready";
4 sau phần khai báovar locales = {
europe: function[] { // The Europe continent's local scope
var myFriend = "Monique";
var france = function[] { // France country's local scope
var paris = function[] { // The Paris city's local scope
console.log[myFriend]; // output: Monique
};
paris[];
};
france[];
}
};
locales.europe[];
0Xem cây bút
Phạm vi biến đổi. 14 bởi SitePoint [@SitePoint]
trên CodePen
Những điều cần ghi nhớ
1 biến nằm trong phạm vi chức năngvar locales = { europe: function[] { // The Europe continent's local scope var myFriend = "Monique"; var france = function[] { // France country's local scope var paris = function[] { // The Paris city's local scope console.log[myFriend]; // output: Monique }; paris[]; }; france[]; } }; locales.europe[];
- Các biến
2 vàvar locales = { europe: function[] { // The Europe continent's local scope var myFriend = "Monique"; var france = function[] { // France country's local scope var paris = function[] { // The Paris city's local scope console.log[myFriend]; // output: Monique }; paris[]; }; france[]; } }; locales.europe[];
3 nằm trong phạm vi khối [điều này cũng bao gồm các hàm]var locales = { europe: function[] { // The Europe continent's local scope var myFriend = "Monique"; var france = function[] { // France country's local scope var paris = function[] { // The Paris city's local scope console.log[myFriend]; // output: Monique }; paris[]; }; france[]; } }; locales.europe[];
- Tất cả các khai báo — lớp, hàm và biến — được nâng lên đầu phạm vi chứa, trước khi bất kỳ phần nào trong mã của bạn được thực thi
- Các chức năng được cẩu lên trước, sau đó đến các biến
- Khai báo hàm được ưu tiên hơn khai báo biến, nhưng không ưu tiên hơn các phép gán biến
Chia sẻ bài viết này
Ivaylo Gerchev
Tôi là một nhà phát triển/thiết kế web đến từ Bulgaria. Các công nghệ web yêu thích của tôi bao gồm SVG, HTML, CSS, Tailwind, JavaScript, Node, Vue và React. Khi tôi không lập trình Web, tôi thích lập trình thực tế của riêng mình;]