Bạn có phải khai báo các biến trong javascript không?

Không cần phải khai báo tất cả các vars javascript một lần ở đầu hàm và đã lâu rồi

Quay lại những ngày xưa tồi tệ của javascript, mọi người đột nhiên bắt đầu viết tất cả mã của họ như thế này

Quy tắc ở đây là

  • Chỉ một câu lệnh var cho mỗi hàm
  • Câu lệnh var đó phải ở đầu hàm

Phong cách này đã trở nên phổ biến sau JavaScript của Douglas Crockford. Các bộ phận tốt

JavaScript có cú pháp C, nhưng các khối của nó không có phạm vi. Vì vậy, quy ước rằng các biến nên được khai báo ở lần sử dụng đầu tiên thực sự là một lời khuyên tồi trong JavaScript. JavaScript có phạm vi hàm, nhưng không có phạm vi khối, vì vậy tôi khai báo tất cả các biến của mình ở đầu mỗi hàm. JavaScript cho phép các biến được khai báo sau khi chúng được sử dụng. Điều đó giống như một lỗi đối với tôi và tôi không muốn viết các chương trình trông giống như lỗi

Và cho đến ngày nay, jslint, công cụ linting của Crockford, sẽ bị lỗi theo mặc định nếu bạn không tuân theo quy tắc này

Nhưng tại sao?

Lý do là, như Crockford đề cập. JavaScript cho phép các biến được khai báo sau khi chúng được sử dụng. Được mệnh danh là “biến nâng”, điều này có khả năng tạo ra các lỗi tinh vi và nguy hiểm

Mã này xuất ra “10” một cách giả tạo, bởi vì var total có tác dụng cẩu lên và khai báo totalundefined ở đầu hàm, khiến nó không kiểm tra được tính trung thực ở dòng 8

Nhưng quy tắc của Crockford có thực sự cần thiết nữa không?

Giải pháp 1. Sử dụng let

Sử dụng ES2015/ES6, trong nút hoặc thông qua babel, chúng tôi chỉ có thể viết mã của mình để sử dụng let thay vì var, cung cấp phạm vi khối biến của chúng tôi và không kéo chúng lên

Điều này hoàn toàn tránh được vấn đề và có nghĩa là không còn động cơ nào để xác định nhiều biến cho mỗi câu lệnh let hoặc nhóm tất cả các định nghĩa biến của chúng ở đầu hàm — mặc dù vì lý do nào đó bạn vẫn thấy mọi người làm điều này, ngay cả với let

Nhưng còn javascript hiện tại của tôi, mà tôi chưa chuyển đổi sang ES2015 thì sao?

Giải pháp 2. sử dụng eslint

Eslint phát hiện các loại lỗi cẩu thả khác nhau cách xa một dặm (với cấu hình mặc định của nó), bằng cách đưa ra lỗi nếu

  • Bạn khai báo lại một biến đã được khai báo
  • Bạn cố gắng sử dụng một biến trước khi nó được khai báo

Với hai quy tắc đơn giản này, tôi thách thức bất kỳ ai phát hiện ra lỗi do cẩu biến, ngay cả khi bạn tiếp tục sử dụng var

Và nếu bạn đang sử dụng ES2015 trở lên, tôi thậm chí còn khuyên bạn nên sử dụng eslint để ngăn hoàn toàn việc sử dụng var và buộc let

Giải pháp 3. Tiếp tục sử dụng jslint (nếu bạn phải)

Ngay cả jslint cũng sẽ phàn nàn nếu bạn sử dụng một biến nằm ngoài phạm vi hoặc khai báo lại một biến từ phạm vi bên ngoài

Với điều này, đó là một bí ẩn đối với tôi tại sao jslint vẫn thực thi quy tắc var đơn theo mặc định. Ít nhất, có một tùy chọn total0 mà bạn có thể sử dụng để tắt quy tắc đó

Tóm lại là. làm ơn, làm ơn ngừng viết mã với một loạt các biến được khai báo trong một câu lệnh duy nhất ở đầu hàm của bạn. Nó khó bảo trì hơn, đọc kém rõ ràng hơn và theo tôi, nó có nhiều khả năng tạo ra lỗi hơn là ngăn chặn vấn đề mà nó đang cố gắng giải quyết. Chẳng hạn như nguy cơ vô tình tạo các biến toàn cục nếu bạn quên thêm một số biến mới vào câu lệnh var duy nhất của mình, nếu bạn chưa sử dụng một kẻ nói dối

Rõ ràng, luôn luôn sử dụng một kẻ nói dối (hãy an toàn nhé các con), bởi vì những kẻ nói dối, kể cả của Crockford, làm mất hiệu lực nhu cầu thực hiện các vars đơn lẻ và nắm bắt biến cẩu trước khi nó xảy ra. Nhưng nếu bạn không hoặc không thể, thay vì tuân theo quy ước var đơn lẻ, hãy làm theo các quy tắc này và bạn sẽ có thời gian tốt hơn nhiều

  • Không bao giờ khai báo lại một var từ phạm vi cha hoặc sử dụng cùng một tên hai lần
  • Không bao giờ tham chiếu một biến cục bộ trước khi nó được khai báo

Giờ đây, bạn có thể tìm hiểu cách phát hiện các sự cố cẩu biến trước khi chúng xảy ra, thay vì chỉ tuân theo một cách mù quáng một quy ước đơn-var khó hiểu

Một biến là một tên được liên kết với một giá trị; . Biến cho phép bạn lưu trữ và thao tác dữ liệu trong chương trình của mình. Ví dụ: dòng JavaScript sau đây gán giá trị

var sum = i + 3;
7 cho một biến có tên
var sum = i + 3;
8

i = 2;

Và dòng sau thêm

var sum = i + 3;
9 vào
var sum = i + 3;
8 và gán kết quả cho một biến mới,
i = 10;
i = "ten";
1

var sum = i + 3;

Hai dòng mã này trình bày mọi thứ bạn cần biết về các biến. Tuy nhiên, để hiểu hết cách hoạt động của biến trong JavaScript, bạn cần nắm vững thêm một số khái niệm. Thật không may, những khái niệm này đòi hỏi nhiều hơn một vài dòng mã để giải thích. Phần còn lại của chương này giải thích cách gõ, khai báo, phạm vi, nội dung và độ phân giải của biến. Nó cũng khám phá việc thu gom rác và tính đối ngẫu của biến/thuộc tính. []

Một điểm khác biệt quan trọng giữa JavaScript và các ngôn ngữ như Java và C là JavaScript không được gõ. Điều này một phần có nghĩa là biến JavaScript có thể chứa giá trị của bất kỳ kiểu dữ liệu nào, không giống như biến Java hoặc C, chỉ có thể chứa một loại dữ liệu cụ thể mà nó được khai báo. Ví dụ: trong JavaScript, việc gán một số cho một biến và sau đó gán một chuỗi cho biến đó là hoàn toàn hợp pháp

i = 10;
i = "ten";

Trong C, C++, Java hoặc bất kỳ ngôn ngữ được gõ mạnh nào khác, mã như thế này là bất hợp pháp

Một tính năng liên quan đến việc thiếu gõ JavaScript là ngôn ngữ này thuận tiện và tự động chuyển đổi các giá trị từ loại này sang loại khác, khi cần thiết. Ví dụ: nếu bạn cố nối một số vào một chuỗi, JavaScript sẽ tự động chuyển đổi số đó thành chuỗi tương ứng để có thể nối thêm. Chuyển đổi kiểu dữ liệu được trình bày chi tiết trong Chương 3

JavaScript rõ ràng là một ngôn ngữ đơn giản hơn để được untyped. Ưu điểm của các ngôn ngữ được gõ mạnh như C ++ và Java là chúng thực thi các thực hành lập trình nghiêm ngặt, giúp dễ dàng viết, bảo trì và sử dụng lại các chương trình dài, phức tạp. Vì nhiều chương trình JavaScript là các tập lệnh ngắn hơn nên sự chặt chẽ này là không cần thiết và chúng tôi được hưởng lợi từ cú pháp đơn giản hơn

Trước khi bạn sử dụng một biến trong chương trình JavaScript, bạn phải khai báo nó. [] Các biến được khai báo với từ khóa

i = 10;
i = "ten";
2, như thế này

var i;
var sum;

Bạn cũng có thể khai báo nhiều biến với cùng một từ khóa

i = 10;
i = "ten";
2

var i, sum;

Và bạn có thể kết hợp khai báo biến với khởi tạo biến

var message = "hello";
var i = 0, j = 0, k = 0;

Nếu bạn không chỉ định giá trị ban đầu cho một biến bằng câu lệnh

i = 10;
i = "ten";
2, thì biến đó được khai báo, nhưng giá trị ban đầu của nó là
i = 10;
i = "ten";
5 cho đến khi mã của bạn lưu giá trị vào đó

Lưu ý rằng câu lệnh

i = 10;
i = "ten";
2 cũng có thể xuất hiện như một phần của vòng lặp
i = 10;
i = "ten";
7 và
i = 10;
i = "ten";
8 (được giới thiệu trong Chương 6), cho phép bạn khai báo ngắn gọn biến vòng lặp như một phần của chính cú pháp vòng lặp. Ví dụ

for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");

Các biến được khai báo với

i = 10;
i = "ten";
2 là vĩnh viễn. cố gắng xóa chúng bằng toán tử
var i;
var sum;
0 gây ra lỗi. (Toán tử
var i;
var sum;
0 được giới thiệu trong Chương 5. )

Tuyên bố lặp đi lặp lại và bỏ qua

Việc khai báo một biến nhiều lần với câu lệnh

i = 10;
i = "ten";
2 là hợp pháp và vô hại. Nếu khai báo lặp lại có một bộ khởi tạo, thì nó hoạt động như thể nó chỉ đơn giản là một câu lệnh gán

Nếu bạn cố đọc giá trị của một biến không được khai báo, JavaScript sẽ tạo ra lỗi. Nếu bạn gán một giá trị cho một biến mà bạn chưa khai báo với

i = 10;
i = "ten";
2, JavaScript sẽ ngầm khai báo biến đó cho bạn. Tuy nhiên, lưu ý rằng các biến được khai báo ngầm định luôn được tạo dưới dạng biến toàn cục, ngay cả khi chúng được sử dụng trong phần thân của hàm. Để ngăn việc tạo một biến toàn cục (hoặc sử dụng một biến toàn cục hiện có) khi bạn định tạo một biến cục bộ để sử dụng trong một hàm, bạn phải luôn sử dụng câu lệnh
i = 10;
i = "ten";
2 trong thân hàm. Tốt nhất là sử dụng
i = 10;
i = "ten";
2 cho tất cả các biến, cho dù toàn cục hay cục bộ. (Sự khác biệt giữa biến cục bộ và biến toàn cục sẽ được khám phá chi tiết hơn trong phần tiếp theo. )

Phạm vi của một biến là vùng chương trình của bạn mà nó được định nghĩa. Một biến toàn cầu có phạm vi toàn cầu; . Mặt khác, các biến được khai báo trong hàm chỉ được định nghĩa trong phần thân của hàm. Chúng là các biến cục bộ và có phạm vi cục bộ. Các tham số của hàm cũng được tính là biến cục bộ và chỉ được xác định trong phần thân của hàm

Trong phần thân của hàm, biến cục bộ được ưu tiên hơn biến toàn cục có cùng tên. Nếu bạn khai báo một biến cục bộ hoặc tham số hàm có cùng tên với biến toàn cục, bạn sẽ ẩn biến toàn cục một cách hiệu quả. Ví dụ: đoạn mã sau in ra từ âlocalâ

var scope = "global";         // Declare a global variable
function checkscope( ) {
    var scope = "local";      // Declare a local variable with the same name
    document.write(scope);    // Use the local variable, not the global one
}
checkscope( );                 // Prints "local"

Mặc dù bạn có thể không cần sử dụng câu lệnh

i = 10;
i = "ten";
2 khi viết mã trong phạm vi toàn cầu, nhưng bạn phải luôn sử dụng
i = 10;
i = "ten";
2 để khai báo các biến cục bộ. Cân nhắc điều gì sẽ xảy ra nếu bạn không

scope = "global";             // Declare a global variable, even without var
function checkscope( ) {
    scope = "local";          // Oops! We just changed the global variable
    document.write(scope);    // Uses the global variable
    myscope = "local";        // This implicitly declares a new global variable
    document.write(myscope);  // Uses the new global variable
}
checkscope( );                 // Prints "locallocal"
document.write(scope);        // This prints "local"
document.write(myscope);      // This prints "local"

Nói chung, các hàm không biết biến nào được định nghĩa trong phạm vi toàn cục hoặc chúng đang được sử dụng để làm gì. Do đó, nếu một hàm sử dụng biến toàn cục thay vì biến cục bộ, thì nó có nguy cơ thay đổi giá trị mà một số phần khác của chương trình dựa vào đó. May mắn thay, tránh vấn đề này là đơn giản. khai báo tất cả các biến với

i = 10;
i = "ten";
2

Các định nghĩa hàm có thể được lồng vào nhau. Mỗi hàm có phạm vi cục bộ riêng, vì vậy có thể có nhiều lớp phạm vi cục bộ lồng nhau. Ví dụ

var scope = "global scope";          // A global variable
function checkscope( ) {
    var scope = "local scope";       // A local variable
    function nested( ) {
        var scope = "nested scope";  // A nested scope of local variables
        document.write(scope);       // Prints "nested scope"
    }
    nested( );
}
checkscope( );

Lưu ý rằng không giống như C, C++ và Java, JavaScript không có phạm vi cấp khối. Tất cả các biến được khai báo trong một hàm, bất kể chúng được khai báo ở đâu, đều được định nghĩa trong toàn bộ hàm. Trong đoạn mã sau, các biến

var sum = i + 3;
8,
var i, sum;
0 và
var i, sum;
1 đều có cùng phạm vi. cả ba đều được định nghĩa trong toàn bộ phần thân của hàm. Điều này sẽ không xảy ra nếu mã được viết bằng C, C++ hoặc Java

var sum = i + 3;
0

Quy tắc rằng tất cả các biến được khai báo trong một hàm được xác định trong toàn bộ hàm có thể gây ra kết quả đáng ngạc nhiên. Đoạn mã sau minh họa điều này

var sum = i + 3;
1

Bạn có thể nghĩ rằng lệnh gọi đầu tiên đến

var i, sum;
2 sẽ hiển thị âglobalâ, bởi vì câu lệnh
i = 10;
i = "ten";
2 khai báo biến cục bộ chưa được thực thi. Tuy nhiên, do các quy tắc phạm vi, đây không phải là điều xảy ra. Biến cục bộ được xác định trong toàn bộ phần thân của hàm, có nghĩa là biến toàn cục cùng tên bị ẩn trong hàm. Mặc dù biến cục bộ được định nghĩa xuyên suốt nhưng nó không thực sự được khởi tạo cho đến khi câu lệnh
i = 10;
i = "ten";
2 được thực thi. Do đó, hàm
var i, sum;
5 trong ví dụ trước tương đương với hàm sau

var sum = i + 3;
2

Ví dụ này minh họa lý do tại sao thực hành lập trình tốt là đặt tất cả các khai báo biến của bạn cùng nhau khi bắt đầu bất kỳ hàm nào

Không xác định so với Chưa được chỉ định

Các ví dụ trong phần trước thể hiện một điểm tinh tế trong lập trình JavaScript. có hai loại biến không xác định khác nhau. Loại biến không xác định đầu tiên là loại chưa bao giờ được khai báo. Việc cố gắng đọc giá trị của một biến không được khai báo như vậy sẽ gây ra lỗi thời gian chạy. Các biến không được khai báo là không xác định vì đơn giản là chúng không tồn tại. Như đã mô tả trước đó, việc gán giá trị cho một biến không được khai báo không gây ra lỗi;

Loại thứ hai của biến không xác định là biến đã được khai báo nhưng chưa bao giờ được gán giá trị cho nó. Nếu bạn đọc giá trị của một trong các biến này, bạn sẽ nhận được giá trị mặc định của nó,

i = 10;
i = "ten";
5. Loại biến không xác định này có thể được gọi là không được gán một cách hữu ích hơn, để phân biệt nó với loại biến không xác định nghiêm trọng hơn thậm chí không được khai báo và không tồn tại

Đoạn mã sau minh họa một số khác biệt giữa các biến thực sự không xác định và chỉ đơn thuần là các biến chưa được gán

var sum = i + 3;
3

Các loại nguyên thủy và các loại tham chiếu

Chủ đề tiếp theo chúng ta cần xem xét là nội dung của các biến. Chúng ta thường nói rằng các biến có hoặc chứa các giá trị. Nhưng chúng chứa đựng những gì? . Các loại có thể được chia thành hai nhóm. kiểu nguyên thủy và kiểu tham chiếu. Các số, giá trị boolean và các loại

var i, sum;
7 và
i = 10;
i = "ten";
5 là nguyên thủy. Các đối tượng, mảng và hàm là các kiểu tham chiếu

Kiểu nguyên thủy có kích thước cố định trong bộ nhớ. Ví dụ: một số chiếm tám byte bộ nhớ và giá trị boolean có thể được biểu thị chỉ bằng một bit. Loại số là lớn nhất trong các loại nguyên thủy. Nếu mỗi biến JavaScript dự trữ tám byte bộ nhớ, thì biến đó có thể trực tiếp giữ bất kỳ giá trị nguyên thủy nào. []

Tuy nhiên, các loại tham chiếu là một vấn đề khác. Ví dụ, các đối tượng có thể có độ dài bất kỳ. chúng không có kích thước cố định. Điều này cũng đúng với mảng. một mảng có thể có bất kỳ số phần tử nào. Tương tự, một hàm có thể chứa bất kỳ số lượng mã JavaScript nào. Vì các loại này không có kích thước cố định nên giá trị của chúng không thể được lưu trữ trực tiếp trong tám byte bộ nhớ được liên kết với mỗi biến. Thay vào đó, biến lưu trữ một tham chiếu đến giá trị. Thông thường, tham chiếu này là một số dạng con trỏ hoặc địa chỉ bộ nhớ. Bản thân nó không phải là giá trị dữ liệu, nhưng nó báo cho biến biết nơi cần tìm để tìm giá trị

Sự khác biệt giữa các kiểu nguyên thủy và tham chiếu là một điều quan trọng vì chúng hoạt động khác nhau. Xem xét đoạn mã sau sử dụng số (kiểu nguyên thủy)

var sum = i + 3;
4

Không có gì đáng ngạc nhiên về mã này. Bây giờ hãy xem điều gì sẽ xảy ra nếu chúng ta thay đổi mã một chút để nó sử dụng mảng (một kiểu tham chiếu) thay vì số

var sum = i + 3;
5

Nếu kết quả này có vẻ không ngạc nhiên với bạn, thì bạn đã quá quen thuộc với sự khác biệt giữa kiểu nguyên thủy và kiểu tham chiếu. Nếu nó có vẻ đáng ngạc nhiên, hãy xem kỹ dòng thứ hai. Lưu ý rằng đó là tham chiếu đến giá trị mảng, không phải chính mảng đó, được gán trong câu lệnh này. Sau dòng mã thứ hai đó, chúng ta vẫn chỉ có một đối tượng mảng;

Nếu sự phân biệt kiểu nguyên thủy so với kiểu tham chiếu là mới đối với bạn, chỉ cần cố gắng ghi nhớ nội dung của biến. Các biến giữ các giá trị thực của các kiểu nguyên thủy, nhưng chúng chỉ chứa các tham chiếu đến các giá trị của các kiểu tham chiếu. Hành vi khác nhau của các kiểu nguyên thủy và tham chiếu được khám phá chi tiết hơn trong

Bạn có thể nhận thấy rằng tôi đã không chỉ định chuỗi là kiểu nguyên thủy hay kiểu tham chiếu trong JavaScript. Chuỗi là một trường hợp bất thường. Chúng có kích thước thay đổi, vì vậy rõ ràng chúng không thể được lưu trữ trực tiếp trong các biến có kích thước cố định. Để đạt hiệu quả, bạn sẽ mong đợi JavaScript sao chép các tham chiếu đến chuỗi chứ không phải nội dung thực của chuỗi. Mặt khác, các chuỗi hoạt động giống như một kiểu nguyên thủy theo nhiều cách. Câu hỏi liệu chuỗi là kiểu nguyên thủy hay kiểu tham chiếu thực sự là tranh luận vì chuỗi là bất biến. không có cách nào để thay đổi nội dung của một giá trị chuỗi. Điều này có nghĩa là bạn không thể xây dựng một ví dụ giống như ví dụ trước để chứng minh rằng các mảng được sao chép theo tham chiếu. Cuối cùng, việc bạn coi chuỗi là một kiểu tham chiếu bất biến hoạt động như một kiểu nguyên thủy hay một kiểu nguyên thủy được triển khai với hiệu quả bên trong của một kiểu tham chiếu không quan trọng lắm.

Các loại tham chiếu không có kích thước cố định; . Như chúng ta đã thảo luận, các biến không trực tiếp giữ các giá trị tham chiếu. Giá trị được lưu trữ tại một số vị trí khác và các biến chỉ giữ một tham chiếu đến vị trí đó. Bây giờ, hãy tập trung ngắn gọn vào việc lưu trữ giá trị thực tế

Vì các chuỗi, đối tượng và mảng không có kích thước cố định nên bộ nhớ cho chúng phải được phân bổ động khi kích thước được biết. Mỗi khi chương trình JavaScript tạo một chuỗi, mảng hoặc đối tượng, trình thông dịch phải cấp phát bộ nhớ để lưu trữ thực thể đó. Bất cứ khi nào bộ nhớ được cấp phát động như thế này, thì cuối cùng nó phải được giải phóng để sử dụng lại, nếu không trình thông dịch JavaScript sẽ sử dụng hết bộ nhớ khả dụng trên hệ thống và gặp sự cố

Trong các ngôn ngữ như C và C++, bộ nhớ phải được giải phóng thủ công. Lập trình viên có trách nhiệm theo dõi tất cả các đối tượng được tạo và hủy chúng (giải phóng bộ nhớ) khi chúng không còn cần thiết. Đây có thể là một nhiệm vụ khó khăn và thường là nguồn gốc của lỗi

Thay vì yêu cầu phân bổ thủ công, JavaScript dựa trên một kỹ thuật gọi là thu gom rác. Trình thông dịch JavaScript có thể phát hiện khi một đối tượng sẽ không bao giờ được chương trình sử dụng nữa. Khi nó xác định rằng một đối tượng không thể truy cập được (i. e. , không còn cách nào để tham chiếu đến nó bằng cách sử dụng các biến trong chương trình), nó biết rằng đối tượng không còn cần thiết nữa và bộ nhớ của nó có thể được lấy lại. Hãy xem xét các dòng mã sau đây, ví dụ

var sum = i + 3;
6

Sau khi mã này chạy, chuỗi ban đầu âhelloâ không thể truy cập được nữa; . Hệ thống phát hiện thực tế này và giải phóng không gian lưu trữ để tái sử dụng

Bộ sưu tập rác là tự động và vô hình đối với lập trình viên. Bạn chỉ cần biết đủ về thu gom rác để tin rằng nó hoạt động. để biết bạn có thể tạo tất cả các đối tượng rác bạn muốn và hệ thống sẽ dọn sạch sau khi bạn

Bây giờ bạn có thể nhận thấy rằng có rất nhiều điểm tương đồng trong JavaScript giữa các biến và thuộc tính của các đối tượng. Cả hai đều được gán theo cùng một cách, chúng được sử dụng theo cùng một cách trong các biểu thức JavaScript, v.v. Thực sự có sự khác biệt cơ bản nào giữa biến

var sum = i + 3;
8 và thuộc tính
var sum = i + 3;
8 của một đối tượng
var message = "hello";
var i = 0, j = 0, k = 0;
1 không? . Các biến trong JavaScript về cơ bản giống như các thuộc tính đối tượng

Khi trình thông dịch JavaScript khởi động, một trong những điều đầu tiên nó thực hiện, trước khi thực thi bất kỳ mã JavaScript nào, là tạo một đối tượng toàn cầu. Thuộc tính của đối tượng này là biến toàn cục của chương trình JavaScript. Khi bạn khai báo một biến JavaScript toàn cục, điều bạn thực sự đang làm là định nghĩa một thuộc tính của đối tượng toàn cục

Trình thông dịch JavaScript khởi tạo đối tượng toàn cầu với một số thuộc tính tham chiếu đến các giá trị và hàm được xác định trước. Ví dụ: các thuộc tính

var message = "hello";
var i = 0, j = 0, k = 0;
2,
var message = "hello";
var i = 0, j = 0, k = 0;
3 và
var message = "hello";
var i = 0, j = 0, k = 0;
4 lần lượt đề cập đến số vô hạn, hàm
var message = "hello";
var i = 0, j = 0, k = 0;
5 được xác định trước và đối tượng Toán học được xác định trước. Bạn có thể đọc về các giá trị toàn cầu này trong Phần III

Trong mã cấp cao nhất (i. e. , mã JavaScript không phải là một phần của chức năng), bạn có thể sử dụng từ khóa JavaScript

var message = "hello";
var i = 0, j = 0, k = 0;
6 để chỉ đối tượng toàn cục. Trong các chức năng,
var message = "hello";
var i = 0, j = 0, k = 0;
6 có cách sử dụng khác, được mô tả trong Chương 8

Trong JavaScript phía máy khách, đối tượng Window đóng vai trò là đối tượng chung cho tất cả mã JavaScript có trong cửa sổ trình duyệt mà nó đại diện. Đối tượng Window toàn cục này có thuộc tính

var message = "hello";
var i = 0, j = 0, k = 0;
8 tự tham chiếu có thể được sử dụng thay vì
var message = "hello";
var i = 0, j = 0, k = 0;
6 để chỉ đối tượng toàn cục. Đối tượng Window xác định các thuộc tính toàn cầu cốt lõi, chẳng hạn như
var message = "hello";
var i = 0, j = 0, k = 0;
3 và
var message = "hello";
var i = 0, j = 0, k = 0;
4, cũng như các thuộc tính phía máy khách toàn cầu, chẳng hạn như
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
2 và
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
3

Biến cục bộ. Đối tượng cuộc gọi

Nếu biến toàn cục là thuộc tính của đối tượng toàn cục đặc biệt, thì biến cục bộ là gì? . Đối tượng này được gọi là đối tượng cuộc gọi. Đối tượng cuộc gọi có tuổi thọ ngắn hơn đối tượng toàn cầu, nhưng nó phục vụ cùng một mục đích. Trong khi phần thân của hàm đang thực thi, các đối số hàm và biến cục bộ được lưu trữ dưới dạng thuộc tính của đối tượng lệnh gọi này. Việc sử dụng một đối tượng hoàn toàn riêng biệt cho các biến cục bộ là điều cho phép JavaScript ngăn các biến cục bộ ghi đè lên giá trị của các biến toàn cục có cùng tên

Bối cảnh thực thi JavaScript

Mỗi khi trình thông dịch JavaScript bắt đầu thực thi một chức năng, nó sẽ tạo ngữ cảnh thực thi mới cho chức năng đó. Bối cảnh thực thi rõ ràng là bối cảnh trong đó bất kỳ đoạn mã JavaScript nào thực thi. Một phần quan trọng của ngữ cảnh là đối tượng trong đó các biến được định nghĩa. Do đó, mã JavaScript không phải là một phần của bất kỳ chức năng nào sẽ chạy trong ngữ cảnh thực thi sử dụng đối tượng toàn cục cho các định nghĩa biến. Và mọi hàm JavaScript chạy trong ngữ cảnh thực thi duy nhất của riêng nó với đối tượng lệnh gọi riêng, trong đó các biến cục bộ được xác định

Một điểm thú vị cần lưu ý là việc triển khai JavaScript có thể cho phép nhiều bối cảnh thực thi toàn cầu, mỗi bối cảnh có một đối tượng toàn cầu khác nhau. [] (Mặc dù trong trường hợp này, mỗi đối tượng toàn cục không hoàn toàn là toàn cục. ) Ví dụ rõ ràng là JavaScript phía máy khách, trong đó mỗi cửa sổ trình duyệt riêng biệt hoặc mỗi khung bên trong một cửa sổ, xác định ngữ cảnh thực thi chung riêng biệt. Mã JavaScript phía máy khách trong mỗi khung hoặc cửa sổ chạy trong ngữ cảnh thực thi riêng và có đối tượng chung của riêng nó. Tuy nhiên, các đối tượng toàn cầu phía máy khách riêng biệt này có các thuộc tính liên kết chúng. Do đó, mã JavaScript trong một khung có thể tham chiếu đến một khung khác có biểu thức

for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
4 và biến toàn cục
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
5 trong khung đầu tiên có thể được tham chiếu bởi biểu thức
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
6 trong khung thứ hai

Bạn chưa cần phải hiểu đầy đủ về cách các ngữ cảnh thực thi khung và cửa sổ riêng biệt được liên kết với nhau như thế nào trong JavaScript phía máy khách. Chủ đề đó được đề cập chi tiết trong phần thảo luận về tích hợp JavaScript với trình duyệt web ở Chương 13. Điều bạn nên hiểu bây giờ là JavaScript đủ linh hoạt để một trình thông dịch JavaScript duy nhất có thể chạy các tập lệnh trong các ngữ cảnh thực thi toàn cầu khác nhau và các ngữ cảnh đó không cần phải hoàn toàn tách biệt;

Điểm cuối cùng này yêu cầu xem xét thêm. Khi mã JavaScript trong một ngữ cảnh thực thi có thể đọc và ghi các giá trị thuộc tính cũng như thực thi các chức năng được xác định trong một ngữ cảnh thực thi khác, bạn đã đạt đến mức độ phức tạp cần xem xét các vấn đề bảo mật. Lấy JavaScript phía máy khách làm ví dụ. Giả sử cửa sổ trình duyệt A đang chạy tập lệnh hoặc chứa thông tin từ mạng nội bộ cục bộ của bạn và cửa sổ B đang chạy tập lệnh từ một trang web ngẫu nhiên nào đó trên Internet. Nói chung, bạn không muốn cho phép mã trong cửa sổ B có thể truy cập các thuộc tính của cửa sổ A. Nếu bạn cho phép nó làm như vậy, cửa sổ B có thể đọc thông tin nhạy cảm của công ty và lấy cắp thông tin đó chẳng hạn. Do đó, để chạy mã JavaScript một cách an toàn, phải có một cơ chế bảo mật ngăn chặn truy cập từ bối cảnh thực thi này sang bối cảnh thực thi khác khi không được phép truy cập như vậy. Chúng tôi sẽ trở lại chủ đề này trong

Khi chúng ta lần đầu tiên thảo luận về khái niệm phạm vi biến, tôi chỉ dựa vào định nghĩa dựa trên cấu trúc từ vựng của mã JavaScript. biến toàn cục có phạm vi toàn cục và các biến được khai báo trong hàm có phạm vi cục bộ. Nếu một định nghĩa hàm được lồng trong một hàm khác, các biến được khai báo trong hàm được lồng đó có phạm vi cục bộ lồng nhau. Bây giờ bạn đã biết rằng các biến toàn cục là các thuộc tính của một đối tượng toàn cục và các biến cục bộ là các thuộc tính của một đối tượng lệnh gọi đặc biệt, chúng ta có thể quay lại khái niệm về phạm vi biến và khái niệm hóa lại nó. Mô tả phạm vi mới này cung cấp một cách hữu ích để suy nghĩ về các biến trong nhiều ngữ cảnh;

Mỗi bối cảnh thực thi JavaScript có một chuỗi phạm vi được liên kết với nó. Chuỗi phạm vi này là một danh sách hoặc chuỗi các đối tượng. Khi mã JavaScript cần tra cứu giá trị của một biến

for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
5 (một quy trình được gọi là phân giải tên biến), nó bắt đầu bằng cách xem xét đối tượng đầu tiên trong chuỗi. Nếu đối tượng đó có một thuộc tính tên là
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
5, giá trị của thuộc tính đó được sử dụng. Nếu đối tượng đầu tiên không có thuộc tính tên là
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
5, JavaScript sẽ tiếp tục tìm kiếm với đối tượng tiếp theo trong chuỗi. Nếu đối tượng thứ hai không có thuộc tính có tên là
for(var i = 0; i < 10; i++) document.write(i, ">br<");
for(var i = 0, j=10; i < 10; i++,j--) document.write(i*j, ">br<");
for(var i in o) document.write(i, ">br<");
5, thì quá trình tìm kiếm sẽ chuyển sang đối tượng tiếp theo, v.v.

Trong mã JavaScript cấp cao nhất (i. e. , mã không chứa trong bất kỳ định nghĩa chức năng nào), chuỗi phạm vi bao gồm một đối tượng duy nhất, đối tượng toàn cục. Tất cả các biến được tra cứu trong đối tượng này. Nếu một biến không tồn tại, giá trị biến không được xác định. Tuy nhiên, trong một hàm (không lồng nhau), chuỗi phạm vi bao gồm hai đối tượng. Đầu tiên là đối tượng gọi của hàm và thứ hai là đối tượng toàn cục. Khi hàm tham chiếu đến một biến, đối tượng gọi (phạm vi cục bộ) được kiểm tra trước và đối tượng toàn cục (phạm vi toàn cục) được kiểm tra sau. Một hàm lồng nhau sẽ có ba đối tượng trở lên trong chuỗi phạm vi của nó. minh họa quá trình tra cứu tên biến trong chuỗi phạm vi của hàm

Có cần khai báo các biến JS không?

Luôn khai báo các biến JavaScript bằng var , let hoặc const . Từ khóa var được sử dụng trong tất cả các mã JavaScript từ 1995 đến 2015. Từ khóa let và const đã được thêm vào JavaScript vào năm 2015. Nếu bạn muốn mã của mình chạy trong các trình duyệt cũ hơn, bạn phải sử dụng var.

Điều gì xảy ra nếu bạn không khai báo một biến trong JavaScript?

Nếu bạn không khai báo một biến một cách rõ ràng, JavaScript sẽ khai báo biến đó một cách ngầm định cho bạn .

Chúng ta có thể sử dụng một biến mà không cần khai báo nó không?

Một biến không thể được sử dụng trong chương trình trừ khi nó đã được khai báo .