ECMAScript 2015: const in for vòng lặp


83

Đoạn mã nào trong hai (hoặc không / cả hai) bên dưới sẽ hoạt động trong quá trình triển khai ECMAScript 2015 hoàn chỉnh:

for (const e of a)

for (const i = 0; i < a.length; i += 1)

Theo hiểu biết của tôi, ví dụ đầu tiên sẽ hoạt động vì eđược khởi tạo cho mỗi lần lặp. Đây cũng không phải là trường hợp itrong phiên bản thứ hai?

Tôi bối rối vì các triển khai hiện có (Babel, IE, Firefox, Chrome, ESLint) dường như không nhất quán và có một triển khai hoàn chỉnh const, với các hành vi khác nhau của hai biến thể vòng lặp; Tôi cũng không thể tìm thấy một điểm cụ thể trong tiêu chuẩn, vì vậy điều đó sẽ được đánh giá cao.


1
const là hằng số xx, bạn nên sử dụng để thay
Patsy Issa

3
@JamesThorpe Không, câu hỏi của tôi chỉ đơn giản là cố gắng làm rõ hành vi nên là gì. Ví dụ, ESLint coi ví dụ đầu tiên là OK (và được ưu tiên với prefer-consttùy chọn) và ví dụ thứ hai không hợp lệ. Hầu hết các triển khai trình duyệt coi cả hai ví dụ không hợp lệ.
adrianp

4
AFAIK cái đầu tiên là OK vì nó được khởi tạo lại sau mỗi lần lặp. Nó hoạt động trong Chrome.
lyschoening

@lyschoening và điều này cũng không nên áp dụng cho ví dụ thứ hai?
adrianp

4
@adrianp chắc chắn không phải là ví dụ thứ hai. Vòng lặp for thông thường về cơ bản tương đương với{const i = 0; while(i < a.length) { /* for body */ i += 1}}
lyschoening

Câu trả lời:


91

Vòng lặp for-of sau hoạt động:

for (const e of a)

Đặc tả ES6 mô tả điều này là:

Đối với khai báo: LetOrConst ForBinding

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-in-and-for-of-statements-static-semantics-boundnames

Vòng lặp for bắt buộc sẽ không hoạt động:

for (const i = 0; i < a.length; i += 1)

Điều này là do khai báo chỉ được đánh giá một lần trước khi phần thân của vòng lặp được thực thi.

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-statement-runtime-semantics-labelledevaluation


3
Tại sao bạn liên kết đến bản nháp mà không phải là thông số kỹ thuật cuối cùng? ecma-international.org/ecma-262/6.0/index.html . Ngoài ra, bạn đang trích dẫn quy tắc đánh giá sai cho forvòng lặp. const ...không phải là một biểu thức. Bạn cần phải nhìn vào quy tắc cho for ( LexicalDeclaration Expression ; Expression) Statement .
Felix Kling

@FelixKling vẫn đánh dấu liên kết cũ. Bạn đã đúng về quy tắc đánh giá. Kết luận vẫn nên giữ nguyên vì một ràng buộc bất biến được tạo đúng một lần (trong bước 5)?
lyschoening

4
Đúng, nhưng tôi nghĩ manh mối ở bước 9. consts không được khai báo lại mỗi lần lặp.
Felix Kling

4
for (const e of a)Dường như KHÔNG hoạt động trong phiên bản Firefox mới nhất. Tôi đang nhậnSyntaxError: invalid for/in left-hand side
Chris_F

2
Tài liệu MDN cũng cho biết "Bạn có thể sử dụng const thay vì let, nếu bạn không gán lại biến bên trong khối." developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Phil Gibbins

44

Tôi sẽ không trích dẫn thông số kỹ thuật lần này, vì tôi nghĩ sẽ dễ hiểu hơn về những gì xảy ra bằng ví dụ.

for (const e of a) …

Về cơ bản tương đương với

{
    const __it = a[Symbol.iterator]();
    let __res;
    while ((__res = __it.next()) && !__res.done) {
        const e = __res.value;
        …
    }
}

Để đơn giản tôi đã lờ đi rằng có một TDZ với echo athể hiện và khác nhau __it.return()/ __it.throw(e)cuộc gọi trong trường hợp thoát vòng lặp non ( breakhoặc throwtrong cơ thể).

for (const i = 0; i < a.length; i += 1) …

về cơ bản tương đương với

{
    const i = 0;
    while (i < a.length) {
        …
        i += 1;
    }
}

Ngược lạilet , một constkhai báo trong một forvòng lặp không được khai báo lại trong mỗi lần lặp vòng lặp (và trình khởi tạo vẫn không được thực thi lại). Trừ khi bạn breaktrong lần lặp đầu tiên, ý chí của bạn i +=sẽ ném ở đây.


Đối với tôi (nút 5.0.0) vòng lặp for-of không hoạt động như mong đợi. Sau 'let a = [1,3,5], b = []' và 'for (const e of a) {b.push (e)}' Tôi nhận được 'b == [1, 1, 1]' . Một lỗi nút, hoặc hành vi dự định? Theo mã của bạn, tôi mong đợi một chỉ định 'const e = __res.value' mới cho mỗi lần lặp.
Jürgen Strobel

2
@ JürgenStrobel: Một lỗi nút cũ trong đó constvarphạm vi giống như và không thực hiện được nhiệm vụ. Sử dụng chế độ nghiêm ngặt.
Bergi

Tôi biết rằng điều này là cũ, nhưng một sự điều chỉnh nhẹ: while ( ( __res = __it.next() ) && !__res.done) {. Nếu không thì __reskết thúc là truehoặcfalse
JDB vẫn nhớ Monica

@JDB Cảm ơn, đã sửa! Btw, tôi sẽ ổn nếu bạn vừa chỉnh sửa nó.
Bergi

3

Ví dụ thứ hai của bạn chắc chắn sẽ không hoạt động vì i được khai báo một lần và không phải trên mỗi lần lặp, đây chỉ là một hàm về cách hoạt động của loại vòng lặp đó.

Bạn có thể thử điều này trong một trình duyệt thông thường:

for (var i = 0, otherVar = ""; i < [1,2,3,4].length; i += 1){
  console.log(otherVar)
  otherVar = "If otherVar was initialized on each iteration, then you would never read me.";
}

Nó không phải là trường hợp consthoàn toàn không được phép trong forcác vòng lặp. Chỉ cófor điều đó sẽ sửa đổi const là.

Những điều này hợp lệ:

for(const i = 0;;){ break } 
for(const i = 0; i < 10;){ break; } 

Chúng không hợp lệ:

for(const i = 0;;){ ++i; break; } 
for(const i = 0;;++i){ if(i > 0) break; }

Tôi không chắc tại sao Firefox lại đưa ra lỗi SyntaxError sau khi đọc thông số ES2015 (mặc dù tôi chắc chắn rằng những người thông minh ở Mozilla là đúng), có vẻ như nó phải đưa ra một ngoại lệ:

Tạo một liên kết bất biến mới nhưng chưa được khởi tạo trong Bản ghi môi trường. Giá trị chuỗi N là văn bản của tên liên kết. Nếu S là true thì việc cố gắng truy cập giá trị của ràng buộc trước khi nó được khởi tạo hoặc đặt nó sau khi nó được khởi tạo sẽ luôn ném ra một ngoại lệ, bất kể cài đặt chế độ nghiêm ngặt của các hoạt động tham chiếu đến ràng buộc đó. S là một tham số tùy chọn được mặc định là false.


Làm thế nào bạn có thể nói điều đó mà không cần trích dẫn nguồn? Tại sao đó là trường hợp cho constmà không phải cho let?
Felix Kling

@FelixKling Bạn nghĩ câu nào cần trích dẫn? constkhông cho phép chính nó được chỉ định lại (điều này không gây tranh cãi) và như ví dụ của tôi chứng minh rõ ràng cách forhoạt động đối với phần định nghĩa biến trái với mong đợi ngụ ý của anh ấy. Giá trị của letcó thể được thay đổi, về mặt chức năng nó tương đương với vartrong ví dụ đã cho, nhưng letkhông phải là một phần của câu hỏi.
Kit Sunde

1
Vì vậy, bạn có nghĩa là rõ ràng rằng const ichỉ được khai báo một lần, nhưng let isẽ không? Ví dụ của bạn chỉ thể hiện cách var ihoạt động, không phải const i. Kể từ khi có sự khác biệt rõ ràng giữa var, constlet, tôi nghĩ với lý do ông spec đối với constsẽ rất có giá trị.
Felix Kling

1
@FelixKling Bạn đang đọc nhầm những gì tôi đang nhập. Tôi đang nói mọi thứ trong phần đó của vòng lặp for được khai báo một lần. Khi đó constgiá trị trực giao chỉ có thể được gán một lần, mọi nỗ lực khai báo lại constsẽ không hoạt động. Ví dụ của tôi là để chứng minh thực tế rằng việc khai báo chỉ xảy ra một lần, người đặt câu hỏi hiểu được ngữ nghĩa của const. letkhông phải là vấn đề, và không, tôi không có ý rõ ràng những gì bạn đang đề xuất, bạn đã đọc sai.
Kit Sunde

3
Đã nhận xét trong cuộc trò chuyện: Nếu bạn sử dụng let, thì mỗi lần lặp lại nhận được bản sao của chính nó i. for(let foo = 0; i < 10; ++i){} tương đương với (funtion() { for(var i = 0; i < 10; ++i){ (function(i) { }(i)) } }()); Đây là nơi tôi đến: letconstcả hai đều nằm trong phạm vi khối. for/infor/ofhiển thị cùng một hành vi đối với constlet, nhưng forvòng lặp bình thường thì không. Nó rõ ràng xử lý constkhác nhau (có thể hiểu được). Bạn chỉ đơn giản nói rằng nó được "khai báo một lần" nhưng điều đó đơn giản hóa nó quá nhiều IMO.
Felix Kling
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.