2016-02-24 18 views
6

Tôi hiểu rằng let sẽ được kéo lên đến đỉnh của khối, nhưng truy cập vào nó trước khi khởi tạo sẽ ném ReferenceError do là để Temporal Dead ZoneMục đích của việc đặt cẩu trong ES6 là gì?

Ví dụ:

console.log(x); // Will throw Reference Error 
let x = 'some value'; 

Nhưng một đoạn như thế này sẽ chạy mà không có lỗi:

foo(); // alerts foo; 
function foo(){ // foo will be hoisted 
    alert("foo"); 
} 

câu hỏi của tôi

Mục đích của let được đưa lên đầu trang khi nào nó sẽ phát hiện lỗi khi truy cập? Ngoài ra làm var cũng bị TDZ, tôi biết khi nào nó sẽ ném undefined nhưng là vì TDZ?

+0

Bản sao có thể có của từ khóa ["let" so với từ khóa "var"] (http://stackoverflow.com/questions/762011/let -keyword-vs-var-keyword) –

+1

@ MarcosPérezGudemy nghi ngờ có liên quan đến cẩu. Do cẩu để cho làm cho bất kỳ sự khác biệt mặc dù nó ném một lỗi khi truy cập. Nhưng liên kết bạn chia sẻ cho chúng tôi biết thêm về sự khác biệt giữa let và var – brk

+0

Ok xin lỗi, tôi không biết cẩu là gì, nhưng bây giờ tất cả là rõ ràng. Tôi xóa phiếu bầu trùng lặp của mình. –

Trả lời

4

Các documentation nói:

The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer's AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

Ngoài ra var keyword:

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Bạn cũng có thể kiểm tra bài viết này bằng cách Kyle Simpson: For and against let

0

Bạn cần phải đầu tiên hiểu xuồng. Đang bắt đầu khai báo mã ở đầu khối, hãy xem xét ví dụ sau

function getValue(condition) { 
    if (condition) { 
     var value = "blue"; 
     // other code 
     return value; 
    } else { 
     // value exists here with a value of undefined 
     return null; 
    } 
     // value exists here with a value of undefined 
} 

vì bạn có thể thấy giá trị có thể truy cập được và cũng có thể hoạt động. Khi nó được khai báo ngay sau hàm getValue (điều kiện).

function getValue(condition) { 
    if (condition) { 
     let value = "blue"; 
     // other code 
     return value; 
    } else { 
     // value doesn't exist here 
     return null; 
    } 
    // value doesn't exist here 
} 

Nhưng khi chúng tôi sử dụng, bạn có thể thấy sự khác biệt. Các ví dụ được lấy từ một cuốn sách tôi đang đọc và khuyên bạn cũng để xem

https://leanpub.com/understandinges6/read#leanpub-auto-var-declarations-and-hoisting

để làm rõ thêm

1

http://www.2ality.com/2015/10/why-tdz.html giải thích nó theo một cách tốt đẹp và cũng liên kết tới https://mail.mozilla.org/pipermail/es-discuss/2012-September/024996.html đó là một cuộc thảo luận liên quan về chủ đề.

Trích tóm lược nội dung cho câu hỏi này

Why is there a temporal dead zone for let ?

  1. Nếu TDZ không gây ra một lỗi tham khảo, và bạn truy cập một biến trước khi tuyên bố của nó (tức là trong TDZ) bạn muốn (có lẽ hầu hết) thiếu một lỗi lập trình. TDZ gây ra lỗi tham chiếu giúp bạn nắm bắt sai lầm lập trình của mình.

  2. Câu hỏi tiếp theo của bạn sẽ là - tại sao thậm chí có TDZ cho let? Tại sao không bắt đầu phạm vi biến số let khi được khai báo? Câu trả lời là const.TDZs là dành cho const và (nghèo) let đã bị mắc kẹt với TDZ chỉ để làm cho nó dễ dàng hơn để chuyển đổi giữa các letconst


Also do var also suffer from TDZ, I know when it will throw undefined but is it because of TDZ?

Không, var không bị TDZ. Nó không ném bất kỳ lỗi nào. Nó chỉ đơn giản là undefined cho đến khi thiết lập khác. TDZ là một điều ES6.

-1

A let biến không được treo. Nói một biến số let là 'hoisted' về mặt kỹ thuật là chính xác nhưng theo ý kiến ​​của tôi, việc sử dụng thuật ngữ này là sai lạc. Một cách tương đương để mô tả ngữ nghĩa là bạn nhận được một ReferenceError khi bạn cố gắng tham khảo nó ở trên tuyên bố của nó bởi vì nó chưa tồn tại; điều tương tự bạn sẽ nhận được nếu bạn cố gắng tham chiếu đến một biến không tồn tại ở bất kỳ đâu trong khối đó.

Thông tin thêm:

C++ và JavaScript cả hai đều có chặn Phạm vi nhưng khác nhau ở điểm cụ thể này, vì vậy chúng ta có thể hiểu được điểm này bằng cách tìm hiểu cách họ cư xử khác nhau. Hãy xem xét ví dụ sau:

#include <iostream>               

int main() { 
    int x = 3; 

    { 
     std::cout << x << std::endl; 
     int x = 4; 
    } 

    return 0; 
} 

Trong C++ có thực sự là không có cẩu, thứ hai x không tồn tại khi dòng cout chạy (mà in x vào màn hình), nhưng x đầu tiên vẫn có, và do đó chương trình ngoan ngoãn in 3. Đó là khá khó hiểu. Thay vào đó, chúng tôi nên xem xét tham chiếu đó đến x để không rõ ràng và gây lỗi.

Đây là những gì xảy ra trong mã JavaScript analagous:

'use strict';                

let x = 3; 

(() => { 
    console.log(x); 
    let x = 4; 
})(); 

Trong JavaScript, vấn đề này đã được cố định bởi 'cẩu' thứ hai x, nhưng làm cho nó ném một ReferenceError khi truy cập. Theo như tôi biết, điều này 'hoisting' tương đương với việc tham chiếu đến x một lỗi do sự mơ hồ, như nó phải được.

+0

cho phép sẽ kéo biến lên đầu khối.https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let – brk

+0

Ok chắc chắn, thông số mô tả điều này là cẩu nhưng imo đó là một mô tả gây hiểu lầm. Các hành vi bạn nhận được khi thực hiện spec là giống như nếu không có cẩu và biến nằm ngoài phạm vi. – voltrevo

+0

@ user2181397 Tôi đã thêm bản chỉnh sửa giải thích chi tiết hơn. – voltrevo

0

What is purpose of let getting hoisted to top when it will throw an error on accessing?

Đó là vì vậy chúng tôi có thể có phạm vi khối, đó là khái niệm khá dễ hiểu, mà không có khối tương đương với var cẩu, mà là một nguồn truyền thống của lỗi và hiểu lầm.

xem xét bên trong khối này:

{ 
    let a = 1; 
    console.log(a); 
    let b = 2; 
    console.log(a, b); 
    let c = 3; 
    console.log(a, b, c); 
} 

Các nhà thiết kế có ba lựa chọn chính ở đây:

  1. Có phạm vi khối nhưng với tất cả các tờ khai kéo lên đầu trang và dễ tiếp cận (như var là trong các chức năng); hoặc
  2. Không có phạm vi chặn và thay vào đó có phạm vi bắt đầu mới với mỗi let, const, class, v.v ...; hoặc
  3. Có phạm vi khối, với treo (hoặc những gì tôi gọi là "nửa treo"), nơi mà các tờ khai được kéo lên, nhưng định danh họ tuyên bố là không thể tiếp cận cho đến khi họ đang đạt được trong mã

Tùy chọn   1 để chúng tôi mở ra cùng một loại lỗi mà chúng tôi có với var cẩu. Tùy chọn   2 là cách cách phức tạp hơn để mọi người hiểu và nhiều công việc hơn cho công cụ JavaScript để thực hiện (chi tiết bên dưới nếu bạn muốn). Tùy chọn   3 truy cập vào vị trí ngọt ngào: Phạm vi khối dễ hiểu và dễ triển khai và TDZ ngăn chặn các lỗi giống như lỗi gây ra bởi việc lắp đặt var.

Also do var also suffer from TDZ,I know when it will throw undefined but is it because of TDZ?

Không, var tuyên bố không có TDZ. undefined không phải là ném, nó chỉ là giá trị mà một biến có khi được khai báo nhưng không được đặt thành bất kỳ thứ gì khác (chưa). var khai báo được đưa lên đầu của hàm hoặc môi trường toàn cục và có thể truy cập đầy đủ trong phạm vi đó, ngay cả trước khi đạt được dòng var.


Nó có thể giúp hiểu được cách thức giải quyết nhận dạng được xử lý trong JavaScript:

Các đặc điểm kỹ thuật định nghĩa nó trong điều kiện của một cái gì đó gọi là lexical environment, which contains an environment record, chứa các thông tin về các biến, hằng số, thông số chức năng (nếu có liên quan), class tuyên bố, v.v. cho ngữ cảnh hiện tại. (A context là một thực thi cụ thể của một phạm vi. Tức là, nếu chúng ta có một hàm gọi là example, phần thân của example định nghĩa một phạm vi mới, mỗi lần chúng ta gọi là example, có các biến mới, v.v.   — đó là bối cảnh.)

các thông tin về một định danh (biến, vv), được gọi là một ràng buộc. Nó chứa tên của định danh, giá trị hiện tại của nó và một số thông tin khác về nó (như là nó có thể thay đổi hoặc không thay đổi được, cho dù nó có thể truy cập được chưa, vv).

Khi thực thi mã nhập một ngữ cảnh mới (ví dụ, khi hàm được gọi, hoặc nhập một khối có chứa let hoặc tương tự), công cụ JavaScript sẽ tạo * một đối tượng môi trường từ vựng mới (LEO), với môi trường của nó ghi lại (envrec), và cung cấp cho LEO một liên kết đến LEO "bên ngoài" có chứa nó, tạo thành một chuỗi. Khi động cơ cần tìm kiếm một mã định danh, nó tìm kiếm một ràng buộc trong envrec của LEO trên cùng và, nếu tìm thấy, sử dụng nó; nếu không tìm thấy, hãy nhìn vào LEO tiếp theo trong chuỗi, v.v. cho đến khi chúng ta đến cuối chuỗi. (Bạn có thể đã đoán: Liên kết cuối cùng trong chuỗi là dành cho môi trường toàn cầu.)

Những thay đổi trong ES2015 để kích hoạt phạm vi chặn và let, const, v.v.đã cơ bản:

  • Một LEO mới có thể được tạo ra cho một khối, nếu khối có chứa khai báo khối chỉnh phạm vi
  • Bindings trong một LEO thể được đánh dấu "không thể tiếp cận" nên TDZ có thể được thi hành

với tất cả những gì trong tâm trí, chúng ta hãy nhìn vào mã này:

function example() { 
    console.log("alpha"); 
    var a = 1; 
    let b = 2; 
    if (Math.random() < 0.5) { 
     console.log("beta"); 
     let c = 3; 
     var d = 4; 
     console.log("gamma"); 
     let e = 5; 
     console.log(a, b, c, d, e); 
    } 
} 

Khi example được gọi là, làm thế nào để động cơ xử lý đó (ít nhất, về mặt spec)? Như thế này:

  1. Nó tạo ra một LEO cho bối cảnh của cuộc gọi để example
  2. Nó cho biết thêm bindings cho a, bd để envrec của LEO rằng, tất cả với giá trị undefined:
    • a được thêm vào vì nó là một liên kết var nằm ở bất kỳ vị trí nào trong hàm. Cờ "có thể truy cập" được đặt thành true (vì var).
    • b được thêm bởi vì đó là một liên kết let ở cấp cao nhất của hàm; cờ "có thể truy cập" được đặt thành false bởi vì chúng tôi chưa đạt đến dòng let b.
    • d vì đó là một liên kết var, như a.
  3. Nó thực hiện console.log("alpha").
  4. Nó thực hiện a = 1, thay đổi giá trị của ràng buộc cho a từ undefined thành 1.
  5. Nó thực hiện let b, thay đổi cờ "có thể truy cập" của ràng buộc b thành true.
  6. Nó thực hiện b = 2, thay đổi giá trị của liên kết cho b từ undefined thành 2.
  7. Nó đánh giá Math.random() < 0.5; giả sử đó là sự thật:
  8. Bởi vì khối chứa định danh khối chỉnh phạm vi, động cơ sẽ tạo ra một LEO mới cho khối, thiết lập "bên ngoài" của nó LEO đến một trong những tạo ra trong Bước   1.
  9. Nó cho biết thêm bindings cho ce với sự ghen tị của LEO, với cờ "có thể truy cập" được đặt thành false và giá trị được đặt thành undefined.
  10. Nó thực hiện console.log("beta").
  11. Nó thực hiện let c, đặt cờ "có thể truy cập" của c của ràng buộc thành đúng
  12. Nó thực hiện c = 3.
  13. Nó thực hiện d = 4.
  14. Nó thực hiện console.log("gamma").
  15. Nó thực thi let e, đặt cờ "có thể truy cập" của ràng buộc e thành true.
  16. Nó thực hiện e = 5.
  17. Nó thực hiện console.log(a, b, c, d, e).

Hy vọng rằng câu trả lời:

  • Tại sao chúng ta có let nửa treo (để làm cho nó dễ dàng để hiểu được phạm vi, và để tránh việc có quá nhiều Sư Tử và envrecs, và để tránh lỗi ở mức khối giống như những người var cẩu có ở cấp chức năng)
  • tại sao var không có TDZ ("tiếp cận" cờ một var biến của ràng buộc luôn là true)

* Ít nhất, đó là những gì họ làm về đặc điểm kỹ thuật. Trong thực tế, họ có thể làm bất cứ điều gì họ thích cung cấp nó hoạt động như các đặc điểm kỹ thuật xác định. Trên thực tế, hầu hết các công cụ sẽ hoạt động hiệu quả hơn, sử dụng ngăn xếp, v.v.

Các vấn đề liên quan