Câu hỏi Mục đích của từ khóa var là gì và khi nào tôi nên sử dụng nó (hoặc bỏ qua nó)?


CHÚ THÍCH: Câu hỏi này được hỏi từ quan điểm của ECMAScript phiên bản 3 hoặc 5. Câu trả lời có thể trở nên lỗi thời với việc giới thiệu các tính năng mới trong bản phát hành ECMAScript 6.


1390
2017-09-24 08:54


gốc


Luôn sử dụng var! Ngay cả trong phạm vi toàn cầu, nếu không bạn có thể gặp vấn đề với IE (ít nhất là phiên bản 6). Tôi đang nói điều này từ kinh nghiệm của riêng tôi. - Jamol
Khi khai báo các chuỗi var, việc đặt một dòng mới sau dấu phẩy có ảnh hưởng đến hành vi không? var x = 1, y = 2, [trở về] z = 3; - Alfabravo
Việc không sử dụng "var" cũng khiến bạn bị lộ trong trường hợp tên biến mà bạn chọn xảy ra là một biến toàn cục đã được xác định trước đó. Xem hành trình đau buồn của tôi ở đây: stackoverflow.com/questions/16704014/… - Scott C Wilson
Bài đăng blog meloncard @Ray Toal (chắc chắn đáng đọc) đã chuyển sang blog.safeshepherd.com/23/how-one-missing-var-ruined-our-launch - Hephaestus
Tôi chưa bao giờ tưởng tượng một bài thơ có thể truyền cảm hứng cho tôi xem xét cho một vấn đề có lập trình - Félix Gagnon-Grenier


Các câu trả lời:


Nếu bạn đang ở trong phạm vi toàn cầu thì không có sự khác biệt.

Nếu bạn đang ở trong một hàm thì var sẽ tạo một biến cục bộ, "no var" sẽ tra cứu chuỗi phạm vi cho đến khi nó tìm thấy biến hoặc truy cập phạm vi toàn cục (tại thời điểm đó nó sẽ tạo ra nó):

// These are both globals
var foo = 1;
bar = 2;

function()
{
    var foo = 1; // Local
    bar = 2;     // Global

    // Execute an anonymous function
    (function()
    {
        var wibble = 1; // Local
        foo = 2; // Inherits from scope above (creating a closure)
        moo = 3; // Global
    }())
}

Nếu bạn không làm bài tập thì bạn cần sử dụng var:

var x; // Declare x

1255
2017-09-24 08:55



Là "không thực sự nhiều sự khác biệt" == "Không có sự khác biệt"? - Alex
Vâng, thực sự có, có sự khác biệt :) Cho dù đó là sự khác biệt quan trọng là một câu hỏi khác. Xem câu trả lời của tôi tiếp tục xuống: stackoverflow.com/questions/1470488/… - kangax
Tôi nghĩ rằng đó có thể là điểm của Alex, đó là lý do tại sao anh ấy viết nó bằng cách sử dụng toán tử "bằng"! - James Bedford
Nó giống như bắn mình với một khẩu railgun ... Quên để đặt một 'var' trước biến của một người, và cuối cùng sửa đổi một biến một vài nơi trong chuỗi phạm vi ... Hãy thử thuyết phục một Java / C / Python / v.v. nhà phát triển JavaScript là đáng giá. Ha! C / C + + cạm bẫy nhìn đẹp bằng cách tương phản. Hãy tưởng tượng phải gỡ lỗi JavaScript ... Và một số người làm điều đó, tất nhiên. Và có rất nhiều mã (và không phải mã đơn giản, hãy nhớ bạn) được viết bằng JavaScript ... - Albus Dumbledore
Nếu bạn đang ở trong phạm vi toàn cầu thì không có sự khác biệt. >> có một sự khác biệt được giải thích trong câu trả lời dưới đây - AngularInDepth.com


Có một sự khác biệt.

var x = 1  -biến khai báo  x trong phạm vi hiện tại (còn gọi là ngữ cảnh thực thi). Nếu khai báo xuất hiện trong một hàm - một biến cục bộ được khai báo; nếu nó nằm trong phạm vi toàn cầu - một biến toàn cầu được khai báo.

x = 1, mặt khác, chỉ đơn thuần là một sự phân bổ tài sản. Đầu tiên nó cố gắng giải quyết x chống lại chuỗi phạm vi. Nếu nó tìm thấy nó ở bất kỳ đâu trong chuỗi phạm vi đó, nó thực hiện nhiệm vụ; nếu nó không tìm thấy x, chỉ sau đó nó tạo ra x thuộc tính trên một đối tượng toàn cục (là một đối tượng cấp cao nhất trong một chuỗi phạm vi).

Bây giờ, lưu ý rằng nó không khai báo một biến toàn cầu, nó tạo ra một thuộc tính toàn cục.

Sự khác biệt giữa hai là tinh tế và có thể gây nhầm lẫn trừ khi bạn hiểu rằng khai báo biến cũng tạo thuộc tính (chỉ trên một đối tượng biến) và mọi thuộc tính trong Javascript (tốt, ECMAScript) đều có các cờ nhất định mô tả các thuộc tính của chúng - ReadOnly, DontEnum và DontDelete.

Kể từ khi khai báo biến tạo thuộc tính với cờ DontDelete, sự khác biệt giữa var x = 1 và x = 1 (khi được thực hiện trong phạm vi toàn cục) là khai báo một biến cũ - tạo thuộc tính DontDelete'able, và thuộc tính sau không. Kết quả là, thuộc tính được tạo ra thông qua gán ngầm này sau đó có thể bị xóa khỏi đối tượng toàn cầu, và cái trước đây - cái được tạo ra thông qua khai báo biến - không thể bị xóa.

Nhưng đây chỉ là lý thuyết, và trong thực tế thậm chí còn có nhiều sự khác biệt giữa hai, do các lỗi khác nhau trong việc triển khai (chẳng hạn như các lỗi từ IE).

Hy vọng tất cả đều có ý nghĩa:)


[Cập nhật 2010/12/16]

Trong ES5 (ECMAScript 5; gần đây đã được chuẩn hóa, phiên bản thứ 5 của ngôn ngữ) có một cái gọi là "chế độ nghiêm ngặt" - một chế độ ngôn ngữ chọn tham gia, thay đổi chút ít hành vi của các bài tập không khai báo. Trong chế độ nghiêm ngặt, việc gán cho một số nhận dạng không khai báo là một Tham chiếuError. Lý do cho điều này là để nắm bắt các bài tập tình cờ, ngăn cản việc tạo ra các thuộc tính toàn cầu không mong muốn. Một số trình duyệt mới hơn đã bắt đầu hỗ trợ chế độ nghiêm ngặt. Xem, ví dụ, bảng compat của tôi.


697
2017-09-24 13:38



Nếu tôi nhớ lại chính xác, tôi nghĩ rằng tôi đã từng tìm cách để có thể delete biến được khai báo var với một số eval hack. Nếu tôi nhớ chính xác thì tôi sẽ đăng ở đây. - Tower
@Mageek Anh ấy có thể lấy về các biến được khai báo eval có thể xóa được. Tôi đã viết một bài đăng trên blog về điều này Một lần. - kangax
Một chút về chủ đề, nhưng đề cập đến nó ở đây để tham khảo. "let" rất giống với "var" và được hỗ trợ trong Mozilla. Sự khác biệt chính là phạm vi của một biến var là toàn bộ hàm bao quanh khi "let" bị giới hạn đối với khối của nó - mac
@kangax nếu hai dòng cuối cùng của ví dụ của Alex đã được trộn lẫn: var someObject = {} và someObject.someProperty = 5 ? Sẽ someProperty trở thành toàn cầu, trong khi đối tượng đó là tài sản của địa phương còn lại? - snapfractalpop
Tên thông số cho những gì @kangax gọi DontDelete cờ là có thể định cấu hình (= false), bạn có thể đọc về điều này liên quan đến Object.defineProperty và Object.getOwnPropertyDescriptor - Paul S.


Nói rằng đó là sự khác biệt giữa "địa phương và toàn cầu"không hoàn toàn chính xác.

Nó có thể là tốt hơn để nghĩ về nó như là sự khác biệt giữa "địa phương và gần nhất"Gần nhất chắc chắn có thể là toàn cầu, nhưng điều đó không phải lúc nào cũng đúng.

/* global scope */
var local = true;
var global = true;

function outer() {
    /* local scope */
    var local = true;
    var global = false;

    /* nearest scope = outer */
    local = !global;

    function inner() {
        /* nearest scope = outer */
        local = false;
        global = false;

        /* nearest scope = undefined */
        /* defaults to defining a global */
        public = global;
    }
}

130
2017-09-24 09:50



Không phải là phạm vi gần nhất outer nơi bạn xác định var global = false;? - Snekse
@Snekse: 'gần nhất' không áp dụng khi <code> var global = false; </ code> được khai báo. Trong tuyên bố đó, 'global' được đặt trong phạm vi outer () vì 'var' được sử dụng trong khai báo. Bởi vì 'var' không được sử dụng trong inner (), nó sẽ thay đổi giá trị ở cấp độ tiếp theo, đó là outer (). - Mitch
Tôi tự hỏi nếu bạn bình luận sẽ thay đổi nếu bạn thay đổi dòng đó var global = local; trong trường hợp phạm vi gần của địa phương sẽ là phạm vi bên ngoài "cục bộ" đang được xác định chủ động. Mặc dù nó rất lạ nếu bạn thay đổi cùng một dòng var global = globaltrong trường hợp phạm vi gần nhất khi tìm kiếm giá trị của global sẽ lên cấp ở phạm vi cửa sổ toàn cầu. - Snekse


Khi Javascript được thực hiện trong một trình duyệt, tất cả các mã của bạn được bao quanh bởi một câu lệnh with, như sau:

with (window) {
    //Your code
}

Thông tin thêm về with - MDN

var khai báo biến trong phạm vi hiện tại , không có sự khác biệt giữa khai báo var  bên trong cửa sổ và không tuyên bố gì cả.

Sự khác biệt xuất hiện khi bạn không trực tiếp bên trong cửa sổ, ví dụ: bên trong một hàm hoặc bên trong một khối.

Sử dụng var cho phép bạn ẩn các biến bên ngoài có cùng tên. Bằng cách này, bạn có thể mô phỏng biến "riêng tư", nhưng đó là một chủ đề khác.

Quy tắc chung là luôn sử dụng var, bởi vì nếu không bạn sẽ có nguy cơ giới thiệu các lỗi tinh vi.

CHỈNH SỬA: Sau khi tôi nhận được những phê bình, tôi muốn nhấn mạnh những điều sau:

  • var khai báo biến trong phạm vi hiện tại
  • Phạm vi toàn cầu là window
  • Không sử dụng var khai báo ngầm var trong phạm vi toàn cầu (cửa sổ)
  • Khai báo một biến trong phạm vi toàn cục (cửa sổ) bằng cách sử dụng var cũng giống như bỏ qua nó.
  • Khai báo một biến trong phạm vi khác với cửa sổ sử dụng var  không phải là điều tương tự như tuyên bố một biến mà không var
  • Luôn khai báo var rõ ràng vì đó là thực hành tốt

76
2017-09-24 09:17



Tôi đã không downvote bạn, nhưng phạm vi có lẽ là một từ tốt hơn so với cửa sổ. Bạn đang giải thích toàn bộ là một chút ngớ ngẩn. - Robert Harvey♦
Tôi đơn giản gọi mọi thứ với tên của nó, bạn muốn gọi nó là "phạm vi toàn cầu", nó là ok, nhưng phía client, theo quy ước, là đối tượng cửa sổ, đó là phần tử cuối cùng của chuỗi phạm vi, tại sao bạn có thể gọi mọi chức năng và mọi đối tượng trong cửa sổ mà không cần viết "cửa sổ". - kentaromiura
1 đây là một lời giải thích thực sự tốt đẹp - tôi đã không nghe thấy vấn đề var / no var đóng khung (không có ý định chơi chữ) như thế này trước đây. - doug
Hầu hết câu trả lời này không được chấp nhận với let trong ES6. - Evan Carroll
@EvanCarroll Câu trả lời này cũng không chính xác về mặt kỹ thuật vì bỏ qua var không khai báo bất kỳ biến nào, thay vào đó nó tạo ra một thuộc tính xóa được trên đối tượng toàn cầu, bên cạnh đó chế độ "sử dụng nghiêm ngặt" của ES5 hầu hết là không chính xác. t thậm chí được xem xét trong câu trả lời này kể từ khi câu hỏi không có bất kỳ tham chiếu đến phiên bản javascript (thêm ngày hôm qua) có nghĩa là tiêu chuẩn tham chiếu (tại thời điểm đó) là ECMA 262 3rd Edition. - kentaromiura


Bạn nên luôn luôn sử dụng var từ khóa để khai báo biến. Tại sao? Thực hành mã hóa tốt phải đủ lý do, nhưng tuyên bố một biến không có var từ khóa có nghĩa là nó được khai báo trong toàn cầu phạm vi (một biến như thế này được gọi là một "ngụ ý" toàn cầu). Douglas Crockford đề xuất không bao giờ sử dụng hình cầu ngụ ývà theo Nguyên tắc mã hóa JavaScript của Apple:

Bất kỳ biến nào được tạo mà không có var   từ khoá được tạo ở phạm vi toàn cầu   và không được thu gom rác khi   chức năng trả về (vì nó không   đi ra khỏi phạm vi), trình bày   cơ hội cho một rò rỉ bộ nhớ.

Vì vậy, trong ngắn hạn, luôn luôn khai báo các biến bằng cách sử dụng var từ khóa.


37
2017-09-24 09:52



"Thực hành mã hóa tốt" không bao giờ là lý do chính đáng. Nó có nghĩa là "một số người trên internet cho biết đây là cách mã của tôi nên xem xét". Điều đó thậm chí còn kém hiệu quả hơn "giáo viên của tôi nói", trừ khi ít nhất một người mơ hồ hiểu được lý do đằng sau quy tắc. - cHao
@cao Tôi nghĩ good coding practice luôn luôn là lý do đủ nếu đó là một thực hành tốt nhất được đề nghị, điều này là và bởi một số tác giả Javascript. - Chris S
@ Chris: Không, "thực hành mã hóa tốt" không phải là lý do chính nó. Các lý do nó được coi là thực hành tốt là những gì quan trọng. Trừ khi những tác giả đó cho bạn biết lý do tại sao họ giới thiệu nó, đề xuất của họ không nên mang theo bất kỳ trọng lượng nào. Nếu bạn không đồng ý với các lý do, thì bạn được tự do cân nhắc lời khuyên xấu. Và nếu bạn theo dõi nó mà không bao giờ hỏi tại sao, đó là cách bắt đầu văn hóa hàng hóa. - cHao


Dưới đây là một ví dụ khá tốt về cách bạn có thể bị loại khỏi việc không khai báo biến cục bộ var:

<script>
one();

function one()
{
    for (i = 0;i < 10;i++)
    {
        two();
        alert(i);
    }
}

function two()
{
    i = 1;
}
</script>

(i được đặt lại ở mọi lần lặp của vòng lặp, vì nó không được khai báo cục bộ trong for vòng lặp nhưng trên toàn cầu) cuối cùng dẫn đến vòng lặp vô hạn


27
2017-09-24 09:31



Yikes! Tôi chỉ có thể tưởng tượng tất cả các lỗi có thể do lỗi đánh máy đó gây ra. - BonsaiOak
Tôi tò mò, tại sao bạn đang đi qua tôi như một đối số cho hai ()? (bên trong vòng lặp for) là thừa? - kalin
Đối số được bỏ qua trong hàm two () được đóng gói trong hàm one (), vì hàm two () được định nghĩa mà không có tham số. Bạn là khá chính xác, Nó là không cần thiết vì nó đóng vai trò không. - KK.


Tôi sẽ nói nó tốt hơn để sử dụng var trong hầu hết các tình huống.

Các biến cục bộ luôn luôn nhanh hơn các biến trong phạm vi toàn cục.

Nếu bạn không sử dụng var để khai báo một biến, biến sẽ nằm trong phạm vi toàn cục.

Để biết thêm thông tin, bạn có thể tìm kiếm "phạm vi chuỗi JavaScript" trong Google.


12
2017-09-24 09:02



Nếu bạn khai báo một biến bằng cách sử dụng từ khóa var, nó sẽ được tạo ra trong thời gian chạy vì vậy nó không nên chậm hơn? Bởi vì người khác được tạo ra tại thời gian phân tích cú pháp. - Ryu Kaplan
@ RyuKaplan - này, có đúng không? Tôi đã thử googling và không thể nhận được bất kỳ thông tin về chủ đề này! Bạn có thẩm quyền nguồn cho khẳng định đó không? Cám ơn - mike rodent
@RyuKaplan Phân tích cú pháp / biên dịch khác với thực sự chạy mã. - gcampbell


sự khác biệt khác ví dụ

var a = a || [] ; // works 

trong khi

a = a || [] ; // a is undefined error.

8
2017-08-09 22:11



Bạn có thể giải thích tại sao nó hoạt động trong trường hợp biến được định nghĩa với 'var' và biến không được định nghĩa với var? Biến được tạo trước khi đánh giá bên phải của nhiệm vụ trong trường hợp var? - matt
@Lucek vì var a được nâng lên đỉnh của phạm vi và đặt thành null mà khai báo nhưng không khởi tạo biến, sau đó trong gán, bạn có tham chiếu đến biến không xác định null đánh giá sai, và đặt gán cho []. Sau này, bạn có một nhiệm vụ cho tài sản a của tài sản a. Bạn có thể gán cho một thuộc tính không tồn tại - tạo nó trên gán, nhưng bạn không thể đọc từ một thuộc tính không tồn tại mà không nhận được một ReferenceError ném vào bạn. - Evan Carroll
@EvanCarroll: nó được nâng lên đầu phạm vi và được đặt thành undefined thay vì null. - mithunsatheesh


Sử dụng var luôn luôn là một ý tưởng tốt để ngăn các biến làm lộn xộn phạm vi toàn cầu và các biến xung đột với nhau, gây ra việc ghi đè không mong muốn.


7
2018-04-04 23:14