Câu hỏi Sự khác biệt giữa việc sử dụng "let" và "var" để khai báo một biến trong JavaScript là gì?


ECMAScript 6 đã giới thiệu các let tuyên bố. Tôi đã nghe nó mô tả như là một biến "địa phương", nhưng tôi vẫn không hoàn toàn chắc chắn cách nó hoạt động khác với var từ khóa.

Sự khác biệt là gì? Khi nào nên let được sử dụng trên var?


3259
2018-04-17 20:09


gốc


ECMAScript là tiêu chuẩn và let được bao gồm trong Bản nháp phiên bản thứ 6 và rất có thể sẽ nằm trong đặc tả cuối cùng. - Richard Ayotte
Xem kangax.github.io/es5-compat-table/es6 cho một ma trận hỗ trợ cập nhật các tính năng ES6 (bao gồm cả cho phép). 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 FF không hoàn toàn tiêu chuẩn). - Nico Burns
Trong thời gian dài nhất tôi không biết rằng các vars trong một vòng lặp for đã được scoped với chức năng nó được bọc trong. Tôi nhớ đã tìm ra điều này lần đầu tiên và nghĩ rằng nó là rất ngu ngốc. Tôi thấy một số quyền lực mặc dù biết bây giờ làm thế nào hai có thể được sử dụng ffor lý do khác nhau và làm thế nào trong một số trường hợp bạn thực sự có thể muốn sử dụng một var trong vòng lặp for và không có nó scoped vào khối. - Eric Bishard
Khi tính năng hỗ trợ tính năng ES6 được cải thiện, câu hỏi liên quan đến việc áp dụng ES6 thay đổi tập trung từ hỗ trợ tính năng đến hiệu suất khác biệt. Như vậy, đây là một trang web tôi đã tìm thấy sự khác biệt về hiệu suất điểm chuẩn giữa ES6 và ES5. Hãy ghi nhớ điều này sẽ có khả năng thay đổi theo thời gian khi các công cụ tối ưu hóa cho mã ES6. - timolawl
Đây là một bài đọc rất hay wesbos.com/javascript-scoping - onmyway133


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


Sự khác biệt là phạm vi. var được đưa vào khối chức năng gần nhất và let được scoped đến gần nhất bao quanh khối, có thể nhỏ hơn khối chức năng. Cả hai đều là toàn cầu nếu bên ngoài bất kỳ khối nào.

Ngoài ra, các biến được khai báo với let không thể truy cập trước khi chúng được khai báo trong khối kèm theo của chúng. Như đã thấy trong bản demo, điều này sẽ ném một ngoại lệ ReferenceError.

Bản giới thiệu: 

var html = '';

write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible

try {
  write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
  write('globalLet: exception');
}

write('\nset variables');

var globalVar = 'globalVar';
let globalLet = 'globalLet';

write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);

function functionScoped() {
  write('\n#### function ####');
  write('\nfunctionVar: ' + functionVar); //undefined, but visible

  try {
    write('functionLet: ' + functionLet); //undefined, *not* visible
  } catch (exception) {
    write('functionLet: exception');
  }

  write('\nset variables');

  var functionVar = 'functionVar';
  let functionLet = 'functionLet';

  write('\nfunctionVar: ' + functionVar);
  write('functionLet: ' + functionLet);
}

function blockScoped() {
  write('\n#### block ####');
  write('\nblockVar: ' + blockVar); //undefined, but visible

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }

  for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
    write('\nblockVar: ' + blockVar); // visible here and whole function
  };

  for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
    write('blockLet: ' + blockLet); // visible only here
  };

  write('\nblockVar: ' + blockVar);

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }
}

function write(line) {
  html += (line ? line : '') + '<br />';
}

functionScoped();
blockScoped();

document.getElementById('results').innerHTML = html;
<pre id="results"></pre>

Toàn cầu:

Chúng rất giống nhau khi được sử dụng như thế này bên ngoài một khối chức năng.

let me = 'go';  // globally scoped
var i = 'able'; // globally scoped

Tuy nhiên, các biến toàn cục được xác định bằng let sẽ không được thêm dưới dạng thuộc tính trên toàn cầu window đối tượng như được xác định với var.

console.log(window.me); // undefined
console.log(window.i); // 'able'

Chức năng:

Chúng giống hệt nhau khi được sử dụng như thế này trong một khối chức năng.

function ingWithinEstablishedParameters() {
    let terOfRecommendation = 'awesome worker!'; //function block scoped
    var sityCheerleading = 'go!'; //function block scoped
}

Khối:

Đây là sự khác biệt. let chỉ hiển thị trong for() vòng lặp và var hiển thị cho toàn bộ hàm.

function allyIlliterate() {
    //tuce is *not* visible out here

    for( let tuce = 0; tuce < 5; tuce++ ) {
        //tuce is only visible in here (and in the for() parentheses)
        //and there is a separate tuce variable for each iteration of the loop
    }

    //tuce is *not* visible out here
}

function byE40() {
    //nish *is* visible out here

    for( var nish = 0; nish < 5; nish++ ) {
        //nish is visible to the whole function
    }

    //nish *is* visible out here
}

Tuyên bố lại:

Giả sử chế độ nghiêm ngặt, var sẽ cho phép bạn khai báo lại cùng một biến trong cùng một phạm vi. Mặt khác, let sẽ không:

'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
'use strict';
var me = 'foo';
var me = 'bar'; // No problem, `me` is replaced.

4571
2018-05-27 10:16



Vì vậy, mục đích của báo cáo chỉ để giải phóng bộ nhớ khi không cần thiết trong một khối nhất định? - NoBugs
@NoBugs, Có, và nó được khuyến khích rằng các biến tồn tại chỉ khi chúng cần thiết. - batman
let biểu thức khối let (variable declaration) statement không chuẩn và sẽ bị xóa trong tương lai, bugzilla.mozilla.org/show_bug.cgi?id=1023609. - Gajus
họ là một sự khác biệt trong phạm vi toàn cầu: let không thêm thuộc tính vào biến toàn cục 2ality.com/2015/02/es6-scoping.html#the_global_object - Yukulélé
@ThinkingStiff: Vui lòng xem lại bài meta nàyvà cân nhắc các vấn đề kỹ thuật được nêu ra. - Robert Harvey♦


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

BẢN GIỚI THIỆU

for(var i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

Mã trên chứng minh một vấn đề đóng cửa JavaScript cổ điển. Tham chiếu đến i biến đang được lưu trữ trong trình 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 bởi vì chỉ có một đối tượng truy cập chứa 6 để bạn nhận được sáu trên mỗi nhấp chuột.

Giải pháp chung là để bọc nó trong một hàm ẩn danh và vượt qua i làm đối số. Những vấn đề như vậy cũng có thể tránh được bằng cách sử dụng let thay thế var như thể hiện trong mã dưới đây.

BẢN GIỚI THIỆU (Đã thử nghiệm trong Chrome và Firefox 50)

'use strict';

for(let i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

451
2018-04-17 20:11



Đó là thực sự mát mẻ. Tôi mong đợi "i" được định nghĩa bên ngoài thân vòng lặp chứa trong ngoặc và KHÔNG tạo thành một "đóng cửa" xung quanh "i". Tất nhiên ví dụ của bạn chứng minh bằng cách khác. Tôi nghĩ rằng nó là một chút bối rối từ quan điểm cú pháp của xem nhưng kịch bản này là như vậy phổ biến nó có ý nghĩa để hỗ trợ nó theo cách đó. Rất cám ơn vì đã mang cái này lên. - Karol Kolenda
Hỗ trợ IE 11 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 như thế nào không let được cho là cư xử? - Jim Hunziker
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/… - Jim Hunziker
Thật vậy đây là một lỗ hổng phổ biến trong Javascript và bây giờ tôi có thể thấy lý do tại sao let sẽ thực sự hữu ích. Thiết lập trình lắng nghe sự kiện trong một vòng lặp không còn đòi hỏi một biểu thức hàm gọi hàm immediatelly cho phạm vi cục bộ i tại mỗi lần lặp. - Adrian Moisa
Việc sử dụng "let" chỉ giải quyết vấn đề này. Vì vậy, mỗi phép 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 lặp không phải là thông thường thay đổi trong khối, nhưng các biến khai báo khác trong khối có thể là) 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 làm chia sẻ cùng một phạm vi khối riêng tư do đó cùng tham chiếu đến "i". - gary


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

cho phép hoạt động rất giống với var. Sự khác biệt chính là phạm vi của biến var là toàn bộ hàm bao hàm

Cái bàn 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ác trình duyệt Mozilla và Chrome mới hỗ trợ nó. IE, Safari và những người khác có khả năng không.


131
2018-02-23 18:35



Các bit quan trọng của văn bản từ các tài liệu liên kết có vẻ là, "hãy hoạt động 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ộ kèm theo chức năng". - Michael Burr
Mặc dù về mặt kỹ thuật chính xác để nói rằng IE không hỗ trợ nó, nó chính xác hơn để nói rằng đó là một phần mở rộng chỉ mozilla. - olliej
@olliej, trên thực tế Mozilla đang ở phía trước của trò chơi. Xem trang 19 của ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf - Tyler Crompton
@ TylerCrompton đó chỉ là tập hợp các từ đã được bảo lưu trong nhiều năm. Khi mozilla thêm vào, nó hoàn toàn là phần mở rộng mozilla, không có thông số kỹ thuật liên quan. ES6 nên xác định hành vi cho 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ớ rằng moz cũng có E4X, đó là hoàn toàn chết và moz chỉ. - olliej
IE11 thêm hỗ trợ cho let  msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx - eloyesp


Sự khác biệt giữa let và var?

  • Biến được xác định bằng cách sử dụng var tuyên bố được biết đến trong suốt chức năng nó được định nghĩa trong, từ khi bắt đầu hàm. (*)
  • Biến được xác định bằng cách sử dụng let tuyên bố chỉ được biết đến trong khối nó được định nghĩa trong, 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:

// 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 j chỉ đượ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 ta i được biết đến trong toàn bộ hàm.

Ngoài ra, hãy xem xét rằng các biến 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 treo. Bạn cũng không được phép redeclare cùng một khối scoped biến trong cùng một khối. Điều này làm cho các biến có phạm vi khối ít bị lỗi hơn các biến toàn cục hoặc hàm có phạm vi, được hoisted và không tạo ra bất kỳ lỗi nào trong trường hợp có nhiều khai báo.


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

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

Hôm nay, tuy nhiên, đó chắc chắn không phải là trường hợp. Thực ra, chúng ta cần thực sự tự hỏi liệu có an toàn khi sử dụng let tuyên bố. 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 an toàn let tuyên bố.

  • Nếu bạn đang viết mã JavaScript phía máy khách và sử dụng trình chuyển đổi (như Traceur), bạn có thể sử dụng an toàn let tuyên bố, tuy nhiên mã của bạn có thể là bất cứ điều gì nhưng tối ưu đối với 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 trình chuyển đổi, bạn cần xem xét hỗ trợ trình duyệt.

Hôm nay, ngày 8 tháng 6 năm 2018, vẫn còn một số trình duyệt không hỗ trợ let!

enter image description here


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

Để biết tổng quan cập nhật về trình duyệt nào hỗ trợ let tuyên bố tại thời điểm bạn đọc câu trả lời này, xem điều này Can I Use trang.


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

(**) Chặn các biến phạm vi không được treo


116
2018-06-02 20:59



về câu trả lời v4: i IS được biết đến ở khắp 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: let cũng được treo (lên trên cùng của nó có chứa khối), nhưng sẽ cung cấp cho một ReferenceError khi được tham chiếu trong khối trước khi chuyển nhượng lần đầu. (ps2: Tôi là một người đàn ông bán dấu chấm phẩy nhưng bạn thực sự không cần dấu chấm phẩy sau một khối). Điều đó đang được nói, cảm ơn bạn đã thêm sự kiểm tra thực tế về hỗ trợ! - GitaarLAB
@ GitaarLAB: Theo Mạng nhà phát triển Mozilla : "Trong ECMAScript 2015, hãy để các ràng buộc không phải chịu Hoisting, có nghĩa là để cho các khai báo không di chuyển lên đầu ngữ cảnh thực thi hiện hành." - Dù sao, tôi đã thực hiện một vài cải tiến đối với câu trả lời của tôi, nên làm rõ sự khác biệt trong hành vi cẩu giữa let và var! - John Slegers
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ưu ý rằng cùng một liên kết mà bạn tham chiếu trong nhận xét của bạn cũng cho biết: "Biến (let) nằm trong" vùng chết tạm thời "từ bắt đầu khối cho đến khi khởi tạo được xử lý. "Điều đó có nghĩa là 'số nhận dạng' (chuỗi văn bản 'được bảo lưu' để trỏ đến 'cái gì đó') đã được đặt 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ì hơn là đặt / liên kết khai báo 'định danh' 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 Kyle Simpson mà bạn đã liên kết là Xuất sắc đọc, 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 rằng let và const là được đề 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ì thi hành / kiểm tra các tính năng bổ sung này (như viết chỉ const) dẫn đến 'nhiều công việc hơn' (và các nút phạm vi bổ sung trong phạm vi cây) cho (hiện tại) động cơ (s) để thực thi / kiểm tra / xác minh / thiết lập . - GitaarLAB


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

{
  let a = 123;
};

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

98
2018-04-17 21:38



Câu trả lời được chấp nhận giải thích điểm này. - TM.
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 for loop initializer, thu hẹp đáng kể phạm vi áp dụng các giới hạn của let. Được thăng hạng. - Jon Davis
@ stimpy77 Nó tuyên bố rõ ràng "hãy để phạm vi đến khối bao quanh gần nhất"; sao mọi cách biểu lộ cần được đưa vào? - Dave Newton
có rất nhiều ví dụ và không ai trong số họ chứng minh đúng vấn đề .. Tôi có thể đã bỏ phiếu cho cả câu trả lời được chấp nhận và câu trả lời này? - Jon Davis
Sự đóng góp này chứng minh rằng một "khối" có thể chỉ đơn giản là một tập hợp các dòng được bao trong dấu ngoặc đơn; nghĩa là nó 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 nào, v.v. - webelo


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 có ít nhiều bất kỳ ngôn ngữ nào khác.

ví dụ. Nó phạm vi đến khối bao quanh, Chúng không tồn tại trước khi chúng được khai báo, v.v.

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


40
2018-03-06 10:41



Cũng cần lưu ý rằng ECMAScript là tiêu chuẩn và let được bao gồm trong Bản nháp phiên bản thứ 6 và rất có thể sẽ nằm trong đặc tả cuối cùng. - Richard Ayotte
Đó là sự khác biệt 3 năm làm cho: D - olliej
Chỉ cần lúng túng trong câu hỏi này và trong năm 2012 vẫn là trường hợp chỉ hỗ trợ trình duyệt Mozilla let. Safari, IE và Chome đều không. - pseudosavant
Ý tưởng vô tình tạo ra một phần phạm vi khối về tai nạn là một điểm tốt, hãy cẩn thận, let không hoist, sử dụng một biến được xác định bởi một let được xác định ở đầu khối của bạn. Nếu bạn có một if tuyên bố đó 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 sau khi nó được định nghĩa. ĐIỂM TUYỆT VỜI !!! - Eric Bishard
@EricB: có và không: "Trong ECMAScript 2015, let  sẽ nâng kính biến ở đầu khối. Tuy nhiên, tham chiếu biến trong khối trước khi khai báo biến kết quả trong một Tham chiếuError (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" báo cáo chuyển đổi vì chỉ có một khối cơ bản ". developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… - GitaarLAB


Đây là một ví dụ cho sự khác biệt giữa hai (hỗ trợ chỉ mới bắt đầu cho chrome): enter image description here

Như bạn có thể thấy var j biến vẫn có giá trị nằm ngoài phạm vi vòng lặp (Phạm vi khối), nhưng let i biến là không 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);


39
2017-11-23 22:52



Tôi đang xem công cụ gì ở đây? - Barton
Chrome devtools - vlio20
Là một nhà phát triển các ứng dụng máy tính để bàn cho Quế, tôi đã không được tiếp xúc với các công cụ sáng bóng như vậy. - Barton


let

Phạm vi khối

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

Ở cấp cao nhất (bên ngoài một hàm)

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

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 hàm

Bên trong một funciton (nhưng bên ngoài một khối), let có 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 let bê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 let trong vòng lặp có thể được tham chiếu chỉ 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 dùng let thay vì var trong 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

Bởi vì vùng chết tạm thời, các biến được khai báo sử dụng let không thể truy cập trước khi chúng được khai báo. Cố gắng làm như vậy sẽ 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 let. Bạn cũng không thể khai báo biến bằng cách sử dụng let với cùng số nhận dạng 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

const khá giống với let—Nó có dạng khối và có TDZ. Tuy nhiên, có hai điều khác nhau.

Không gán lại

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

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

Lưu ý rằng nó không có nghĩa là giá trị là không thay đổi. Các thuộc tính của nó vẫn có thể 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().

Trình khởi tạo là bắt buộc

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

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

38
2018-01-17 15:11





  • Biến không nâng

    let sẽ không nâng kính cho toàn bộ phạm vi của khối mà chúng xuất hiện. Ngược lại, var có thể hoist như dưới đây.

    {
       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 var và let được nâng lên.

  • Thu gom rác thải

    Phạm vi khối của let là hữu ích liên quan đến đóng cửa và thu gom rác thải để đòi 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){
        //....
    });
    

    Các click handler gọi lại không cần hugeData thay đổi. Về mặt lý thuyết, sau process(..) chạy, cấu trúc dữ liệu khổng lồ hugeData có thể là rác được thu thập. Tuy nhiên, có thể một số động cơ JS vẫn phải giữ cấu trúc khổng lồ này, vì click chức năng có một đó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

    let trong vòng lặp có thể liên kết lại nó cho mỗi lần lặp của vòng lặp, đảm bảo gán lại giá trị đó từ cuối vòng lặp 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ế var với let

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

    Bởi vì let tạ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), chi tiết hơn là đây.


20
2018-03-22 14:39



Thực ra chúng vẫn được treo - Bergi
Yip họ được treo, nhưng cư xử như thể không được treo bởi vì (trống cuộn) Temporal Dead Zone - một 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, cho là hoisted, nhưng không có sẵn? Làm thế nào là khác với 'không hoisted'? - N-ate
Hy vọng rằng Brian hay Bergi sẽ quay lại để trả lời câu hỏi này. Có phải tuyên bố cho phép hoisted, nhưng không phải là nhiệm vụ? Cảm ơn! - N-ate
@ N-ate, Đây là một bài của Bergi, có lẽ bạn có thể tìm thấy câu trả lời trong đó. - zangw


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

để cho cho phép bạn khai báo các biến được giới hạn trong phạm vi cho khối, câu lệnh hoặc biểu thức mà trên đó nó được sử dụng. Điều này không giống như var từ khóa, định nghĩa biến trên toàn cầu 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ởi để cho có phạm vi của chúng là khối mà chúng được định nghĩa, cũng như trong bất kỳ khối con nào có chứa. Bằng cách này, để cho hoạt động rất giống var. Sự khác biệt chính là phạm vi của một var biến là toàn bộ hàm bao quanh:

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 các chương trình và chức năng, để cho, không giống var, không tạo thuộc tính trên đối tượng chung. 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 đó cho khối đó. Lưu ý sự khác biệt giữa var có phạm vi nằm bên trong hàm mà 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

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


15
2017-08-18 00:58