Có gì khác biệt giữa việc sử dụng những người khác để sử dụng và cho phép


4542

ECMAScript 6 giới thiệu các lettuyên bố .

Tôi đã nghe nói rằng nó được mô tả như một biến "cục bộ", nhưng tôi vẫn không hoàn toàn chắc chắn về cách nó hoạt động khác với vartừ khóa.

Sự khác biệt là gì? Khi nào nên letsử dụng hơn var?


105
ECMAScript là tiêu chuẩn và letđược bao gồm trong dự thảo phiên bản thứ 6 và rất có thể sẽ nằm trong đặc điểm kỹ thuật cuối cùng.
Richard Ayotte

5
Xem kangax.github.io/es5-compat-table/es6 để biết ma trận hỗ trợ cập nhật các tính năng ES6 (bao gồm cả let). Tại thời điểm viết Firefox, Chrome và IE11 đều hỗ trợ nó (mặc dù tôi tin rằng việc triển khai của FF không hoàn toàn chuẩn).
Nico đốt cháy

22
Trong thời gian dài nhất tôi đã không biết rằng các vars trong vòng lặp for được sắp xếp theo chức năng mà nó được gói vào. Tôi nhớ lần đầu tiên phát hiện ra điều này và nghĩ rằng nó rất ngu ngốc. Tôi thấy một số sức mạnh mặc dù bây giờ biết làm thế nào hai cái này có thể được sử dụng cho lý do khác nhau và trong một số trường hợp bạn thực sự muốn sử dụng một var trong một vòng lặp for và không có phạm vi trong khối.
Eric Bishard

1
Đây là một cách đọc rất tốt wesbos.com/javascript-scoping
onmyway133

1
Giải thích rõ câu trả lời ở đây stackoverflow.com/a/43994458/5043867
Pardeep Jain

Câu trả lời:


6101

Quy tắc phạm vi

Sự khác biệt chính là quy tắc phạm vi. Các biến khai báo bằng vartừ khoá được scoped đến cơ quan chức năng ngay lập tức (do đó phạm vi chức năng) trong khi letcác biến được scoped đến ngay lập tức kèm theo khối ký hiệu bằng { }(do đó phạm vi khối).

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar);

  {
    let baz = "Bazz";
    console.log(baz);
  }

  console.log(baz); // ReferenceError
}

run();

Lý do tại sao lettừ khóa được giới thiệu cho ngôn ngữ là phạm vi chức năng gây nhầm lẫn và là một trong những nguồn lỗi chính trong JavaScript.

Hãy xem ví dụ này từ một câu hỏi stackoverflow khác :

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

My value: 3là đầu ra cho bàn điều khiển mỗi lần funcs[j]();được gọi vì các hàm ẩn danh được liên kết với cùng một biến.

Mọi người phải tạo ra các hàm được gọi ngay lập tức để thu được giá trị chính xác từ các vòng lặp nhưng đó cũng là lông.

Tời kéo

Trong khi các biến được khai báo bằng vartừ khóa được nâng lên (khởi tạo undefinedtrước khi mã được chạy), điều đó có nghĩa là chúng có thể truy cập được trong phạm vi kèm theo ngay cả trước khi chúng được khai báo:

function run() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

letcác biến không được khởi tạo cho đến khi định nghĩa của chúng được đánh giá. Truy cập chúng trước khi kết quả khởi tạo trong a ReferenceError. Biến được cho là ở "vùng chết tạm thời" từ khi bắt đầu khối cho đến khi quá trình khởi tạo được xử lý.

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

Tạo thuộc tính đối tượng toàn cầu

Ở cấp cao nhất, letkhông giống như var, không tạo ra một thuộc tính trên đối tượng toàn cầu:

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

Tuyên bố

Trong chế độ nghiêm ngặt, varsẽ cho phép bạn khai báo lại cùng một biến trong cùng phạm vi trong khi lettăng SyntaxError.

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.

let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

23
Hãy nhớ rằng bạn có thể tạo khối bất cứ khi nào bạn muốn. hàm () {code; {let inBlock = 5; mã}; };
trung bình Joe

177
Vì vậy, mục đích của việc để các câu lệnh chỉ giải phóng bộ nhớ khi không cần thiết trong một khối nhất định?
NoBugs

219
@NoBugs, Có, và khuyến khích rằng các biến chỉ tồn tại khi cần thiết.
dơi

67
letbiểu thức khối let (variable declaration) statementlà không chuẩn và sẽ bị xóa trong tương lai, bugzilla.mozilla.org/show_orms.cgi?id=1023609 .
Gajus

19
Vì vậy, tôi không thể nghĩ về bất kỳ trường hợp sử dụng var nào. Ai đó có thể cho tôi một ví dụ về tình huống sử dụng var không?
Luis Sieira

622

letcũng có thể được sử dụng để tránh các vấn đề với việc đóng cửa. Nó liên kết giá trị mới thay vì giữ một tham chiếu cũ như trong các ví dụ dưới đây.

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

Mã ở trên thể hiện một vấn đề đóng JavaScript cổ điển. Tham chiếu đến ibiến đang được lưu trữ trong bao đóng xử lý nhấp chuột, thay vì giá trị thực của i.

Mỗi trình xử lý nhấp chuột duy nhất sẽ tham chiếu đến cùng một đối tượng vì chỉ có một đối tượng truy cập giữ 6 vì vậy bạn nhận được sáu đối tượng trên mỗi lần nhấp.

Một cách giải quyết chung là bọc cái này trong một hàm ẩn danh và chuyển qua ilàm đối số. Những vấn đề như vậy cũng có thể tránh được bằng cách sử dụng letthay vào đó varnhư được hiển thị trong đoạn mã dưới đây.

(Đã thử nghiệm trong Chrome và Firefox 50)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>


54
Điều đó thực sự tuyệt vời. Tôi hy vọng "i" sẽ được xác định bên ngoài thân vòng chứa trong ngoặc và KHÔNG tạo thành "đóng" xung quanh "i". Tất nhiên ví dụ của bạn chứng minh điều khác. Tôi nghĩ rằng nó hơi khó hiểu theo quan điểm cú pháp nhưng kịch bản này rất phổ biến nên có ý nghĩa để hỗ trợ nó theo cách đó. Rất cám ơn đã mang cái này lên.
Karol Kolenda

9
IE 11 hỗ trợ let, nhưng nó cảnh báo "6" cho tất cả các nút. Bạn có nguồn nào nói rằng letphải cư xử thế nào không?
Jim Hunziker

10
Có vẻ như câu trả lời của bạn là hành vi đúng: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kẻ

11
Quả thực đây là một cạm bẫy phổ biến trong Javascript và bây giờ tôi có thể thấy tại sao letnó sẽ thực sự hữu ích. Đặt các trình lắng nghe sự kiện trong một vòng lặp không còn yêu cầu một biểu thức hàm được gọi ngay lập tức cho phạm vi cục bộ itại mỗi lần lặp.
Adrian Moisa

19
Việc sử dụng "cho phép" chỉ trì hoãn vấn đề này. Vì vậy, mỗi lần lặp tạo ra một phạm vi khối độc lập riêng, nhưng biến "i" vẫn có thể bị hỏng bởi các thay đổi tiếp theo trong khối, (được cấp biến iterator thường không thay đổi trong khối, nhưng các biến được khai báo khác trong khối có thể tốt be) và bất kỳ hàm nào được khai báo trong khối có thể, khi được gọi, làm hỏng giá trị của "i" đối với các hàm khác được khai báo trong khối vì chúng chung phạm vi khối riêng do đó cùng tham chiếu đến "i".
gary

199

Sự khác biệt giữa letvà là vargì?

  • Một biến được định nghĩa bằng cách sử dụng một varcâu lệnh được biết trong toàn bộ hàm được xác định trong, từ khi bắt đầu hàm.(*)
  • Một biến được xác định bằng cách sử dụng một letcâu lệnh chỉ được biết đến trong khối mà nó được định nghĩa, kể từ thời điểm nó được xác định trở đi. (**)

Để hiểu sự khác biệt, hãy xem xét mã sau đây:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Ở đây, chúng ta có thể thấy rằng biến của chúng ta jchỉ được biết đến trong vòng lặp đầu tiên, nhưng không phải trước và sau. Tuy nhiên, biến của chúng tôii được biết đến trong toàn bộ chức năng.

Ngoài ra, hãy xem xét rằng các biến trong phạm vi khối không được biết trước khi chúng được khai báo vì chúng không được nâng lên. Bạn cũng không được phép xác định lại cùng một biến trong phạm vi khối trong cùng một khối. Điều này làm cho các biến trong phạm vi khối dễ bị lỗi hơn các biến có phạm vi toàn cầu hoặc theo chức năng, được nâng lên và không tạo ra bất kỳ lỗi nào trong trường hợp khai báo nhiều lần.


Có an toàn khi sử dụng không let ngày hôm nay?

Một số người sẽ lập luận rằng trong tương lai, chúng ta sẽ CHỈ sử dụng các câu lệnh let và các câu lệnh var sẽ trở nên lỗi thời. Giáo sư JavaScript Kyle Simpson đã viết một bài viết rất công phu về lý do tại sao ông tin rằng sẽ không phải là trường hợp .

Hôm nay, tuy nhiên, đó chắc chắn không phải là trường hợp. Trong thực tế, chúng ta thực sự cần phải tự hỏi liệu nó có an toàn để sử dụng lettuyên bố hay không. Câu trả lời cho câu hỏi đó phụ thuộc vào môi trường của bạn:

  • Nếu bạn đang viết mã JavaScript phía máy chủ ( Node.js ), bạn có thể sử dụng letcâu lệnh một cách an toàn .

  • Nếu bạn đang viết mã JavaScript phía máy khách và sử dụng trình chuyển đổi dựa trên trình duyệt (như Traceur hoặc babel- stand Độc ), bạn có thể sử dụng letcâu lệnh một cách an toàn , tuy nhiên mã của bạn có thể là bất cứ điều gì ngoài tối ưu về hiệu suất.

  • Nếu bạn đang viết mã JavaScript phía máy khách và sử dụng trình chuyển đổi dựa trên Node (như tập lệnh shell của dấu vết hoặc Babel ), bạn có thể sử dụng letcâu lệnh một cách an toàn . Và bởi vì trình duyệt của bạn sẽ chỉ biết về mã được dịch mã, nên hạn chế về hiệu suất.

  • Nếu bạn đang viết mã JavaScript phía máy khách và không sử dụng bộ chuyển mã, bạn cần xem xét hỗ trợ trình duyệt.

    Vẫn còn một số trình duyệt hoàn toàn không hỗ trợ let:

nhập mô tả hình ảnh ở đây


Cách theo dõi hỗ trợ trình duyệt

Đối với một cái nhìn tổng quan up-to-date trong đó các trình duyệt hỗ trợ lettuyên bố tại thời điểm đọc của bạn câu trả lời này, xem này Can I Usetrang .


(*) Các biến phạm vi toàn cầu và chức năng có thể được khởi tạo và sử dụng trước khi chúng được khai báo vì các biến JavaScript được nâng lên . Điều này có nghĩa là các khai báo luôn luôn đứng đầu phạm vi.

(**) Khối biến phạm vi không được nâng lên


14
liên quan đến câu trả lời v4: iIS được biết đến ở mọi nơi trong khối chức năng! Nó bắt đầu như undefined(do cẩu) cho đến khi bạn gán một giá trị! ps: letcũng được nâng lên (đến đỉnh của khối chứa), nhưng sẽ đưa ra ReferenceErrorkhi được tham chiếu trong khối trước khi gán đầu tiên. (ps2: Tôi là một chàng trai chuyên nghiệp về dấu chấm phẩy nhưng bạn thực sự không cần một dấu chấm phẩy sau một khối). Điều đó đang được nói, cảm ơn vì đã thêm kiểm tra thực tế về hỗ trợ!
GitaarLAB

@GitaarLAB: Theo Mạng của Nhà phát triển Mozilla : "Trong ECMAScript 2015, hãy để các ràng buộc không phải chịu Biến đổi, có nghĩa là cho phép các khai báo không di chuyển lên trên cùng của bối cảnh thực thi hiện tại." - Dù sao, tôi đã thực hiện một vài cải tiến cho câu trả lời của mình để làm rõ sự khác biệt trong hành vi cẩu thả giữa letvar!
John Slegers

1
Câu trả lời của bạn được cải thiện rất nhiều (tôi đã kiểm tra kỹ lưỡng). Lưu ý rằng cùng một liên kết mà bạn đã tham chiếu trong nhận xét của mình cũng cho biết: "Biến (let) nằm trong" vùng chết tạm thời "từ khi bắt đầu khối cho đến khi quá trình khởi tạo được xử lý." Điều đó có nghĩa là 'định danh' (chuỗi văn bản 'dành riêng' để trỏ đến 'một cái gì đó') đã được bảo lưu trong phạm vi có liên quan, nếu không, nó sẽ trở thành một phần của phạm vi gốc / máy chủ / cửa sổ. Đối với cá nhân tôi, 'cẩu' có nghĩa là không có gì khác hơn là bảo lưu / liên kết 'định danh' được khai báo với phạm vi liên quan của họ; không bao gồm khởi tạo / chuyển nhượng / sửa đổi của họ!
GitaarLAB

Và .. + 1. Bài viết mà Kyle Simpson bạn liên kết là một bài đọc tuyệt vời , cảm ơn bạn vì điều đó! Nó cũng rõ ràng về "vùng chết tạm thời" hay còn gọi là "TDZ". Một điều thú vị tôi muốn thêm: Tôi đã đọc trên MDN letconstđược khuyến nghị chỉ sử dụng khi bạn thực sự cần chức năng bổ sung của chúng , bởi vì việc thực thi / kiểm tra các tính năng bổ sung này (như const chỉ viết) dẫn đến 'công việc nhiều hơn '(và các nút phạm vi bổ sung trong cây phạm vi) cho (các) công cụ (hiện tại) để thực thi / kiểm tra / xác minh / thiết lập.
GitaarLAB

1
Lưu ý rằng MDN nói rằng IE DOES diễn giải cho phép chính xác. Đó là cái gì developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/iêu
Katinka Hesselink

146

Đây là một lời giải thích của lettừ khóa với một số ví dụ.

lethoạt động rất thích var. Sự khác biệt chính là phạm vi của một varbiến là toàn bộ hàm kèm theo

Bảng này trên Wikipedia cho thấy trình duyệt nào hỗ trợ Javascript 1.7.

Lưu ý rằng chỉ có trình duyệt Mozilla và Chrome hỗ trợ nó. IE, Safari và những người khác không có khả năng.


5
Bit chính của văn bản từ tài liệu được liên kết dường như là "hãy hoạt động rất giống var. Sự khác biệt chính là phạm vi của biến var là toàn bộ hàm kèm theo".
Michael Burr

50
Mặc dù về mặt kỹ thuật để nói rằng IE không hỗ trợ nó, nhưng nói đúng hơn là nó chỉ là một phần mở rộng mozilla.
olliej

55
@olliej, thật ra Mozilla đang ở phía trước của trò chơi. Xem trang 19 của ecma
Tyler Crompton

@TylerCrompton đó chỉ là tập hợp các từ đã được dành riêng trong nhiều năm. Khi thêm mozilla, hãy để nó hoàn toàn là một phần mở rộng của mozilla, không có thông số liên quan. ES6 nên xác định hành vi cho các câu lệnh let, nhưng điều đó xuất hiện sau khi mozilla giới thiệu cú pháp. Hãy nhớ moz cũng có E4X, hoàn toàn chết và chỉ moz.
olliej


112

Câu trả lời được chấp nhận thiếu một điểm:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined

19
Câu trả lời được chấp nhận KHÔNG giải thích điểm này trong ví dụ của nó. Câu trả lời được chấp nhận chỉ thể hiện nó trong một trình forkhởi tạo vòng lặp, thu hẹp đáng kể phạm vi áp dụng các giới hạn của let. Nâng cao.
Jon Davis

37
@ activpy77 Nó tuyên bố rõ ràng "hãy nằm trong phạm vi bao quanh gần nhất"; Có phải mọi cách mà biểu hiện cần phải được đưa vào?
Dave Newton

6
có rất nhiều ví dụ và không ai trong số họ thể hiện đúng vấn đề .. Tôi có thể đã đưa ra cả câu trả lời được chấp nhận và câu trả lời này không?
Jon Davis

5
Đóng góp này chứng tỏ rằng một "khối" có thể chỉ đơn giản là một tập hợp các dòng được đặt trong ngoặc; tức là không cần phải liên kết với bất kỳ loại luồng điều khiển, vòng lặp, v.v.
webelo

81

let

Phạm vi khối

Các biến được khai báo sử dụng lettừ khóa có phạm vi khối, có nghĩa là chúng chỉ có sẵn trong khối mà chúng được khai báo.

Ở cấp cao nhất (bên ngoài chức năng)

Ở cấp cao nhất, các biến được khai báo sử dụng letkhông tạo thuộc tính trên đối tượng toàn cục.

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

Bên trong một chức năng

Bên trong một hàm (nhưng bên ngoài một khối), letcó cùng phạm vi với var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Bên trong một khối

Các biến được khai báo sử dụng letbên trong một khối không thể được truy cập bên ngoài khối đó.

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Bên trong một vòng lặp

Các biến được khai báo với letcác vòng lặp chỉ có thể được tham chiếu bên trong vòng lặp đó.

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

Vòng lặp với đóng cửa

Nếu bạn sử dụng letthay vì vartrong một vòng lặp, với mỗi lần lặp, bạn sẽ nhận được một biến mới. Điều đó có nghĩa là bạn có thể sử dụng một cách an toàn trong một vòng lặp.

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Vùng chết tạm thời

Do vùng chết tạm thời , các biến được khai báo sử dụng letkhông thể được truy cập trước khi chúng được khai báo. Cố gắng để làm như vậy ném một lỗi.

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

Không khai báo lại

Bạn không thể khai báo cùng một biến nhiều lần bằng cách sử dụng let. Bạn cũng không thể khai báo một biến bằng cách sử dụng letcùng một mã định danh như một biến khác được khai báo sử dụng var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

constlà khá giống với letphạm vi khối củaitit và có TDZ. Tuy nhiên, có hai điều khác nhau.

Không phân công lại

Biến được khai báo sử dụng constkhông thể được chỉ định lại.

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

Lưu ý rằng điều đó không có nghĩa là giá trị là bất biến. Thuộc tính của nó vẫn có thể được thay đổi.

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

Nếu bạn muốn có một đối tượng bất biến, bạn nên sử dụng Object.freeze().

Cần khởi tạo

Bạn luôn phải chỉ định một giá trị khi khai báo một biến bằng cách sử dụng const.

const a; // SyntaxError: Missing initializer in const declaration

51

Dưới đây là một ví dụ cho sự khác biệt giữa hai (hỗ trợ chỉ bắt đầu cho chrome):
nhập mô tả hình ảnh ở đây

Như bạn có thể thấy var jbiến vẫn có giá trị nằm ngoài phạm vi vòng lặp for (Phạm vi khối), nhưng let ibiến không được xác định bên ngoài phạm vi vòng lặp for.

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);


2
Tôi đang xem công cụ gì ở đây?
Barton

20
Chrome devtools
vlio20

Là một nhà phát triển các applet máy tính để bàn cho Cinnamon, tôi đã không được tiếp xúc với các công cụ sáng bóng như vậy.
Barton

48

Có một số khác biệt tinh tế - let phạm vi hoạt động giống như phạm vi biến đổi hơn trong bất kỳ ngôn ngữ nào khác.

ví dụ: Phạm vi của khối kèm theo, Chúng không tồn tại trước khi chúng được khai báo, v.v.

Tuy nhiên, đáng chú ý rằng đó letchỉ là một phần của các triển khai Javascript mới hơn và có các mức độ hỗ trợ trình duyệt khác nhau .


11
Cũng đáng lưu ý rằng ECMAScript là tiêu chuẩn và letđược bao gồm trong dự thảo phiên bản thứ 6 và rất có thể sẽ nằm trong đặc tả kỹ thuật cuối cùng.
Richard Ayotte

23
Đó là sự khác biệt 3 năm tạo nên: D
olliej

4
Chỉ vấp phải câu hỏi này và năm 2012 vẫn chỉ có trường hợp trình duyệt Mozilla hỗ trợ let. Safari, IE và Chome đều không.
pseudosavant

2
Ý tưởng vô tình tạo ra phạm vi khối một phần do tai nạn là một điểm tốt, hãy cẩn thận, letkhông cần nâng, để sử dụng một biến được xác định bởi một letđịnh nghĩa ở đầu khối của bạn. Nếu bạn có một ifcâu lệnh không chỉ là một vài dòng mã, bạn có thể quên rằng bạn không thể sử dụng biến đó cho đến khi nó được xác định. ĐIỂM TUYỆT VỜI !!!
Eric Bishard

2
@EricB: có và không: "Trong ECMAScript 2015, let sẽ đưa biến lên đầu khối. Tuy nhiên, việc tham chiếu biến trong khối trước khi khai báo biến dẫn đến ReferenceError (lưu ý của tôi: thay vì cũ tốt undefined). biến nằm trong 'vùng chết tạm thời' từ khi bắt đầu khối cho đến khi khai báo được xử lý. " Tương tự với "câu lệnh chuyển đổi vì chỉ có một khối bên dưới". Nguồn: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
mẹo

27

Sự khác biệt chính là sự khác biệt về phạm vi , trong khi cho phép chỉ có sẵn bên trong phạm vi được khai báo, như trong vòng lặp for, ví dụ var có thể được truy cập bên ngoài vòng lặp. Từ tài liệu trong MDN (ví dụ cũng từ MDN):

cho phép bạn khai báo các biến bị giới hạn phạm vi đối với khối, câu lệnh hoặc biểu thức mà nó được sử dụng. Điều này không giống như var từ khóa , định nghĩa một biến toàn cục hoặc cục bộ cho toàn bộ hàm bất kể phạm vi khối.

Các biến được khai báo bằng let có phạm vi của chúng là khối mà chúng được xác định, cũng như trong bất kỳ khối con nào được chứa. Theo cách này, hãy làm việc rất giống như var . Sự khác biệt chính là phạm vi của một biến var là toàn bộ hàm kèm theo:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}`

Ở cấp cao nhất của chương trình và chức năng, hãy , không giống như var , không tạo ra một thuộc tính trên đối tượng toàn cầu. Ví dụ:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

Khi được sử dụng bên trong một khối, hãy giới hạn phạm vi của biến đối với khối đó. Lưu ý sự khác biệt giữa var có phạm vi bên trong hàm nơi nó được khai báo.

var a = 1;
var b = 2;

if (a === 1) {
  var a = 11; // the scope is global
  let b = 22; // the scope is inside the if-block

  console.log(a);  // 11
  console.log(b);  // 22
} 

console.log(a); // 11
console.log(b); // 2

Cũng đừng quên tính năng ECMA6, vì vậy nó chưa được hỗ trợ đầy đủ, vì vậy tốt hơn là luôn chuyển nó sang ECMA5 bằng Babel, v.v ... để biết thêm thông tin về việc truy cập trang web của babel


24
  • Biến không nâng

    letsẽ không nâng lên toàn bộ phạm vi của khối mà chúng xuất hiện. Ngược lại, varcó thể nâng lên như bên dưới.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }

    Trên thực tế, Per @Bergi, Cả hai varletđều được nâng lên .

  • Thu gom rác thải

    Phạm vi khối letlà hữu ích liên quan đến việc đóng cửa và thu gom rác để lấy lại bộ nhớ. Xem xét,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });

    Cuộc clickgọi lại xử lý không cần hugeDatabiến nào cả. Về mặt lý thuyết, sau khi process(..)chạy, cấu trúc dữ liệu khổng lồ hugeDatacó thể được thu gom rác. Tuy nhiên, có thể một số công cụ JS vẫn sẽ phải giữ cấu trúc khổng lồ này, vì clickchức năng này đã đóng cửa trên toàn bộ phạm vi.

    Tuy nhiên, phạm vi khối có thể làm cho cấu trúc dữ liệu khổng lồ này thành rác được thu thập.

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
  • let vòng lặp

    lettrong vòng lặp có thể liên kết lại nó với mỗi lần lặp của vòng lặp, đảm bảo gán lại cho nó giá trị từ cuối vòng lặp trước đó. Xem xét,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }

    Tuy nhiên, thay thế varbằnglet

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }

    Bởi vì lettạo một môi trường từ vựng mới với các tên đó cho a) biểu thức khởi tạo b) mỗi lần lặp (chủ yếu để đánh giá biểu thức tăng), nên có thêm chi tiết ở đây .


4
Yip chúng được nâng lên, nhưng hoạt động như thể không được nâng lên vì Vùng chết tạm thời (trống cuộn) - một cái tên rất ấn tượng cho một định danh không thể truy cập cho đến khi nó được tuyên bố :-)
Drenai

Vì vậy, hãy để được nâng lên, nhưng không có sẵn? Làm thế nào mà khác với 'không được nâng lên'?
N-ate

Hy vọng Brian hoặc Bergi trở lại để trả lời điều này. Là tuyên bố buông tay, nhưng không phải là sự phân công? Cảm ơn!
N-ate

1
@ N-ate, Đây là một bài viết của Bergi, có lẽ bạn có thể tìm thấy câu trả lời trong đó.
zangw

Thật thú vị, nó thậm chí còn được gọi là cẩu khi nói đến. Tôi hiểu rằng về mặt kỹ thuật, công cụ phân tích cú pháp đã bắt trước nó, nhưng với tất cả ý định và mục đích, một lập trình viên nên coi nó như thể nó không tồn tại. Mặt khác, việc nâng var có ý nghĩa đối với một lập trình viên.
N-ate

19

Đây là một ví dụ để thêm vào những gì người khác đã viết. Giả sử bạn muốn tạo một mảng các hàm, adderFunctionstrong đó mỗi hàm lấy một đối số Số duy nhất và trả về tổng của đối số và chỉ mục của hàm trong mảng. Cố gắng tạo adderFunctionsvòng lặp bằng vartừ khóa sẽ không hoạt động theo cách mà ai đó có thể mong đợi một cách ngây thơ:

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

Quá trình trên không tạo ra mảng chức năng mong muốn vì iphạm vi của nó vượt ra ngoài vòng lặp của forkhối trong đó mỗi hàm được tạo. Thay vào đó, ở cuối vòng lặp, việc đóng itrong mỗi hàm tham chiếu đến igiá trị ở cuối vòng lặp (1000) cho mỗi hàm ẩn danh trong adderFunctions. Đây hoàn toàn không phải là điều chúng tôi muốn: hiện tại chúng tôi có một mảng gồm 1000 chức năng khác nhau trong bộ nhớ với cùng một hành vi. Và nếu sau đó chúng tôi cập nhật giá trị của i, đột biến sẽ ảnh hưởng đến tất cả adderFunctions.

Tuy nhiên, chúng ta có thể thử lại bằng lettừ khóa:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

Lần này, iđược bật lại trên mỗi lần lặp của forvòng lặp. Mỗi hàm hiện giữ giá trị itại thời điểm tạo hàm vàadderFunctions hoạt động như mong đợi.

Bây giờ, hình ảnh trộn lẫn hai hành vi và có thể bạn sẽ thấy lý do tại sao không nên kết hợp cái mới hơn letconstvới cái cũ hơn vartrong cùng một tập lệnh. Làm như vậy có thể dẫn đến một số mã khó hiểu ngoạn mục.

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

Đừng để điều này xảy ra với bạn. Sử dụng một kẻ nói dối.

LƯU Ý: Đây là một ví dụ giảng dạy nhằm thể hiện var/ lethành vi trong các vòng lặp và với việc đóng chức năng cũng sẽ dễ hiểu. Đây sẽ là một cách khủng khiếp để thêm số. Nhưng kỹ thuật chung để thu thập dữ liệu trong các lần đóng chức năng ẩn danh có thể gặp phải trong thế giới thực trong các bối cảnh khác. YMMV.


2
@aborz: Cú pháp hàm ẩn danh cũng rất tuyệt trong ví dụ thứ hai. Đó chỉ là những gì tôi đã từng sử dụng trong C #. Tôi đã học được điều gì hôm nay.
Barton

Sửa lỗi: Về mặt kỹ thuật, cú pháp hàm Mũi tên được mô tả tại đây => developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
trộm

3
Thật ra, bạn không cần let value = i;. Câu forlệnh tạo ra một khối từ vựng.
Bàn chải đánh răng

17

Sự khác biệt là trong phạm vi của các biến được khai báo với mỗi biến.

Trong thực tế, có một số hậu quả hữu ích của sự khác biệt về phạm vi:

  1. letcác biến chỉ được nhìn thấy trong khối kèm theo gần nhất của chúng ({ ... } ).
  2. letcác biến chỉ có thể sử dụng được trong các dòng mã xảy ra sau khi biến được khai báo (mặc dù chúng được nâng lên !).
  3. letcác biến có thể không được xác định lại bởi một varhoặc let.
  4. letBiến toàn cục không được thêm vào windowđối tượng toàn cầu .
  5. letcác biến rất dễ sử dụng khi đóng (chúng không gây ra tình trạng chủng tộc ).

Các hạn chế được áp đặt bằng cách letgiảm khả năng hiển thị của các biến và tăng khả năng va chạm tên không mong muốn sẽ được tìm thấy sớm. Điều này giúp dễ dàng theo dõi và lý do về các biến, bao gồm khả năng tiếp cận của chúng (giúp lấy lại bộ nhớ không sử dụng).

Hậu quả là, let các biến ít có khả năng gây ra sự cố khi được sử dụng trong các chương trình lớn hoặc khi các khung phát triển độc lập được kết hợp theo những cách mới và bất ngờ.

varcó thể vẫn hữu ích nếu bạn chắc chắn muốn có hiệu ứng liên kết đơn khi sử dụng bao đóng trong một vòng lặp (# 5) hoặc để khai báo các biến toàn cục có thể nhìn thấy bên ngoài trong mã của bạn (# 4). Việc sử dụng varcho xuất khẩu có thể được thay thế nếu exportdi chuyển ra khỏi không gian transpiler và vào ngôn ngữ cốt lõi.

Ví dụ

1. Không sử dụng bên ngoài khối kèm theo gần nhất: Khối mã này sẽ gây ra lỗi tham chiếu vì lần sử dụng thứ hai xxảy ra bên ngoài khối nơi được khai báo với let:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

Ngược lại, cùng một ví dụ với var các công trình.

2. Không sử dụng trước khi khai báo:
Khối mã này sẽ ném ReferenceErrortrước khi mã có thể được chạy vì xđược sử dụng trước khi được khai báo:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

Ngược lại, cùng một ví dụ với các varphân tích cú pháp và chạy mà không đưa ra bất kỳ ngoại lệ nào.

3. Không khai báo lại: Đoạn mã sau chứng tỏ rằng một biến được khai báo có letthể không được khai báo lại sau:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4. Quả cầu không được đính kèm window:

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5. Dễ dàng sử dụng với các bao đóng: Các biến được khai báo varkhông hoạt động tốt với các bao đóng bên trong các vòng lặp. Đây là một vòng lặp đơn giản đưa ra chuỗi các giá trị mà biến icó tại các thời điểm khác nhau:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

Cụ thể, kết quả này:

i is 0
i is 1
i is 2
i is 3
i is 4

Trong JavaScript, chúng tôi thường sử dụng các biến tại một thời điểm muộn hơn đáng kể so với khi chúng được tạo. Khi chúng tôi chứng minh điều này bằng cách trì hoãn đầu ra với một bao đóng được chuyển đến setTimeout:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... Đầu ra vẫn không thay đổi miễn là chúng tôi gắn bó let. Ngược lại, nếu chúng ta đã sử dụng var ithay thế:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... vòng lặp bất ngờ xuất ra "i là 5" năm lần:

i is 5
i is 5
i is 5
i is 5
i is 5

5
# 5 không phải do một điều kiện cuộc đua. Bằng cách sử dụng varthay vì let, mã tương đương với: var i = 0; while (i < 5) { doSomethingLater(); i++; } inằm ngoài bao đóng và theo thời gian doSomethingLater()được thực thi, iđã được tăng lên 5 lần, do đó đầu ra là i is 5năm lần. Bằng cách sử dụng let, biến inằm trong bao đóng, vì vậy mỗi cuộc gọi không đồng bộ sẽ có bản sao ithay vì sử dụng 'toàn cầu' được tạo bằng var.
Daniel T.

@DanielT.: Tôi không nghĩ việc chuyển đổi định nghĩa biến ra khỏi trình khởi tạo vòng lặp giải thích bất cứ điều gì. Đó chỉ đơn giản là định nghĩa bình thường của ngữ nghĩa của for. Một phép biến đổi chính xác hơn, mặc dù phức tạp hơn, là for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(i cổ điển là $ {j} ), 125/*ms*/); })(i); }, giới thiệu một "bản ghi kích hoạt chức năng" để lưu từng giá trị ivới tên của jhàm bên trong hàm.
mormegil

14

Có thể hai chức năng sau đây cho thấy sự khác biệt:

function varTest() {
    var x = 31;
    if (true) {
        var x = 71;  // Same variable!
        console.log(x);  // 71
    }
    console.log(x);  // 71
}

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // Different variable
        console.log(x);  // 71
    }
    console.log(x);  // 31
}

13

let thật thú vị, vì nó cho phép chúng ta làm một cái gì đó như thế này:

(() => {
    var count = 0;

    for (let i = 0; i < 2; ++i) {
        for (let i = 0; i < 2; ++i) {
            for (let i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Mà kết quả là đếm [0, 7].

Trong khi

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Chỉ tính [0, 1].


2
đây là lần đầu tiên tôi từng thấy bất cứ ai hành động như bóng tối biến đổi là mong muốn. không, mục đích của let là không cho phép tạo bóng
John Haugeland

1
mục đích? đó là một cấu trúc, bạn có thể sử dụng nó theo cách bạn muốn, một trong những cách thú vị là như thế này.
Dmitry

13

Phạm vi khối chức năng VS:

Sự khác biệt chính giữa varletlà biến khai báo với varđược chức năng scoped . Trong khi đó các hàm được khai báo letlà có phạm vi khối . Ví dụ:

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

các biến có var:

Khi hàm đầu tiên testVarđược gọi là biến foo, được khai báo với var, vẫn có thể truy cập được bên ngoài ifcâu lệnh. Biến foonày sẽ có sẵn ở mọi nơi trong phạm vi của testVar hàm .

các biến có let:

Khi hàm thứ hai testLetđược gọi là thanh biến, được khai báo bằng let, chỉ có thể truy cập bên trong ifcâu lệnh. Bởi vì các biến được khai báo letlà có phạm vi khối (trong đó một khối là mã giữa các dấu ngoặc nhọn if{}, vd for{}, function{}).

let các biến không được nâng lên:

Một điểm khác biệt giữa varletlà các biến có khai báo let không được nâng lên . Một ví dụ là cách tốt nhất để minh họa hành vi này:

các biến let không được nâng lên:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

các biến với var do được nâng lên:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

Toàn cầu letkhông gắn bó vớiwindow :

Một biến được khai báo lettrong phạm vi toàn cục (là mã không có trong hàm) sẽ không được thêm dưới dạng một thuộc tính trên windowđối tượng toàn cục . Ví dụ: mã này nằm trong phạm vi toàn cầu):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object


Khi nào nên letsử dụng hơn var?

Sử dụng lethơn varbất cứ khi nào bạn có thể bởi vì nó chỉ đơn giản là phạm vi cụ thể hơn. Điều này làm giảm xung đột đặt tên tiềm năng có thể xảy ra khi xử lý một số lượng lớn các biến. varcó thể được sử dụng khi bạn muốn một biến toàn cục rõ ràng nằm trên windowđối tượng (luôn luôn cân nhắc cẩn thận nếu điều này thực sự cần thiết).


9

Dường như, ít nhất là trong Visual Studio 2015, TypeScript 1.5, "var" cho phép nhiều khai báo cùng tên biến trong một khối và "let" không.

Điều này sẽ không tạo ra một lỗi biên dịch:

var x = 1;
var x = 2;

Điều này sẽ:

let x = 1;
let x = 2;

9

var là phạm vi toàn cầu (Palăng-có thể) biến.

letconstlà phạm vi khối.

test.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined


8

Khi đang sử dụng let

Các lettừ khóa gắn việc khai báo biến với phạm vi của bất cứ khối (thường là một { .. }cặp) nó chứa trong. Nói cách khác,let ngầm hijacks phạm vi bất kỳ của khối khai báo biến của nó.

letcác biến không thể được truy cập trong windowđối tượng vì chúng không thể được truy cập trên toàn cầu.

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

Khi đang sử dụng var

var và các biến trong ES5 có phạm vi trong các hàm có nghĩa là các biến có giá trị trong hàm và không nằm ngoài chính hàm đó.

varcác biến có thể được truy cập trong windowđối tượng vì chúng không thể được truy cập trên toàn cầu.

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

Nếu bạn muốn biết thêm hãy tiếp tục đọc bên dưới

một trong những câu hỏi phỏng vấn nổi tiếng nhất về phạm vi cũng có thể đủ cho việc sử dụng chính xác letvarnhư dưới đây;

Khi đang sử dụng let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

Điều này là do khi sử dụng let, với mỗi lần lặp lặp, biến được đặt trong phạm vi và có bản sao riêng.

Khi đang sử dụng var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

Điều này là do khi sử dụng var, với mỗi lần lặp lặp, biến được đặt trong phạm vi và có bản sao được chia sẻ.


8

Trong hầu hết các điều khoản cơ bản,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

Sandbox để chơi xung quanh

Chỉnh sửa let vs var


7

Nếu tôi đọc đúng thông số kỹ thuật thì let rất may cũng có thể được tận dụng để tránh các chức năng tự gọi được sử dụng để mô phỏng các thành viên chỉ riêng tư - một mẫu thiết kế phổ biến làm giảm khả năng đọc mã, làm phức tạp việc gỡ lỗi, không thêm bảo vệ mã thực hoặc lợi ích nào khác - ngoại trừ có thể làm hài lòng ai đó mong muốn về ngữ nghĩa, vì vậy hãy ngừng sử dụng nó. / rant

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

Xem ' Thi đua giao diện riêng tư '


Bạn có thể giải thích rõ hơn về cách thức Biểu thức hàm được gọi ngay lập tức không cung cấp bảo vệ mã của Wikipedia và letkhông? (Tôi giả sử bạn có nghĩa là IIFE với chức năng tự gọi ra của mình.)
Robert Siemer

Và tại sao bạn thiết lập hiddenPropertytrong constructor? Chỉ có một hiddenPropertycho tất cả các trường hợp trong lớp của bạn.
Robert Siemer

4

Một số hack với let:

1.

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2.

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3.

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

Getter và setter với let:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)

xin vui lòng, điều này có nghĩa là let { type, name, value } = node;gì? Bạn tạo một đối tượng mới với 3 thuộc tính type / name / value và khởi tạo chúng với các giá trị thuộc tính từ nút?
AlainIb

Trong ví dụ 3, bạn đang khai báo lại nút gây ra ngoại lệ. Tất cả các ví dụ cũng hoạt động hoàn hảo với varquá.
Rehan Haider

4

hãy để vs var. Đó là tất cả về phạm vi .

Các biến var là toàn cục và có thể được truy cập cơ bản ở mọi nơi, trong khi các biến không phải là toàn cục và chỉ tồn tại cho đến khi dấu ngoặc đơn đóng giết chúng.

Xem ví dụ của tôi dưới đây và lưu ý cách biến sư tử (let) hoạt động khác nhau trong hai console.logs; nó trở nên nằm ngoài phạm vi trong console.log thứ 2.

var cat = "cat";
let dog = "dog";

var animals = () => {
    var giraffe = "giraffe";
    let lion = "lion";

    console.log(cat);  //will print 'cat'.
    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).

    console.log(giraffe); //will print 'giraffe'.
    console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.

4

ES6 đã giới thiệu hai từ khóa mới ( letconst ) thay thế cho var .

Khi bạn cần giảm tốc độ khối, bạn có thể sử dụng let và const thay vì var.

Bảng dưới đây tóm tắt sự khác biệt giữa var, let và const

nhập mô tả hình ảnh ở đây


3

hãy là một phần của es6. Các chức năng này sẽ giải thích sự khác biệt một cách dễ dàng.

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}

3

Dưới đây cho thấy 'let' và 'var' khác nhau như thế nào trong phạm vi:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

Giá trị gfoođược xác định letban đầu nằm trong phạm vi toàn cầu và khi chúng ta khai báo gfoolại bên trong phạm viif clause của nó thay đổi và khi một giá trị mới được gán cho biến bên trong phạm vi đó thì nó không ảnh hưởng đến phạm vi toàn cầu.

Trong khi đó hfoo, được định nghĩa varban đầu là trong phạm vi toàn cầu , nhưng một lần nữa khi chúng ta khai báo nó bên trong if clause, nó xem xét phạm vi toàn cầu hfoo, mặc dù var đã được sử dụng lại để khai báo nó. Và khi chúng ta gán lại giá trị của nó, chúng ta thấy rằng phạm vi toàn cầu hfoo cũng bị ảnh hưởng. Đây là sự khác biệt chính.


2

Như được đề cập ở trên:

Sự khác biệt là phạm vi. varđược đặt trong phạm vi khối chức năng gần nhất và letnằm trong khối bao quanh gần nhất , có thể nhỏ hơn khối chức năng. Cả hai đều là toàn cục nếu bên ngoài bất kỳ khối nào. Hãy xem ví dụ:

Ví dụ 1:

Trong cả hai ví dụ của tôi, tôi có một chức năng myfunc. myfuncchứa một biến myvarbằng 10. Trong ví dụ đầu tiên của tôi, tôi kiểm tra xem có myvarbằng 10 ( myvar==10) không. Nếu có, tôi agian khai báo một biến myvar(bây giờ tôi có hai biến myvar) bằng cách sử dụng vartừ khóa và gán cho nó một giá trị mới (20). Trong dòng tiếp theo tôi in giá trị của nó trên bàn điều khiển của tôi. Sau khối điều kiện tôi lại in giá trị myvartrên bảng điều khiển của mình. Nếu bạn nhìn vào đầu ra của myfunc, myvarcó giá trị bằng 20.

để từ khóa

Ví dụ2: Trong ví dụ thứ hai của tôi thay vì sử dụng vartừ khóa trong khối điều kiện của tôi, tôi khai báo myvarbằng lettừ khóa. Bây giờ khi tôi gọi myfunc tôi nhận được hai đầu ra khác nhau: myvar=20myvar=10.

Vì vậy, sự khác biệt là rất đơn giản tức là phạm vi của nó.


3
Vui lòng không đăng hình ảnh về mã, nó được coi là thông lệ xấu trên SO vì nó sẽ không thể tìm kiếm được cho người dùng trong tương lai (cũng như các mối quan tâm về khả năng truy cập). Đồng thời, câu trả lời này không thêm gì mà các câu trả lời khác chưa được giải quyết.
inostia

2

Tôi muốn liên kết các từ khóa này với Bối cảnh thực thi, bởi vì Bối cảnh thực thi là quan trọng trong tất cả những điều này. Bối cảnh thực thi có hai giai đoạn: Giai đoạn sáng tạo và giai đoạn thực thi. Ngoài ra, mỗi Bối cảnh thực thi có Môi trường biến đổi và Môi trường bên ngoài (Môi trường từ điển).

Trong Giai đoạn tạo của bối cảnh thực thi, var, let và const vẫn sẽ lưu trữ biến của nó trong bộ nhớ với giá trị không xác định trong Môi trường biến đổi của bối cảnh thực thi đã cho. Sự khác biệt là trong Giai đoạn thực thi. Nếu bạn sử dụng tham chiếu một biến được xác định bằng var trước khi nó được gán một giá trị, nó sẽ không được xác định. Không có ngoại lệ sẽ được nâng lên.

Tuy nhiên, bạn không thể tham chiếu biến được khai báo bằng let hoặc const cho đến khi nó được khai báo. Nếu bạn cố gắng sử dụng nó trước khi nó được khai báo, thì một ngoại lệ sẽ được nêu ra trong Giai đoạn thực thi của bối cảnh thực thi. Bây giờ biến vẫn sẽ nằm trong bộ nhớ, nhờ vào Giai đoạn tạo của bối cảnh thực thi, nhưng Công cụ sẽ không cho phép bạn sử dụng nó:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

Với một biến được xác định bằng var, nếu Engine không thể tìm thấy biến trong Môi trường biến đổi của bối cảnh thực thi hiện tại, thì nó sẽ đi lên chuỗi phạm vi (Môi trường bên ngoài) và kiểm tra biến môi trường ngoài của môi trường bên ngoài để tìm biến. Nếu nó không thể tìm thấy nó ở đó, nó sẽ tiếp tục tìm kiếm Chuỗi phạm vi. Đây không phải là trường hợp với let và const.

Tính năng thứ hai của let là nó giới thiệu phạm vi khối. Các khối được xác định bởi dấu ngoặc nhọn. Ví dụ bao gồm các khối chức năng, nếu các khối, cho các khối, v.v. Khi bạn khai báo một biến với bên trong một khối, biến đó chỉ có sẵn bên trong khối. Trong thực tế, mỗi khi khối được chạy, chẳng hạn như trong vòng lặp for, nó sẽ tạo ra một biến mới trong bộ nhớ.

ES6 cũng giới thiệu từ khóa const để khai báo các biến. const cũng là khối phạm vi. Sự khác biệt giữa let và const là các biến const cần được khai báo bằng bộ khởi tạo, hoặc nó sẽ tạo ra lỗi.

Và cuối cùng, khi nói đến Bối cảnh thực thi, các biến được xác định bằng var sẽ được gắn vào đối tượng 'this'. Trong bối cảnh thực thi toàn cầu, đó sẽ là đối tượng cửa sổ trong các trình duyệt. Đây không phải là trường hợp cho phép hoặc const.


2

Tôi nghĩ rằng các điều khoản và hầu hết các ví dụ là một chút áp đảo, Vấn đề chính tôi có cá nhân với sự khác biệt là hiểu "Khối" là gì. Tại một số điểm tôi nhận ra, một khối sẽ là bất kỳ dấu ngoặc nhọn nào ngoại trừ IFcâu lệnh. một khung mở {của hàm hoặc vòng lặp sẽ xác định một khối mới, bất cứ thứ gì được xác định letbên trong nó, sẽ không có sẵn sau dấu ngoặc đóng }của cùng một thứ (hàm hoặc vòng lặp); Với ý nghĩ đó, nó dễ hiểu hơn:

let msg = "Hello World";

function doWork() { // msg will be available since it was defined above this opening bracket!
  let friends = 0;
  console.log(msg);

  // with VAR though:
  for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
  console.log(iCount2);
  
    for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
  console.log(iCount1);
  
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);


1

Bây giờ tôi nghĩ rằng có một phạm vi biến tốt hơn cho một khối các câu lệnh bằng cách sử dụng let:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

Tôi nghĩ mọi người sẽ bắt đầu sử dụng let here sau đó để họ có phạm vi tương tự trong JavaScript như các ngôn ngữ khác, Java, C #, v.v.

Những người không hiểu rõ về phạm vi trong JavaScript được sử dụng để mắc lỗi trước đó.

Tời kéo không được hỗ trợ sử dụng let.

Với cách tiếp cận này, lỗi trong JavaScript đang được loại bỏ.

Tham khảo ES6 In Depth: let and const để hiểu rõ hơn về nó.


Để hiểu sâu hơn về nó, hãy tham khảo liên kết - davidwalsh.name/for-and-against-let
swaraj patil

1

Bài viết này xác định rõ sự khác biệt giữa var, let và const

const là một tín hiệu cho thấy định danh sẽ không được gán lại.

let, là tín hiệu cho thấy biến có thể được gán lại, chẳng hạn như bộ đếm trong vòng lặp hoặc hoán đổi giá trị trong thuật toán. Nó cũng báo hiệu rằng biến sẽ chỉ được sử dụng trong khối được xác định trong đó, không phải lúc nào cũng là toàn bộ hàm chứa.

varbây giờ là tín hiệu yếu nhất có sẵn khi bạn xác định một biến trong JavaScript. Biến có thể được hoặc không được gán lại và biến có thể được sử dụng cho toàn bộ hàm hoặc chỉ cho mục đích của một khối hoặc vòng lặp.

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.