Tôi có thể khai báo biến bên ngoài hàm javascript không?

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ưới

Quy 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ình

Như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 nhau

Mộ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ă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 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ài

Bâ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 ra

Loạ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'];   
1

Bâ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 ra

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'];

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ăng

Trong 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ày

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]

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ạn

Ghi 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ụ JavaScript

var 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
}
0

Xem 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
}
1

Xem 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[];
2

Chứ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
}
2

Cá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ăng

Khai 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
}
3

Xem 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
}
4

Như 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
}
5

Xem 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
}
6

Ghi 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
}
7

Xem 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
}
8

Xem 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
}
9

Xem 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áo

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[];
0

Xem 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ớ

  • 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 biến nằm trong phạm vi chức năng
  • 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 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]
  • 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;]

Bạn có thể định nghĩa một biến bên ngoài một hàm không?

Trong Python và hầu hết các ngôn ngữ lập trình, các biến được khai báo bên ngoài hàm được gọi là biến toàn cục . Bạn có thể truy cập các biến như vậy bên trong và bên ngoài một hàm, vì chúng có phạm vi toàn cục.

Làm cách nào để sử dụng một biến được khai báo trong hàm JavaScript khác?

1. Khai báo biến bên ngoài các hàm đó mà cả hai hàm đó đều có thể truy cập . 2. Truyền giá trị từ hàm này sang hàm khác dưới dạng đối số.

Chủ Đề