อะไรคือความแตกต่างระหว่างการใช้ "ปล่อย" และ "var"


4539

ECMAScript 6 แนะนำคำสั่งlet

ฉันได้ยินมาว่ามันอธิบายว่าเป็นตัวแปร "ท้องถิ่น" แต่ฉันก็ยังไม่แน่ใจว่ามันจะทำงานแตกต่างจากvarคำหลักได้อย่างไร

อะไรคือความแตกต่าง? ควรletใช้มากกว่าเมื่อvarใด


104
ECMAScript เป็นมาตรฐานและletรวมอยู่ในร่างฉบับที่ 6และส่วนใหญ่จะอยู่ในข้อกำหนดขั้นสุดท้าย
Richard Ayotte

5
ดูkangax.github.io/es5-compat-table/es6สำหรับเมทริกซ์การสนับสนุนล่าสุดของฟีเจอร์ ES6 (รวมถึงการอนุญาต) ในขณะที่เขียน Firefox, Chrome และ IE11 ทั้งหมดสนับสนุน (แม้ว่าฉันเชื่อว่าการใช้งานของ FF ไม่ได้มาตรฐาน)
Nico Burns

22
เป็นเวลานานที่สุดที่ฉันไม่ทราบว่า vars in for for ถูก จำกัด ขอบเขตไว้ที่ฟังก์ชันที่ถูกห่อไว้ฉันจำได้ว่าคิดสิ่งนี้เป็นครั้งแรกและคิดว่ามันโง่มาก ฉันเห็นพลังบางอย่าง แต่รู้แล้วว่าตอนนี้ทั้งสองสามารถใช้เหตุผลที่แตกต่างกันอย่างไรและในบางกรณีคุณอาจต้องการใช้ var in a for for loop และไม่ได้กำหนดขอบเขตไว้ที่บล็อก
Eric Bishard

1
นี่คือการอ่านที่ดีมากwesbos.com/javascript-scoping
onmyway133

1
อย่างดีอธิบายคำตอบได้ที่นี่stackoverflow.com/a/43994458/5043867
Pardeep Jain

คำตอบ:


6097

กฎการกำหนดขอบเขต

ความแตกต่างที่สำคัญคือกฎการกำหนดขอบเขต ตัวแปรที่ประกาศโดยvarคีย์เวิร์ดถูกกำหนดขอบเขตไว้ที่ฟังก์ชันของฟังก์ชันทันที (ดังนั้นขอบเขตฟังก์ชัน) ในขณะที่letตัวแปรนั้นถูกกำหนดขอบเขตไว้ที่บล็อกที่ล้อมรอบทันทีซึ่งแสดงโดย{ }(ดังนั้นขอบเขตบล็อก)

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

  console.log(foo, bar);

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

  console.log(baz); // ReferenceError
}

run();

สาเหตุที่ทำให้letคำหลักถูกนำมาใช้กับภาษาคือขอบเขตของฟังก์ชันมีความสับสนและเป็นหนึ่งในแหล่งหลักของข้อบกพร่องใน JavaScript

ดูตัวอย่างนี้จากคำถามสแต็คโอเวอร์โฟลว์อื่น :

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: 3เป็นเอาต์พุตไปยังคอนโซลในแต่ละครั้งที่funcs[j]();ถูกเรียกใช้เนื่องจากฟังก์ชันที่ไม่ระบุชื่อถูกผูกไว้กับตัวแปรเดียวกัน

ผู้คนต้องสร้างฟังก์ชั่นที่เรียกใช้ทันทีเพื่อจับค่าที่ถูกต้องจากลูป แต่ก็มีขนดกเช่นกัน

hoisting

ในขณะที่ตัวแปรที่ประกาศด้วยvarคำสำคัญถูกยกขึ้นมา (เริ่มต้นด้วยundefinedก่อนที่จะรันโค้ด) ซึ่งหมายความว่าพวกเขาสามารถเข้าถึงได้ในขอบเขตการปิดล้อมแม้กระทั่งก่อนที่พวกเขาจะประกาศ:

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

run();

letตัวแปรจะไม่เริ่มต้นจนกว่าจะมีการประเมินความหมายของพวกเขา ReferenceErrorการเข้าถึงพวกเขาก่อนที่จะมีผลในการเริ่มต้น ตัวแปรบอกว่าอยู่ใน "เขตตายชั่วคราว" จากจุดเริ่มต้นของบล็อกจนกระทั่งเริ่มต้นการประมวลผล

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

checkHoisting();

การสร้างคุณสมบัติวัตถุทั่วโลก

ที่ระดับบนสุดletไม่เหมือนvarกันไม่ได้สร้างคุณสมบัติบนวัตถุส่วนกลาง:

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

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

Redeclaration

ในโหมดเข้มงวดคุณvarจะสามารถประกาศตัวแปรเดิมอีกครั้งในขอบเขตเดียวกันในขณะที่letเพิ่ม 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
จำไว้ว่าคุณสามารถสร้างบล็อกได้ทุกเมื่อที่คุณต้องการ function () {code; {let inBlock = 5; } รหัส; };
ค่าเฉลี่ย Joe

177
ดังนั้นจุดประสงค์ของการให้งบเพียงเพื่อเพิ่มหน่วยความจำเมื่อไม่ต้องการในบล็อกบางอย่าง?
NoBugs

219
@Nobobugs ใช่แล้วและได้รับการสนับสนุนว่ามีตัวแปรอยู่ในที่ที่จำเป็นเท่านั้น
แบทแมน

67
letการแสดงออกของบล็อกlet (variable declaration) statementเป็นที่ไม่ได้มาตรฐานและจะถูกลบออกในอนาคตbugzilla.mozilla.org/show_bug.cgi?id=1023609
Gajus

19
ดังนั้นฉันไม่สามารถนึกถึงกรณีใด ๆ ที่การใช้ var เป็นการใช้งานใด ๆ ใครช่วยให้ฉันตัวอย่างของสถานการณ์ที่จะใช้ var?
Luis Sieira

621

letสามารถใช้เพื่อหลีกเลี่ยงปัญหาการปิด มันผูกมูลค่าที่สดใหม่แทนที่จะเก็บการอ้างอิงเก่าดังแสดงในตัวอย่างด้านล่าง

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>

รหัสด้านบนแสดงให้เห็นถึงปัญหาการปิด JavaScript แบบคลาสสิก อ้างอิงถึงตัวแปรจะถูกเก็บไว้ในการปิดการคลิกจัดการมากกว่ามูลค่าที่แท้จริงของii

ตัวจัดการการคลิกทุกครั้งจะอ้างถึงวัตถุเดียวกันเพราะมีเพียงวัตถุตัวนับเดียวที่มี 6 ตัวดังนั้นคุณจะได้รับหกเมื่อคลิก

วิธีแก้ปัญหาทั่วไปคือการห่อสิ่งนี้ในฟังก์ชั่นที่ไม่ระบุชื่อและส่งผ่านiเป็นอาร์กิวเมนต์ ปัญหาดังกล่าวสามารถหลีกเลี่ยงได้ในขณะนี้โดยใช้letแทนvarตามที่แสดงในรหัสด้านล่าง

(ทดสอบใน Chrome และ 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" จะถูกกำหนดไว้ภายนอกลำตัววงมีอยู่ในวงเล็บและจะไม่สร้าง "การปิด" รอบ ๆ "i" แน่นอนว่าตัวอย่างของคุณจะพิสูจน์เป็นอย่างอื่น ฉันคิดว่ามันค่อนข้างสับสนจากมุมมองไวยากรณ์ แต่สถานการณ์นี้เป็นเรื่องธรรมดามากที่จะต้องมีการสนับสนุนในลักษณะนั้น ขอบคุณมากที่นำสิ่งนี้มา
Karol Kolenda

9
IE 11 รองรับletแต่มันเตือน "6" สำหรับปุ่มทั้งหมด คุณมีแหล่งข้อมูลใดที่บอกว่าletควรทำอย่างไร?
Jim Hunziker

10
ดูเหมือนว่าคำตอบของคุณคือพฤติกรรมที่ถูกต้อง: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jim Hunziker

11
อันที่จริงนี่เป็นข้อผิดพลาดทั่วไปใน Javascript และตอนนี้ฉันเห็นได้ว่าทำไมจึงletมีประโยชน์จริงๆ การตั้งค่าตัวรับฟังเหตุการณ์ในลูปไม่ต้องการนิพจน์ฟังก์ชันที่เรียกใช้ทันทีเพื่อกำหนดขอบเขตแบบโลคัลiในแต่ละการวนซ้ำ
Adrian Moisa

19
การใช้ "ให้" เพียงแค่ defers ปัญหานี้ ดังนั้นการวนซ้ำแต่ละครั้งจะสร้างขอบเขตบล็อกอิสระส่วนตัว แต่ตัวแปร "i" ยังสามารถได้รับความเสียหายจากการเปลี่ยนแปลงที่ตามมาภายในบล็อก (ได้รับตัวแปรตัววนซ้ำไม่ได้โดยปกติแล้วจะเปลี่ยนแปลงภายในบล็อก แต่ตัวแปรอื่น ๆ ที่ประกาศไว้ภายในบล็อกอาจดี จะ) และฟังก์ชั่นใด ๆ ประกาศภายในสามารถป้องกันเมื่อเรียกเสียหายมูลค่าของ "i" สำหรับฟังก์ชั่นอื่น ๆ ประกาศภายในบล็อกเพราะพวกเขาทำร่วมกันขอบเขตบล็อกส่วนตัวจึงอ้างอิงเดียวกันกับ "ฉัน"
แกรี่

199

ความแตกต่างระหว่างletและvarคืออะไร

  • ตัวแปรที่กำหนดโดยใช้varคำสั่งนั้นเป็นที่รู้จักกันทั่วทั้งฟังก์ชั่นที่มันถูกนิยามไว้ตั้งแต่เริ่มต้นของฟังก์ชั่น (*)
  • ตัวแปรที่กำหนดโดยใช้letคำสั่งเป็นที่รู้จักเฉพาะในบล็อกที่ถูกกำหนดในจากช่วงเวลาที่มันถูกกำหนดเป็นต้นไป (**)

เพื่อให้เข้าใจถึงความแตกต่างให้พิจารณารหัสต่อไปนี้:

// 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

ที่นี่เราจะเห็นได้ว่าตัวแปรของเราjเป็นที่รู้จักเฉพาะในลูปแรก แต่ไม่ใช่ก่อนและหลัง แต่ตัวแปรของเราiเป็นที่รู้จักในทุกฟังก์ชั่น

นอกจากนี้ให้พิจารณาว่าตัวแปรที่กำหนดขอบเขตของบล็อกไม่เป็นที่รู้จักก่อนที่จะมีการประกาศเนื่องจากไม่ได้ยก นอกจากนี้คุณยังไม่ได้รับอนุญาตให้ประกาศตัวแปรที่กำหนดขอบเขตของบล็อกใหม่อีกครั้งภายในบล็อกเดียวกัน สิ่งนี้ทำให้เกิดข้อผิดพลาดน้อยกว่าตัวแปรตัวแปรที่กำหนดขอบเขตของบล็อกหรือตัวแปรที่กำหนดขอบเขตหน้าที่ซึ่งถูกยกขึ้นและไม่ก่อให้เกิดข้อผิดพลาดใด ๆ ในกรณีที่มีการประกาศหลายรายการ


letวันนี้ปลอดภัยไหมที่จะใช้?

บางคนอาจโต้แย้งว่าในอนาคตเราจะใช้คำสั่ง let และคำสั่ง var นั้นจะล้าสมัย กูรู JavaScriptไคล์ซิมป์สันเขียนบทความซับซ้อนมากเกี่ยวกับเหตุผลที่เขาเชื่อว่าจะไม่เป็นกรณีที่

อย่างไรก็ตามในวันนี้ไม่ใช่กรณีอย่างแน่นอน ที่จริงแล้วเราต้องถามตัวเองว่าปลอดภัยหรือไม่ที่จะใช้letข้อความ คำตอบสำหรับคำถามนั้นขึ้นอยู่กับสภาพแวดล้อมของคุณ:

  • หากคุณกำลังเขียนโค้ด JavaScript ฝั่งเซิร์ฟเวอร์ ( Node.js ) คุณสามารถใช้letคำสั่งนี้ได้อย่างปลอดภัย

  • หากคุณกำลังเขียนฝั่งไคลเอ็นต์รหัส JavaScript และใช้ transpiler เบราว์เซอร์ (เช่นTraceurหรือBabel-แบบสแตนด์อโลน ), คุณสามารถใช้letคำสั่ง แต่รหัสของคุณมีแนวโน้มที่จะเป็นอะไรก็ได้ แต่ที่ดีที่สุดด้วยความเคารพต่อผลการดำเนินงาน

  • หากคุณกำลังเขียนโค้ด JavaScript ฝั่งไคลเอ็นต์และใช้ transpiler ที่ใช้ Node (เช่นเชลล์สคริปต์ traceurหรือBabel ) คุณสามารถใช้letคำสั่งนี้ได้อย่างปลอดภัย และเนื่องจากเบราว์เซอร์ของคุณจะรู้เกี่ยวกับรหัส transpiled เท่านั้นข้อ จำกัด ของประสิทธิภาพจึงควรถูก จำกัด

  • หากคุณกำลังเขียนโค้ด JavaScript ฝั่งไคลเอ็นต์และไม่ใช้ transpiler คุณต้องพิจารณาการสนับสนุนเบราว์เซอร์

    ยังมีเบราว์เซอร์บางตัวที่ไม่รองรับletเลย:

ป้อนคำอธิบายรูปภาพที่นี่


วิธีการติดตามการสนับสนุนเบราว์เซอร์

สำหรับภาพรวมล่าสุดของเบราว์เซอร์ที่สนับสนุนletคำสั่งในขณะที่คุณอ่านคำตอบนี้Can I Useดูหน้านี้


(*) ตัวแปรทั่วโลกและขอบเขตการทำงานสามารถเริ่มต้นและใช้ก่อนที่จะมีการประกาศเพราะตัวแปร JavaScript จะยกยกซึ่งหมายความว่าการประกาศจะอยู่ด้านบนสุดของขอบเขตเสมอ

(**) ตัวแปรที่กำหนดขอบเขตของบล็อกไม่ถูกยก


14
เกี่ยวกับคำตอบ v4: iรู้จักกันทุกที่ในฟังก์ชั่นบล็อค! มันเริ่มต้นเป็นundefined(เนื่องจากการชักรอก) จนกว่าคุณจะกำหนดค่า! ps: letถูกยกขึ้น (ไปยังด้านบนสุดของมันมีบล็อก) แต่จะให้ a ReferenceErrorเมื่ออ้างอิงในบล็อกก่อนที่จะได้รับมอบหมายครั้งแรก (ps2: ฉันเป็นคนประเภทอัฒภาค แต่คุณไม่ต้องการเซมิโคลอนหลังจากบล็อก) ที่ถูกกล่าวขอบคุณสำหรับการเพิ่มการตรวจสอบความเป็นจริงเกี่ยวกับการสนับสนุน!
GitaarLAB

@GitaarLAB: ตามที่Mozilla Developer Network : "ใน ECMAScript 2015 การเชื่อมโยงไม่ได้อยู่ภายใต้ Variable Hoisting ซึ่งหมายความว่าการแจ้งเตือนจะไม่ย้ายไปด้านบนของบริบทการดำเนินการปัจจุบัน" - อย่างไรก็ตามฉันได้ทำการปรับปรุงเล็กน้อยสำหรับคำตอบที่ควรอธิบายความแตกต่างของพฤติกรรมการชักระหว่างletและvar!
John Slegers

1
คำตอบของคุณดีขึ้นมาก (ฉันตรวจสอบอย่างละเอียด) โปรดทราบว่าลิงก์เดียวกันกับที่คุณอ้างถึงในความคิดเห็นของคุณยังพูดว่า: "ตัวแปร (ให้) อยู่ใน" เขตเวลาชั่วคราว "ตั้งแต่เริ่มต้นบล็อกจนกว่าจะประมวลผลการเริ่มต้น" ซึ่งหมายความว่า 'ตัวระบุ' (สตริงข้อความ 'สงวน' ให้ชี้ไปที่ 'บางสิ่งบางอย่าง') ถูกสงวนไว้ในขอบเขตที่เกี่ยวข้องมิฉะนั้นจะกลายเป็นส่วนหนึ่งของขอบเขตรูท / โฮสต์ / หน้าต่าง สำหรับฉันเป็นการส่วนตัว 'การชักรอก' หมายถึงไม่มีอะไรมากไปกว่าการจอง / เชื่อมโยง 'ตัวระบุ' ที่ประกาศไปยังขอบเขตที่เกี่ยวข้อง ไม่รวมการเริ่มต้น / การมอบหมาย / การแก้ไข!
GitaarLAB

และ .. + 1 บทความของ Kyle Simpson ที่คุณเชื่อมโยงนั้นเป็นการอ่านที่ยอดเยี่ยมขอบคุณสำหรับสิ่งนั้น นอกจากนี้ยังมีความชัดเจนเกี่ยวกับ "เขตตายชั่วคราว" aka "TDZ" สิ่งหนึ่งที่น่าสนใจที่ฉันต้องการเพิ่ม: ฉันอ่าน MDN แล้วletและconstได้รับการแนะนำให้ใช้เฉพาะเมื่อคุณต้องการฟังก์ชั่นเพิ่มเติมของพวกเขาเพราะบังคับใช้ / ตรวจสอบคุณสมบัติพิเศษเหล่านี้ (เช่น const แบบเขียนอย่างเดียว) ส่งผลให้ '(และขอบเขตเพิ่มเติมโหนดในขอบเขตต้นไม้) สำหรับเอ็นจิ้น (ปัจจุบัน) เพื่อบังคับใช้ / ตรวจสอบ / ตรวจสอบ / ตั้งค่า
GitaarLAB

1
โปรดทราบว่า MDN บอกว่า IE แปลความหมายได้อย่างถูกต้อง มันคืออะไร developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Katinka Hesselink

146

นี่คือคำอธิบายของletคำหลักที่มีตัวอย่างบางส่วน

letชอบvarมาก ความแตกต่างที่สำคัญคือขอบเขตของvarตัวแปรคือฟังก์ชั่นการล้อมรอบทั้งหมด

ตารางนี้บน Wikipedia แสดงว่าเบราว์เซอร์ใดรองรับ Javascript 1.7

โปรดทราบว่ามีเพียงเบราว์เซอร์ Mozilla และ Chrome เท่านั้นที่รองรับ IE, Safari และอื่น ๆ ที่อาจเกิดขึ้นไม่ได้


5
คีย์บิตของข้อความจากเอกสารที่เชื่อมโยงน่าจะเป็น "ให้ทำงานได้เหมือน var ความแตกต่างที่สำคัญคือขอบเขตของตัวแปร var คือฟังก์ชันล้อมรอบทั้งหมด"
Michael Burr

50
ในขณะที่มันถูกต้องทางเทคนิคที่จะบอกว่า IE ไม่สนับสนุนมันก็ถูกต้องมากขึ้นที่จะบอกว่ามันเป็นส่วนขยายของโมซิลล่าเท่านั้น
olliej

55
@olliej ที่จริง Mozilla เป็นเกมที่นำหน้า ดูหน้า 19 ของecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
Tyler Crompton

@TylerCrompton เป็นเพียงชุดคำศัพท์ที่สงวนไว้เป็นเวลาหลายปี เมื่อเพิ่ม Mozilla เพิ่มให้มันเป็นส่วนขยาย Mozilla อย่างหมดจดไม่มีข้อกำหนดที่เกี่ยวข้อง ES6 ควรกำหนดพฤติกรรมสำหรับคำสั่ง let แต่เกิดขึ้นหลังจาก mozilla แนะนำไวยากรณ์ โปรดจำไว้ว่า moz ยังมี E4X ซึ่งตายไปทั้งหมดและ moz เท่านั้น
olliej

9
IE11 เพิ่มการสนับสนุนสำหรับlet msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx
eloyesp

112

คำตอบที่ได้รับการยอมรับไม่มีจุด:

{
  let a = 123;
};

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

19
คำตอบที่ยอมรับไม่ได้อธิบายจุดนี้ในตัวอย่าง คำตอบที่ได้รับการยอมรับเพียง แต่แสดงให้เห็นถึงมันในforการเริ่มต้นห่วงอย่างมาก จำกัด ขอบเขตของการประยุกต์ใช้ข้อ จำกัด letของ upvoted
Jon Davis

37
@ stimpy77 มันระบุอย่างชัดเจนว่า "ให้มีการกำหนดขอบเขตไปยังบล็อกที่อยู่ใกล้ที่สุด"; ต้องรวมทุกวิธีที่จะปรากฏหรือไม่
เดฟนิวตัน

6
มีตัวอย่างมากมายและไม่มีใครแสดงเรื่องนี้ได้อย่างถูกต้อง .. ฉันอาจยกระดับคำตอบที่ยอมรับและคำตอบนี้ได้หรือไม่
Jon Davis

5
การมีส่วนร่วมนี้แสดงให้เห็นว่า "บล็อก" สามารถเป็นชุดของบรรทัดที่อยู่ในวงเล็บ เช่นไม่จำเป็นต้องเชื่อมโยงกับโฟลว์การควบคุมลูปและอื่น ๆ
webelo

81

let

ขอบเขตบล็อก

ตัวแปรที่ประกาศโดยใช้letคำหลักนั้นจะถูกกำหนดขอบเขตแบบบล็อกซึ่งหมายความว่าจะมีให้เฉพาะในบล็อกเท่านั้นที่ถูกประกาศเท่านั้น

ที่ระดับบนสุด (นอกฟังก์ชั่น)

ที่ระดับบนสุดตัวแปรที่ประกาศโดยใช้letจะไม่สร้างคุณสมบัติบนวัตถุกลาง

var globalVariable = 42;
let blockScopedVariable = 43;

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

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

ภายในฟังก์ชั่น

ภายในฟังก์ชั่น ( แต่ด้านนอกของบล็อก) มีขอบเขตเช่นเดียวกับletvar

(() => {
  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

ภายในบล็อก

ตัวแปรที่ประกาศโดยใช้letภายในบล็อกนั้นไม่สามารถเข้าถึงได้นอกบล็อกนั้น

{
  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

ภายในวง

ตัวแปรที่ประกาศด้วยletในลูปสามารถอ้างอิงได้เฉพาะภายในลูปนั้น

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.

ลูปพร้อมการปิด

หากคุณใช้letแทนvarการวนซ้ำการวนซ้ำแต่ละครั้งคุณจะได้รับตัวแปรใหม่ นั่นหมายความว่าคุณสามารถใช้การปิดในห่วงได้อย่างปลอดภัย

// 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);
}

โซนตายชั่วคราว

เนื่องจากโซนเวลาตายชั่วคราวตัวแปรที่ประกาศใช้letไม่สามารถเข้าถึงได้ก่อนที่จะถูกประกาศ ความพยายามที่จะทำผิดพลาด

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

ไม่มีการประกาศอีกครั้ง

letคุณไม่สามารถประกาศตัวแปรเดียวกันหลายครั้งโดยใช้ นอกจากนี้คุณยังไม่สามารถประกาศตัวแปรที่ใช้กับตัวระบุเช่นเดียวกับอีกตัวแปรหนึ่งที่มีการประกาศใช้letvar

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ค่อนข้างคล้ายกับlet- เป็นบล็อกที่มีขอบเขตและมี TDZ อย่างไรก็ตามมีสองสิ่งที่แตกต่างกัน

ไม่มีการกำหนดใหม่

ตัวแปรที่ประกาศโดยใช้constไม่สามารถกำหนดซ้ำได้

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

โปรดทราบว่ามันไม่ได้หมายความว่าค่านั้นไม่เปลี่ยนรูป คุณสมบัติของมันยังคงสามารถเปลี่ยนแปลงได้

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

Object.freeze()หากคุณต้องการที่จะมีการเปลี่ยนรูปวัตถุที่คุณควรใช้

ต้องมี Initializer

constคุณต้องระบุค่าเมื่อประกาศตัวแปรที่ใช้

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

51

นี่คือตัวอย่างสำหรับความแตกต่างระหว่างทั้งสอง (การสนับสนุนเพิ่งเริ่มต้นสำหรับ chrome):
ป้อนคำอธิบายรูปภาพที่นี่

อย่างที่คุณเห็นว่าvar jตัวแปรยังคงมีค่าอยู่นอกขอบเขตของลูปขอบเขต (Block Scope) แต่let iตัวแปรนั้นไม่ได้กำหนดไว้นอกขอบเขตของลูปขอบเขต

"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
ฉันกำลังดูเครื่องมืออะไรที่นี่?
บาร์ตัน

20
Chrome devtools
vlio20

ในฐานะผู้พัฒนาแอพเพล็ตเดสก์ท็อปสำหรับ Cinnamon ฉันไม่ได้สัมผัสกับเครื่องมือที่มันวาว
บาร์ตัน

48

มีความแตกต่างเล็กน้อยบางอย่าง - letกำหนดขอบเขตจะทำงานมากกว่าการกำหนดขอบเขตตัวแปรในภาษาอื่น ๆ ไม่มากก็น้อย

เช่นขอบเขตของสิ่งที่แนบมาพวกมันไม่มีอยู่ก่อนที่มันจะถูกประกาศ ฯลฯ

อย่างไรก็ตามมันเป็นเรื่องที่น่าสังเกตว่าletเป็นเพียงส่วนหนึ่งของการใช้งาน Javascript ที่ใหม่กว่าและมีการรองรับเบราว์เซอร์ที่หลากหลาย


11
นอกจากนี้ยังเป็นที่น่าสังเกตว่า ECMAScript เป็นมาตรฐานและletรวมอยู่ในร่างฉบับที่ 6และน่าจะเป็นข้อกำหนดขั้นสุดท้าย
Richard Ayotte

23
นั่นคือความแตกต่าง 3 ปีที่สร้าง: D
olliej

4
เพิ่งตะลึงกับคำถามนี้และในปี 2012 ก็ยังคงเป็นกรณีที่เบราว์เซอร์ Mozilla สนับสนุนletเท่านั้น Safari, IE และ Chome ไม่สามารถทำได้
pseudosavant

2
แนวคิดของการสร้างขอบเขตบล็อกบางส่วนโดยไม่ตั้งใจคือจุดที่ดีระวังletอย่ายกขึ้นเพื่อใช้ตัวแปรที่กำหนดโดยletนิยามที่ด้านบนสุดของบล็อก หากคุณมีifคำสั่งที่เป็นมากกว่าโค้ดสองสามบรรทัดคุณอาจลืมว่าคุณไม่สามารถใช้ตัวแปรนั้นจนกว่าจะมีการกำหนด จุดที่ดี !!!
Eric Bishard

2
@EricB: ใช่และไม่ใช่: "ใน ECMAScript 2015 let จะยกตัวแปรขึ้นไปด้านบนของบล็อกอย่างไรก็ตามการอ้างอิงตัวแปรในบล็อกก่อนที่การประกาศตัวแปรจะส่งผลให้มีReferenceError (บันทึกย่อของฉัน: ดีเก่าundefined) ตัวแปรอยู่ใน 'เขตเวลาชั่วคราว' จากจุดเริ่มต้นของบล็อกจนกระทั่งมีการประมวลผลการประกาศ " ไปเหมือนกันสำหรับ "switch statement เพราะมีเพียงหนึ่งบล็อกพื้นฐาน" ที่มา: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

27

ความแตกต่างที่สำคัญคือความแตกต่างของขอบเขตในขณะที่ให้สามารถใช้ได้เฉพาะภายในขอบเขตที่มีการประกาศเช่นในการวนรอบvarสามารถเข้าถึงได้นอกวงตัวอย่างเช่น จากเอกสารในMDN (ตัวอย่างเช่นจาก MDN):

ให้ช่วยให้คุณสามารถประกาศตัวแปรที่ถูก จำกัด อยู่ในขอบเขตเพื่อป้องกันคำสั่งหรือการแสดงออกที่มันถูกนำมาใช้ สิ่งนี้แตกต่างจากคำหลักvarซึ่งกำหนดตัวแปรแบบโกลบอลหรือแบบโลคัลกับฟังก์ชันทั้งหมดโดยไม่คำนึงถึงขอบเขตบล็อก

ตัวแปรที่ประกาศโดยletมีขอบเขตบล็อกที่ถูกกำหนดรวมถึงในบล็อกย่อยที่มีอยู่ วิธีนี้ช่วยให้การทำงานเป็นอย่างมากเช่นvar ความแตกต่างที่สำคัญคือขอบเขตของตัวแปรvarคือฟังก์ชันการล้อมรอบทั้งหมด:

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
}`

ที่ระดับสูงสุดของโปรแกรมและฟังก์ชั่นให้ไม่เหมือนกับvarไม่ได้สร้างคุณสมบัติบนวัตถุกลาง ตัวอย่างเช่น:

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

เมื่อใช้ภายในบล็อกให้ จำกัด ขอบเขตของตัวแปรไว้ที่บล็อกนั้น สังเกตความแตกต่างระหว่างvarที่มีขอบเขตอยู่ภายในฟังก์ชันที่มีการประกาศ

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

อย่าลืมว่ามันเป็นคุณสมบัติ ECMA6 ดังนั้นจึงยังไม่รองรับอย่างเต็มที่ดังนั้นจึงควรส่งไปที่ ECMA5 โดยใช้ Babel และอื่น ๆ เสมอ ... สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเยี่ยมชมเว็บไซต์ babel


24
  • ตัวแปรไม่ได้ติตตั้ง

    letจะไม่ยกขึ้นกับขอบเขตทั้งหมดของบล็อกที่ปรากฏในทางตรงกันข้ามvarสามารถยกได้ดังต่อไปนี้

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

    ที่จริงต่อ @Bergi, ทั้งสองvarและletยก

  • เก็บขยะ

    ขอบเขตการบล็อกของletมีประโยชน์เกี่ยวข้องกับการปิดและการรวบรวมขยะเพื่อเรียกคืนหน่วยความจำ พิจารณา,

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

    clickโทรกลับจัดการไม่จำเป็นต้องhugeDataตัวแปรที่ทั้งหมด ในทางทฤษฎีหลังจากprocess(..)รันโครงสร้างข้อมูลขนาดใหญ่hugeDataอาจถูกรวบรวมขยะ อย่างไรก็ตามเป็นไปได้ว่าเอ็นจิ้น JS บางตัวยังคงต้องรักษาโครงสร้างขนาดใหญ่นี้ไว้เนื่องจากclickฟังก์ชั่นจะปิดทั้งขอบเขต

    อย่างไรก็ตามขอบเขตบล็อกสามารถทำให้โครงสร้างข้อมูลขนาดใหญ่นี้ถูกเก็บขยะ

    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 ลูป

    letในการวนซ้ำสามารถผูกกับการวนซ้ำของการวนซ้ำแต่ละครั้งตรวจสอบให้แน่ใจว่าได้กำหนดค่าอีกครั้งจากจุดสิ้นสุดของการวนซ้ำก่อนหน้านี้ พิจารณา,

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

    อย่างไรก็ตามแทนที่varด้วยlet

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

    เพราะletสร้างสภาพแวดล้อมคำศัพท์ใหม่ที่มีชื่อเหล่านั้นสำหรับก) initialiser แสดงออกข) แต่ละซ้ำ (previosly เพื่อประเมินการแสดงออกเพิ่มขึ้น) รายละเอียดเพิ่มเติมที่นี่


4
Yip พวกเขาจะยก แต่ประพฤติตัวเป็นหากไม่ได้ยกขึ้นเพราะ (ม้วนกลอง) ขมับตายโซน - ชื่อที่น่าทึ่งมากสำหรับตัวระบุจะไม่สามารถเข้าถึงได้จนกว่าจะมีการประกาศ :-)
Drenai

ดังนั้นขอยก แต่ไม่พร้อมใช้งาน? มันแตกต่างจาก 'ไม่ชักรอก' อย่างไร
N-ate

หวังว่า Brian หรือ Bergi จะกลับมาตอบคำถามนี้ มีการประกาศให้ยก แต่ไม่ได้รับมอบหมาย? ขอบคุณ!
N-ate

1
@ N-ate นี่คือหนึ่งโพสต์ของ Bergi บางทีคุณอาจหาคำตอบได้
zangw

เป็นเรื่องที่น่าสนใจแม้จะถูกเรียกว่า hoisting เมื่อมันมาถึง ฉันเข้าใจแล้วว่ากลไกการแยกวิเคราะห์นั้นมีการตรวจจับล่วงหน้า แต่สำหรับทุกเจตนาและวัตถุประสงค์ผู้เขียนโปรแกรมควรปฏิบัติต่อมันราวกับว่ามันไม่มีอยู่จริง การยกของ var บนมืออื่น ๆ มีผลกระทบต่อโปรแกรมเมอร์
N-กิน

19

นี่คือตัวอย่างของการเพิ่มสิ่งที่ผู้อื่นเขียนไปแล้ว สมมติว่าคุณต้องการสร้างอาเรย์ของฟังก์ชั่นโดยadderFunctionsที่แต่ละฟังก์ชั่นใช้อาร์กิวเมนต์หมายเลขเดียวและส่งกลับผลรวมของอาร์กิวเมนต์และดัชนีของฟังก์ชันในอาเรย์ การพยายามสร้างadderFunctionsด้วยลูปโดยใช้varคำหลักนั้นจะไม่เป็นไปตามที่บางคนคาดหวัง

// 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

กระบวนการข้างต้นไม่ได้สร้างอาเรย์ของฟังก์ชั่นที่ต้องการเพราะiขอบเขตขยายออกไปมากกว่าการวนซ้ำของforบล็อกที่แต่ละฟังก์ชั่นถูกสร้างขึ้น แต่ในตอนท้ายของวงที่iมีการปิดของแต่ละฟังก์ชั่นหมายถึงiค่า 's ตอนท้ายของวง (1000) adderFunctionsสำหรับทุกฟังก์ชั่นที่ไม่ระบุชื่อใน นี่ไม่ใช่สิ่งที่เราต้องการ: ตอนนี้เรามีฟังก์ชั่นต่าง ๆ กว่า 1,000 รายการในหน่วยความจำที่มีพฤติกรรมเหมือนกันทุกประการ และหากเราปรับปรุงมูลค่าของiในภายหลังการกลายพันธุ์จะส่งผลกระทบต่อทั้งหมดadderFunctionsการกลายพันธุ์จะมีผลต่อทั้งหมด

อย่างไรก็ตามเราสามารถลองอีกครั้งโดยใช้letคำหลัก:

// 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

เวลานี้iจะรีบาวด์ในการวนซ้ำแต่ละครั้งของforลูป แต่ละฟังก์ชั่นตอนนี้เก็บค่าiในเวลาของการสร้างฟังก์ชั่นและadderFunctionsทำงานตามที่คาดไว้

ตอนนี้ภาพที่ผสมทั้งสองพฤติกรรมและคุณอาจจะเห็นว่าทำไมมันไม่แนะนำให้ผสมใหม่letและconstกับเก่ากว่าvarในสคริปต์เดียวกัน การทำเช่นนั้นอาจส่งผลให้รหัสบางอย่างน่าสับสน

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

อย่าปล่อยให้เรื่องนี้เกิดขึ้นกับคุณ. ใช้ linter

หมายเหตุ:นี่เป็นตัวอย่างการสอนที่มีจุดประสงค์เพื่อสาธิตvar/ letพฤติกรรมในลูปและด้วยการปิดฟังก์ชั่นที่จะเข้าใจได้ง่าย นี่จะเป็นวิธีที่น่ากลัวในการเพิ่มตัวเลข แต่เทคนิคทั่วไปของการเก็บข้อมูลในการปิดฟังก์ชั่นที่ไม่ระบุชื่ออาจจะพบในโลกแห่งความจริงในบริบทอื่น ๆ YMMV


2
@aborz: ยังมีฟังก์ชั่นที่ไม่ระบุชื่อที่น่าสนใจมากในตัวอย่างที่สอง มันเป็นสิ่งที่ฉันคุ้นเคยใน C # วันนี้ฉันได้เรียนรู้อะไรบางอย่าง
บาร์ตัน

การแก้ไข: ทางเทคนิคไวยากรณ์ของฟังก์ชันลูกศรอธิบายไว้ที่นี่ => developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

3
let value = i;ที่จริงแล้วคุณไม่จำเป็นต้อง forงบสร้างบล็อกคำศัพท์
แปรงสีฟัน

17

ความแตกต่างอยู่ในขอบเขตของตัวแปรที่ประกาศพร้อมกัน

ในทางปฏิบัติมีจำนวนของผลที่เป็นประโยชน์ของความแตกต่างในขอบเขต:

  1. letตัวแปรจะปรากฏเฉพาะในบล็อกที่อยู่ใกล้ที่สุด ( { ... })
  2. letตัวแปรสามารถใช้งานได้ในบรรทัดของรหัสที่เกิดขึ้นหลังจากประกาศตัวแปร (แม้ว่าจะมีการยก !)
  3. letตัวแปรที่อาจจะไม่ได้ redeclared โดยที่ตามมาหรือvarlet
  4. letตัวแปรโกลบอลจะไม่ถูกเพิ่มในโกลบอลwindowออบเจค
  5. letตัวแปรใช้งานง่ายเมื่อปิด (ไม่ทำให้เกิดสภาพการแข่งขัน )

ข้อ จำกัด ที่กำหนดโดยletลดการมองเห็นของตัวแปรและเพิ่มโอกาสที่จะพบการชนชื่อที่ไม่คาดคิดก่อน สิ่งนี้ทำให้ง่ายต่อการติดตามและเหตุผลเกี่ยวกับตัวแปรรวมถึงความสามารถในการเข้าถึงได้ (ช่วยในการเรียกคืนหน่วยความจำที่ไม่ได้ใช้)

ดังนั้น letตัวแปรมีโอกาสน้อยที่จะทำให้เกิดปัญหาเมื่อใช้ในโปรแกรมขนาดใหญ่หรือเมื่อรวมเฟรมเวิร์กที่พัฒนาโดยอิสระเข้าด้วยกันในรูปแบบใหม่และไม่คาดคิด

varอาจยังมีประโยชน์หากคุณแน่ใจว่าคุณต้องการเอฟเฟ็กต์การโยงเดี่ยวเมื่อใช้การปิดในลูป (# 5) หรือการประกาศตัวแปรโกลบอลที่มองเห็นจากภายนอกในโค้ดของคุณ (# 4) การใช้varเพื่อการส่งออกอาจถูกแทนที่หากexportโยกย้ายออกจากพื้นที่ transpiler และเป็นภาษาหลัก

ตัวอย่าง

1. ไม่มีการใช้นอกบล็อกที่อยู่ใกล้ที่สุด: บล็อกของรหัสนี้จะโยนข้อผิดพลาดการอ้างอิงเพราะการใช้ครั้งที่สองxเกิดขึ้นนอกบล็อกที่มีการประกาศด้วยlet:

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

ในทางตรงกันข้ามตัวอย่างเดียวกันกับvarผลงาน

2. ห้ามใช้ก่อนการประกาศ:
บล็อกรหัสนี้จะส่งReferenceErrorรหัสก่อนจึงจะสามารถเรียกใช้รหัสได้เนื่องจากxถูกใช้ก่อนที่จะมีการประกาศ:

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

ในทางตรงกันข้ามตัวอย่างเดียวกันกับการvarแยกวิเคราะห์และการทำงานโดยไม่ต้องโยนข้อยกเว้นใด ๆ

3. ไม่มีการประกาศซ้ำ: รหัสต่อไปนี้แสดงให้เห็นว่าตัวแปรที่ประกาศด้วยletอาจไม่สามารถประกาศใหม่ได้ในภายหลัง:

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

4. ลูกโลกไม่ได้ติดอยู่กับ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. ใช้งานง่ายกับการปิด: ตัวแปรที่ประกาศด้วยvarทำงานได้ไม่ดีกับการปิดในลูป นี่คือลูปแบบง่าย ๆ ที่ส่งออกลำดับของค่าที่ตัวแปรiมี ณ เวลาต่างกัน:

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

ผลลัพธ์นี้:

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

ใน JavaScript เรามักจะใช้ตัวแปรในภายหลังอย่างมีนัยสำคัญกว่าเมื่อพวกเขาถูกสร้างขึ้น เมื่อเราสาธิตสิ่งนี้โดยการหน่วงเวลาเอาต์พุตด้วยการปิดที่ส่งไปที่setTimeout:

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

... ผลลัพธ์ยังคงไม่เปลี่ยนแปลงตราบใดที่เรายังยึดติดletอยู่ ในทางตรงกันข้ามถ้าเราใช้var iแทน:

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

... ลูปเอาท์พุท "i คือ 5" โดยไม่คาดคิดห้าครั้ง:

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

5
# 5 ไม่ได้เกิดจากสภาพการแข่งขัน โดยใช้varแทนletรหัสจะเท่ากับ: var i = 0; while (i < 5) { doSomethingLater(); i++; } iอยู่นอกการปิดและตามเวลาที่doSomethingLater()ดำเนินการiได้เพิ่มขึ้น 5 ครั้งแล้วดังนั้นผลลัพธ์จะเป็นi is 5ห้าครั้ง โดยใช้letตัวแปรiอยู่ภายในปิดเพื่อให้การโทรแต่ละ async ได้รับสำเนาของตัวเองiแทนการใช้ 'โลก' varหนึ่งที่สร้างขึ้นด้วย
Daniel T.

@DanielT: ฉันไม่คิดว่าการแปลงการยกนิยามตัวแปรออกจาก loop initializer จะอธิบายอะไร forนั่นเป็นเพียงความหมายปกติของความหมายของ การแปลงที่แม่นยำยิ่งขึ้นแม้ว่าจะซับซ้อนกว่าเดิมคือfor (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(i คลาสสิกคือ $ {j} ), 125/*ms*/); })(i); }ซึ่งแนะนำ "ฟังก์ชั่นบันทึกการเปิดใช้งานฟังก์ชั่น" เพื่อบันทึกค่าแต่ละค่าiด้วยชื่อของjฟังก์ชั่นภายใน
mormegil

14

สองฟังก์ชันต่อไปนี้อาจแสดงความแตกต่าง:

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 น่าสนใจเพราะมันทำให้เราทำสิ่งนี้:

(() => {
    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++);
            }
        }
    }
})();

ซึ่งผลลัพธ์ในการนับ [0, 7]

แต่ทว่า

(() => {
    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++);
            }
        }
    }
})();

นับเฉพาะ [0, 1]


2
นี่เป็นครั้งแรกที่ฉันเคยเห็นทุกคนทำตัวเหมือนตัวแปรเงาเป็นที่พึงปรารถนา ไม่จุดประสงค์ของการปล่อยคือไม่ยอมให้มีเงา
John Haugeland

1
วัตถุประสงค์? มันเป็นโครงสร้างคุณสามารถใช้มันได้ตามที่คุณต้องการหนึ่งในวิธีที่น่าสนใจก็คือ
Dmitry

13

ฟังก์ชั่นขอบเขตบล็อก VS:

ความแตกต่างที่สำคัญระหว่างvarและletเป็นตัวแปรที่ประกาศด้วยvarกำลังฟังก์ชั่นที่กำหนดขอบเขต ในขณะที่ฟังก์ชั่นที่มีการประกาศletเป็นบล็อกขอบเขต ตัวอย่างเช่น:

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 

ตัวแปรที่มีvar:

เมื่อฟังก์ชั่นแรกtestVarเรียกว่าตัวแปร foo ซึ่งประกาศด้วยvarจะยังคงสามารถเข้าถึงได้นอกifคำสั่ง ตัวแปรนี้fooจะใช้ได้ทุกที่ภายในขอบเขตของฟังก์ชั่นtestVar

ตัวแปรที่มีlet:

เมื่อฟังก์ชั่นที่สองtestLetได้รับการเรียกว่าตัวแปรบาร์ประกาศด้วยletจะสามารถเข้าถึงได้เฉพาะในifคำสั่ง เพราะตัวแปรประกาศด้วยletกำลังบล็อกขอบเขต (ที่บล็อกเป็นรหัสระหว่างวงเล็บปีกกาเช่นif{}, for{}, function{})

let ตัวแปรไม่ได้รับการชักรอก:

ความแตกต่างระหว่างอีกvarและletเป็นตัวแปรที่มีประกาศด้วยไม่ได้รับการยกlet ตัวอย่างเป็นวิธีที่ดีที่สุดในการอธิบายพฤติกรรมนี้:

ตัวแปรที่let ไม่มีการชักรอก:

console.log(letVar);

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

ตัวแปรที่มีvar สิ่งที่ต้องทำรับยก:

console.log(varVar);

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

ทั่วโลกletไม่ได้ติดกับwindow:

ตัวแปรที่ประกาศด้วยletในขอบเขตส่วนกลาง (ซึ่งเป็นรหัสที่ไม่ได้อยู่ในฟังก์ชั่น) จะไม่ได้รับการเพิ่มเป็นคุณสมบัติในwindowวัตถุทั่วโลก ตัวอย่างเช่น (รหัสนี้อยู่ในขอบเขตส่วนกลาง):

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


ควรletใช้มากกว่าเมื่อvarใด

ใช้letมากกว่าvarเมื่อใดก็ตามที่คุณสามารถเพราะมันเป็นเพียงแค่ขอบเขตเฉพาะเจาะจงมากขึ้น สิ่งนี้จะช่วยลดความขัดแย้งในการตั้งชื่อที่อาจเกิดขึ้นเมื่อจัดการกับตัวแปรจำนวนมาก varสามารถนำมาใช้เมื่อคุณต้องการตัวแปรทั่วโลกอย่างชัดเจนที่จะอยู่ในwindowวัตถุ (พิจารณาอย่างรอบคอบหากจำเป็นจริงๆ)


9

นอกจากนี้ยังปรากฏว่าอย่างน้อยใน Visual Studio 2015, TypeScript 1.5, "var" อนุญาตให้มีการประกาศหลายชื่อตัวแปรเดียวกันในบล็อกและ "อนุญาต" ไม่ได้

สิ่งนี้จะไม่สร้างข้อผิดพลาดในการคอมไพล์:

var x = 1;
var x = 2;

นี่จะ:

let x = 1;
let x = 2;

9

var เป็นขอบเขตโกลบอล (รอกได้)

letและconstเป็นขอบเขตบล็อก

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

เมื่อใช้งาน let

letคำหลักยึดติดการประกาศตัวแปรขอบเขตของสิ่งที่บล็อก (ปกติจะเป็น{ .. }คู่) มันมีอยู่ใน. ในคำอื่น ๆletโดยปริยาย hijacks ขอบเขตบล็อกใด ๆ สำหรับการประกาศตัวแปร

letไม่สามารถเข้าถึงตัวแปรในwindowวัตถุได้เนื่องจากไม่สามารถเข้าถึงได้ทั่วโลก

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

เมื่อใช้งาน var

var และตัวแปรใน ES5 มีขอบเขตในฟังก์ชั่นหมายถึงตัวแปรที่ถูกต้องภายในฟังก์ชั่นและไม่ได้อยู่นอกฟังก์ชั่นของตัวเอง

varตัวแปรสามารถเข้าถึงได้ในwindowวัตถุเพราะพวกเขาไม่สามารถเข้าถึงได้ทั่วโลก

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

หากคุณต้องการทราบข้อมูลเพิ่มเติมอ่านต่อด้านล่าง

หนึ่งในคำถามสัมภาษณ์ที่โด่งดังที่สุดเกี่ยวกับขอบเขตก็สามารถใช้งานได้อย่างเพียงพอletและvarดังต่อไปนี้

เมื่อใช้งาน let

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

นี่เป็นเพราะเมื่อใช้letสำหรับการวนซ้ำทุกครั้งตัวแปรจะถูกกำหนดขอบเขตและมีสำเนาของตัวเอง

เมื่อใช้งาน var

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

นี่เป็นเพราะเมื่อใช้varสำหรับการวนซ้ำทุกครั้งตัวแปรจะถูกกำหนดขอบเขตและมีการแชร์สำเนา


7

ถ้าผมอ่านรายละเอียดที่ถูกต้องแล้วlet โชคดีที่ยังสามารถยกระดับเพื่อหลีกเลี่ยงตัวเองฟังก์ชั่นการเรียกใช้ในการจำลองเฉพาะสมาชิกเท่านั้นเอกชน - รูปแบบการออกแบบที่นิยมที่ลดลงการอ่านรหัสที่มีความซับซ้อนแก้จุดบกพร่องที่เพิ่มไม่มีการป้องกันรหัสจริงหรือประโยชน์อื่น - ยกเว้นบางทีความพึงพอใจของใครบางคน ต้องการซีแมนทิกส์ดังนั้นให้หยุดใช้ / พูดจาโผงผาง

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

ดู 'การเลียนแบบส่วนต่อประสานส่วนตัว '


คุณสามารถอธิบายรายละเอียดเกี่ยวกับวิธีที่การเรียกใช้ฟังก์ชั่นการแสดงออกทันทีไม่ให้ "การป้องกันรหัส" ได้letหรือไม่? (ฉันถือว่าคุณหมายถึง IIFE ด้วย "ฟังก์ชั่นเรียกตนเอง")
Robert Siemer

และทำไมคุณถึงตั้งค่าhiddenPropertyในนวกรรมิก? มีเพียงครั้งเดียวhiddenPropertyสำหรับทุกอินสแตนซ์ใน "คลาส" ของคุณ
Robert Siemer

7

ในแง่พื้นฐานที่สุด

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

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

⚡️ Sandbox เพื่อเล่น↓

แก้ไข let vs var


4

แฮ็คบางกับ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"

ทะเยอทะยานและหมากับ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)

โปรดหมายความว่าlet { type, name, value } = node;อะไร คุณสร้างวัตถุใหม่ด้วย 3 คุณสมบัติประเภท / ชื่อ / ค่าและเริ่มต้นพวกเขาด้วยค่าคุณสมบัติจากโหนด?
AlainIb

ในตัวอย่างที่ 3 คุณกำลังประกาศโหนดอีกครั้งซึ่งทำให้เกิดข้อยกเว้น ตัวอย่างทั้งหมดเหล่านี้ยังทำงานได้อย่างสมบูรณ์แบบด้วยvarเช่นกัน
Rehan Haider

4

ให้เทียบกับ มันเป็นเรื่องของขอบเขต

ตัวแปร var เป็นโกลบอลและสามารถเข้าถึงได้โดยทั่วไปทุกที่ในขณะที่ปล่อยให้ตัวแปรไม่ใช่โกลบอลและจะมีอยู่เฉพาะในวงเล็บปิดเท่านั้นที่ฆ่ามัน

ดูตัวอย่างของฉันด้านล่างและสังเกตว่าตัวแปร lion (let) ทำหน้าที่แตกต่างกันอย่างไรในสอง console.logs; มันอยู่นอกขอบเขตใน console.log ที่ 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 แนะนำสองคำหลักใหม่ ( ให้และconst ) ทางเลือกเพื่อvar var

เมื่อคุณต้องการการลดความเร็วในระดับบล็อกคุณสามารถไปกับ let และ const แทน var

ตารางด้านล่างสรุปความแตกต่างระหว่าง var, let และ const

ป้อนคำอธิบายรูปภาพที่นี่


3

ให้เป็นส่วนหนึ่งของ es6 ฟังก์ชั่นเหล่านี้จะอธิบายความแตกต่างในวิธีที่ง่าย

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

ด้านล่างแสดงให้เห็นว่า 'ให้' และ 'var' แตกต่างกันอย่างไรในขอบเขต:

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

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

gfooกำหนดโดยletในขั้นต้นอยู่ในขอบเขตทั่วโลกและเมื่อเราประกาศgfooอีกครั้งภายในif clauseของขอบเขตการเปลี่ยนแปลงและเมื่อค่าใหม่จะกำหนดให้กับตัวแปรที่อยู่ภายในขอบเขตว่ามันไม่ได้ส่งผลกระทบต่อขอบเขตทั่วโลก

ในขณะที่hfooกำหนดโดยvarจะเริ่มต้นในขอบเขตทั่วโลกแต่อีกครั้งเมื่อเราประกาศภายในif clauseมันจะพิจารณาขอบเขตทั่วโลก hfoo แม้ว่า var ถูกนำมาใช้อีกครั้งเพื่อประกาศมัน และเมื่อเรากำหนดค่าใหม่เราจะเห็นว่าขอบเขตโกลบอล hfoo ได้รับผลกระทบด้วย นี่คือความแตกต่างหลัก


2

ดังกล่าวข้างต้น:

ความแตกต่างคือการกำหนดขอบเขต varถูกกำหนดขอบเขตไว้ที่บล็อกฟังก์ชันที่ใกล้ที่สุดและletถูกกำหนดขอบเขตไว้ที่บล็อกที่อยู่ใกล้ที่สุดซึ่งอาจเล็กกว่าบล็อกฟังก์ชัน ทั้งสองเป็นโกลบอลถ้าอยู่นอกบล็อกใด ๆ ให้ดูตัวอย่าง:

example1:

myfuncในทั้งสองตัวอย่างของฉันฉันมีฟังก์ชั่น myfuncมีตัวแปรmyvarเท่ากับ 10 ในตัวอย่างแรกของฉันฉันตรวจสอบว่าmyvarเท่ากับ 10 ( myvar==10) ถ้าใช่ฉัน agian ประกาศตัวแปร myvar(ตอนนี้ฉันมีสองตัวแปร myvar) โดยใช้varคำหลักและกำหนดค่าใหม่ (20) ในบรรทัดถัดไปฉันพิมพ์ค่าลงในคอนโซลของฉัน หลังจากบล็อกตามเงื่อนไขฉันพิมพ์ค่าของmyvarบนคอนโซลอีกครั้ง ถ้าคุณดูที่การส่งออกของmyfunc, myvarมีค่าเท่ากับ 20

ให้คำหลัก

Example2: ในตัวอย่างที่สองของฉันแทนการใช้varคำหลักในบล็อกเงื่อนไขของฉันฉันประกาศmyvarใช้letคำหลัก ตอนนี้เมื่อฉันเรียกmyfunc ฉันได้รับสองเอาท์พุทที่แตกต่างกันmyvar=20และmyvar=10และ

ดังนั้นความแตกต่างนั้นง่ายมากนั่นคือขอบเขตของมัน


3
โปรดอย่าโพสต์รูปภาพของรหัสถือว่าเป็นการปฏิบัติที่ไม่เหมาะสมใน SO เนื่องจากจะไม่สามารถค้นหาได้สำหรับผู้ใช้ในอนาคต (รวมถึงปัญหาการเข้าถึงได้ง่าย) รวมทั้งคำตอบนี้ไม่ได้เพิ่มสิ่งที่คำตอบอื่น ๆ ยังไม่ได้กล่าวถึง
inostia

2

ฉันต้องการเชื่อมโยงคำหลักเหล่านี้กับบริบทการดำเนินการเนื่องจากบริบทการดำเนินการมีความสำคัญในทุกสิ่งนี้ บริบทการดำเนินการมีสองขั้นตอน: ระยะการสร้างและระยะการดำเนินการ นอกจากนี้แต่ละบริบทการดำเนินการมีสภาพแวดล้อมที่แปรปรวนและสภาพแวดล้อมภายนอก (สภาพแวดล้อมของคำศัพท์)

ระหว่างขั้นตอนการสร้างของบริบทการดำเนินการ var, let และ const จะยังคงเก็บตัวแปรไว้ในหน่วยความจำด้วยค่าที่ไม่ได้กำหนดในสภาพแวดล้อมที่เปลี่ยนแปลงได้ของบริบทการดำเนินการที่กำหนด ความแตกต่างอยู่ในช่วงการดำเนินการ หากคุณใช้การอ้างอิงตัวแปรที่กำหนดด้วย var ก่อนที่จะได้รับการกำหนดค่ามันจะไม่ถูกกำหนด จะไม่มีข้อยกเว้น

อย่างไรก็ตามคุณไม่สามารถอ้างอิงตัวแปรที่ประกาศด้วย let หรือ const จนกว่าจะมีการประกาศ หากคุณพยายามที่จะใช้มันก่อนที่จะประกาศแล้วข้อยกเว้นจะถูกยกขึ้นในช่วงการดำเนินการของบริบทการดำเนินการ ตอนนี้ตัวแปรจะยังคงอยู่ในหน่วยความจำโดยขั้นตอนการสร้างของบริบทการดำเนินการ แต่เครื่องยนต์จะไม่อนุญาตให้คุณใช้งาน:

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

ด้วยตัวแปรที่กำหนดด้วย var หาก Engine ไม่สามารถค้นหาตัวแปรในสภาพแวดล้อมที่แปรปรวนของบริบทการดำเนินการปัจจุบันของมันจะขึ้นไปที่ขอบเขตของขอบเขต (สภาพแวดล้อมภายนอก) และตรวจสอบตัวแปรสภาพแวดล้อมภายนอกของสภาพแวดล้อมตัวแปร หากไม่พบที่นั่นก็จะค้นหาเชนของขอบเขตต่อไป นี่ไม่ใช่กรณีที่มีการปล่อยและ const

คุณลักษณะที่สองของการปล่อยคือการแนะนำขอบเขตบล็อก บล็อกถูกกำหนดโดยวงเล็บปีกกา ตัวอย่างรวมถึงฟังก์ชั่นบล็อก, ถ้าบล็อก, สำหรับบล็อก, ฯลฯ เมื่อคุณประกาศตัวแปรที่ให้ด้านในของบล็อกตัวแปรนั้นมีอยู่ในบล็อกเท่านั้น ในความเป็นจริงทุกครั้งที่มีการเรียกใช้บล็อกเช่นภายในสำหรับลูปมันจะสร้างตัวแปรใหม่ในหน่วยความจำ

ES6 ยังแนะนำคำสำคัญ const สำหรับการประกาศตัวแปร const ยังบล็อกขอบเขต ความแตกต่างระหว่าง let และ const คือตัวแปร const จำเป็นต้องประกาศโดยใช้ initializer หรือจะสร้างข้อผิดพลาด

และในที่สุดเมื่อมันมาถึงบริบทการดำเนินการตัวแปรที่กำหนดด้วย var จะถูกแนบไปกับวัตถุ 'this' ในบริบทการดำเนินการทั่วโลกนั่นจะเป็นวัตถุหน้าต่างในเบราว์เซอร์ นี่ไม่ใช่กรณีสำหรับ let หรือ const


2

ฉันคิดว่าคำศัพท์และตัวอย่างส่วนใหญ่ค่อนข้างล้นหลามประเด็นหลักที่ฉันมีกับความแตกต่างคือการเข้าใจว่า "บล็อก" คืออะไร เมื่อถึงจุดหนึ่งฉันรู้ว่าบล็อกจะเป็นวงเล็บปีกกาใด ๆ ยกเว้นIFคำสั่ง วงเล็บเปิด{ของฟังก์ชั่นหรือลูปจะกำหนดบล็อกใหม่อะไรก็ตามที่กำหนดไว้letภายในจะไม่สามารถใช้ได้หลังจากวงเล็บปิด}ของสิ่งเดียวกัน (ฟังก์ชั่นหรือลูป); โดยที่ในใจมันง่ายต่อการเข้าใจ:

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

ตอนนี้ฉันคิดว่ามีการกำหนดขอบเขตตัวแปรที่ดีกว่าให้กับกลุ่มข้อความสั่งโดยใช้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
}

ฉันคิดว่าผู้คนจะเริ่มใช้ให้ที่นี่หลังจากนั้นเพื่อให้พวกเขามีขอบเขตที่คล้ายกันใน JavaScript เช่นภาษาอื่น ๆ , Java, C #, ฯลฯ

ผู้ที่มีความเข้าใจที่ไม่ชัดเจนเกี่ยวกับการกำหนดขอบเขตใน JavaScript ใช้เพื่อทำผิดก่อนหน้านี้

hoisting letไม่สนับสนุนการใช้

ด้วยวิธีการนี้ข้อผิดพลาดที่มีอยู่ใน JavaScript จะถูกลบออก

อ้างอิงถึงES6 In Depth: ให้และ constเข้าใจมันให้ดีขึ้น


สำหรับความเข้าใจในเชิงลึกเกี่ยวกับมันอ้างอิงลิงค์ - davidwalsh.name/for-and-against-let
swaraj patil

1

บทความนี้กำหนดความแตกต่างอย่างชัดเจนระหว่าง var, let และ const

const เป็นสัญญาณที่ตัวระบุจะไม่ถูกกำหนดใหม่

letเป็นสัญญาณที่ตัวแปรอาจถูกกำหนดใหม่เช่นตัวนับในลูปหรือการแลกเปลี่ยนค่าในอัลกอริทึม นอกจากนี้ยังส่งสัญญาณว่าตัวแปรจะใช้เฉพาะในบล็อกที่กำหนดไว้ซึ่งไม่ใช่ฟังก์ชันที่มีทั้งหมด

varตอนนี้เป็นสัญญาณที่อ่อนแอที่สุดที่มีให้เมื่อคุณกำหนดตัวแปรใน JavaScript ตัวแปรอาจหรือไม่อาจกำหนดใหม่ได้และตัวแปรอาจหรือไม่อาจใช้สำหรับฟังก์ชันทั้งหมดหรือเพียงเพื่อวัตถุประสงค์ของบล็อกหรือลูป

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.