ECMAScript 6 แนะนำคำสั่งlet
ฉันได้ยินมาว่ามันอธิบายว่าเป็นตัวแปร "ท้องถิ่น" แต่ฉันก็ยังไม่แน่ใจว่ามันจะทำงานแตกต่างจากvarคำหลักได้อย่างไร
อะไรคือความแตกต่าง? ควรletใช้มากกว่าเมื่อvarใด
ECMAScript 6 แนะนำคำสั่งlet
ฉันได้ยินมาว่ามันอธิบายว่าเป็นตัวแปร "ท้องถิ่น" แต่ฉันก็ยังไม่แน่ใจว่ามันจะทำงานแตกต่างจากvarคำหลักได้อย่างไร
อะไรคือความแตกต่าง? ควรletใช้มากกว่าเมื่อvarใด
คำตอบ:
ความแตกต่างที่สำคัญคือกฎการกำหนดขอบเขต ตัวแปรที่ประกาศโดย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]();ถูกเรียกใช้เนื่องจากฟังก์ชันที่ไม่ระบุชื่อถูกผูกไว้กับตัวแปรเดียวกัน
ผู้คนต้องสร้างฟังก์ชั่นที่เรียกใช้ทันทีเพื่อจับค่าที่ถูกต้องจากลูป แต่ก็มีขนดกเช่นกัน
ในขณะที่ตัวแปรที่ประกาศด้วย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
ในโหมดเข้มงวดคุณ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
letการแสดงออกของบล็อกlet (variable declaration) statementเป็นที่ไม่ได้มาตรฐานและจะถูกลบออกในอนาคตbugzilla.mozilla.org/show_bug.cgi?id=1023609
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>
letแต่มันเตือน "6" สำหรับปุ่มทั้งหมด คุณมีแหล่งข้อมูลใดที่บอกว่าletควรทำอย่างไร?
letมีประโยชน์จริงๆ การตั้งค่าตัวรับฟังเหตุการณ์ในลูปไม่ต้องการนิพจน์ฟังก์ชันที่เรียกใช้ทันทีเพื่อกำหนดขอบเขตแบบโลคัลiในแต่ละการวนซ้ำ
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 จะยกยกซึ่งหมายความว่าการประกาศจะอยู่ด้านบนสุดของขอบเขตเสมอ
(**) ตัวแปรที่กำหนดขอบเขตของบล็อกไม่ถูกยก
iรู้จักกันทุกที่ในฟังก์ชั่นบล็อค! มันเริ่มต้นเป็นundefined(เนื่องจากการชักรอก) จนกว่าคุณจะกำหนดค่า! ps: letถูกยกขึ้น (ไปยังด้านบนสุดของมันมีบล็อก) แต่จะให้ a ReferenceErrorเมื่ออ้างอิงในบล็อกก่อนที่จะได้รับมอบหมายครั้งแรก (ps2: ฉันเป็นคนประเภทอัฒภาค แต่คุณไม่ต้องการเซมิโคลอนหลังจากบล็อก) ที่ถูกกล่าวขอบคุณสำหรับการเพิ่มการตรวจสอบความเป็นจริงเกี่ยวกับการสนับสนุน!
letและvar!
letและconstได้รับการแนะนำให้ใช้เฉพาะเมื่อคุณต้องการฟังก์ชั่นเพิ่มเติมของพวกเขาเพราะบังคับใช้ / ตรวจสอบคุณสมบัติพิเศษเหล่านี้ (เช่น const แบบเขียนอย่างเดียว) ส่งผลให้ '(และขอบเขตเพิ่มเติมโหนดในขอบเขตต้นไม้) สำหรับเอ็นจิ้น (ปัจจุบัน) เพื่อบังคับใช้ / ตรวจสอบ / ตรวจสอบ / ตั้งค่า
นี่คือคำอธิบายของletคำหลักที่มีตัวอย่างบางส่วน
letชอบvarมาก ความแตกต่างที่สำคัญคือขอบเขตของvarตัวแปรคือฟังก์ชั่นการล้อมรอบทั้งหมด
ตารางนี้บน Wikipedia แสดงว่าเบราว์เซอร์ใดรองรับ Javascript 1.7
โปรดทราบว่ามีเพียงเบราว์เซอร์ Mozilla และ Chrome เท่านั้นที่รองรับ IE, Safari และอื่น ๆ ที่อาจเกิดขึ้นไม่ได้
let msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx
คำตอบที่ได้รับการยอมรับไม่มีจุด:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
forการเริ่มต้นห่วงอย่างมาก จำกัด ขอบเขตของการประยุกต์ใช้ข้อ จำกัด letของ upvoted
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
constconstค่อนข้างคล้ายกับ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()หากคุณต้องการที่จะมีการเปลี่ยนรูปวัตถุที่คุณควรใช้
constคุณต้องระบุค่าเมื่อประกาศตัวแปรที่ใช้
const a; // SyntaxError: Missing initializer in const declaration
นี่คือตัวอย่างสำหรับความแตกต่างระหว่างทั้งสอง (การสนับสนุนเพิ่งเริ่มต้นสำหรับ 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);
มีความแตกต่างเล็กน้อยบางอย่าง - letกำหนดขอบเขตจะทำงานมากกว่าการกำหนดขอบเขตตัวแปรในภาษาอื่น ๆ ไม่มากก็น้อย
เช่นขอบเขตของสิ่งที่แนบมาพวกมันไม่มีอยู่ก่อนที่มันจะถูกประกาศ ฯลฯ
อย่างไรก็ตามมันเป็นเรื่องที่น่าสังเกตว่าletเป็นเพียงส่วนหนึ่งของการใช้งาน Javascript ที่ใหม่กว่าและมีการรองรับเบราว์เซอร์ที่หลากหลาย
letรวมอยู่ในร่างฉบับที่ 6และน่าจะเป็นข้อกำหนดขั้นสุดท้าย
letเท่านั้น Safari, IE และ Chome ไม่สามารถทำได้
letอย่ายกขึ้นเพื่อใช้ตัวแปรที่กำหนดโดยletนิยามที่ด้านบนสุดของบล็อก หากคุณมีifคำสั่งที่เป็นมากกว่าโค้ดสองสามบรรทัดคุณอาจลืมว่าคุณไม่สามารถใช้ตัวแปรนั้นจนกว่าจะมีการกำหนด จุดที่ดี !!!
let จะยกตัวแปรขึ้นไปด้านบนของบล็อกอย่างไรก็ตามการอ้างอิงตัวแปรในบล็อกก่อนที่การประกาศตัวแปรจะส่งผลให้มีReferenceError (บันทึกย่อของฉัน: ดีเก่าundefined) ตัวแปรอยู่ใน 'เขตเวลาชั่วคราว' จากจุดเริ่มต้นของบล็อกจนกระทั่งมีการประมวลผลการประกาศ " ไปเหมือนกันสำหรับ "switch statement เพราะมีเพียงหนึ่งบล็อกพื้นฐาน" ที่มา: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
ความแตกต่างที่สำคัญคือความแตกต่างของขอบเขตในขณะที่ให้สามารถใช้ได้เฉพาะภายในขอบเขตที่มีการประกาศเช่นในการวนรอบ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
ตัวแปรไม่ได้ติตตั้ง
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 เพื่อประเมินการแสดงออกเพิ่มขึ้น) รายละเอียดเพิ่มเติมที่นี่
นี่คือตัวอย่างของการเพิ่มสิ่งที่ผู้อื่นเขียนไปแล้ว สมมติว่าคุณต้องการสร้างอาเรย์ของฟังก์ชั่นโดย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
let value = i;ที่จริงแล้วคุณไม่จำเป็นต้อง forงบสร้างบล็อกคำศัพท์
ความแตกต่างอยู่ในขอบเขตของตัวแปรที่ประกาศพร้อมกัน
ในทางปฏิบัติมีจำนวนของผลที่เป็นประโยชน์ของความแตกต่างในขอบเขต:
letตัวแปรจะปรากฏเฉพาะในบล็อกที่อยู่ใกล้ที่สุด ( { ... })letตัวแปรสามารถใช้งานได้ในบรรทัดของรหัสที่เกิดขึ้นหลังจากประกาศตัวแปร (แม้ว่าจะมีการยก !)letตัวแปรที่อาจจะไม่ได้ redeclared โดยที่ตามมาหรือvarletletตัวแปรโกลบอลจะไม่ถูกเพิ่มในโกลบอลwindowออบเจค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
varแทนletรหัสจะเท่ากับ: var i = 0; while (i < 5) { doSomethingLater(); i++; } iอยู่นอกการปิดและตามเวลาที่doSomethingLater()ดำเนินการiได้เพิ่มขึ้น 5 ครั้งแล้วดังนั้นผลลัพธ์จะเป็นi is 5ห้าครั้ง โดยใช้letตัวแปรiอยู่ภายในปิดเพื่อให้การโทรแต่ละ async ได้รับสำเนาของตัวเองiแทนการใช้ 'โลก' varหนึ่งที่สร้างขึ้นด้วย
forนั่นเป็นเพียงความหมายปกติของความหมายของ การแปลงที่แม่นยำยิ่งขึ้นแม้ว่าจะซับซ้อนกว่าเดิมคือfor (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(i คลาสสิกคือ $ {j} ), 125/*ms*/); })(i); }ซึ่งแนะนำ "ฟังก์ชั่นบันทึกการเปิดใช้งานฟังก์ชั่น" เพื่อบันทึกค่าแต่ละค่าiด้วยชื่อของjฟังก์ชั่นภายใน
สองฟังก์ชันต่อไปนี้อาจแสดงความแตกต่าง:
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
}
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]
ความแตกต่างที่สำคัญระหว่าง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วัตถุ (พิจารณาอย่างรอบคอบหากจำเป็นจริงๆ)
นอกจากนี้ยังปรากฏว่าอย่างน้อยใน Visual Studio 2015, TypeScript 1.5, "var" อนุญาตให้มีการประกาศหลายชื่อตัวแปรเดียวกันในบล็อกและ "อนุญาต" ไม่ได้
สิ่งนี้จะไม่สร้างข้อผิดพลาดในการคอมไพล์:
var x = 1;
var x = 2;
นี่จะ:
let x = 1;
let x = 2;
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
เมื่อใช้งาน 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สำหรับการวนซ้ำทุกครั้งตัวแปรจะถูกกำหนดขอบเขตและมีการแชร์สำเนา
ถ้าผมอ่านรายละเอียดที่ถูกต้องแล้ว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 ด้วย "ฟังก์ชั่นเรียกตนเอง")
hiddenPropertyในนวกรรมิก? มีเพียงครั้งเดียวhiddenPropertyสำหรับทุกอินสแตนซ์ใน "คลาส" ของคุณ
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:
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 คุณสมบัติประเภท / ชื่อ / ค่าและเริ่มต้นพวกเขาด้วยค่าคุณสมบัติจากโหนด?
varเช่นกัน
ให้เทียบกับ มันเป็นเรื่องของขอบเขต
ตัวแปร 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.
ES6 แนะนำสองคำหลักใหม่ ( ให้และconst ) ทางเลือกเพื่อvar var
เมื่อคุณต้องการการลดความเร็วในระดับบล็อกคุณสามารถไปกับ let และ const แทน var
ตารางด้านล่างสรุปความแตกต่างระหว่าง var, let และ const
ให้เป็นส่วนหนึ่งของ 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
}
ด้านล่างแสดงให้เห็นว่า 'ให้' และ '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 ได้รับผลกระทบด้วย นี่คือความแตกต่างหลัก
ดังกล่าวข้างต้น:
ความแตกต่างคือการกำหนดขอบเขต
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และ
ดังนั้นความแตกต่างนั้นง่ายมากนั่นคือขอบเขตของมัน
ฉันต้องการเชื่อมโยงคำหลักเหล่านี้กับบริบทการดำเนินการเนื่องจากบริบทการดำเนินการมีความสำคัญในทุกสิ่งนี้ บริบทการดำเนินการมีสองขั้นตอน: ระยะการสร้างและระยะการดำเนินการ นอกจากนี้แต่ละบริบทการดำเนินการมีสภาพแวดล้อมที่แปรปรวนและสภาพแวดล้อมภายนอก (สภาพแวดล้อมของคำศัพท์)
ระหว่างขั้นตอนการสร้างของบริบทการดำเนินการ 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
ฉันคิดว่าคำศัพท์และตัวอย่างส่วนใหญ่ค่อนข้างล้นหลามประเด็นหลักที่ฉันมีกับความแตกต่างคือการเข้าใจว่า "บล็อก" คืออะไร เมื่อถึงจุดหนึ่งฉันรู้ว่าบล็อกจะเป็นวงเล็บปีกกาใด ๆ ยกเว้น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);
ตอนนี้ฉันคิดว่ามีการกำหนดขอบเขตตัวแปรที่ดีกว่าให้กับกลุ่มข้อความสั่งโดยใช้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เข้าใจมันให้ดีขึ้น
บทความนี้กำหนดความแตกต่างอย่างชัดเจนระหว่าง var, let และ const
constเป็นสัญญาณที่ตัวระบุจะไม่ถูกกำหนดใหม่
letเป็นสัญญาณที่ตัวแปรอาจถูกกำหนดใหม่เช่นตัวนับในลูปหรือการแลกเปลี่ยนค่าในอัลกอริทึม นอกจากนี้ยังส่งสัญญาณว่าตัวแปรจะใช้เฉพาะในบล็อกที่กำหนดไว้ซึ่งไม่ใช่ฟังก์ชันที่มีทั้งหมด
varตอนนี้เป็นสัญญาณที่อ่อนแอที่สุดที่มีให้เมื่อคุณกำหนดตัวแปรใน JavaScript ตัวแปรอาจหรือไม่อาจกำหนดใหม่ได้และตัวแปรอาจหรือไม่อาจใช้สำหรับฟังก์ชันทั้งหมดหรือเพียงเพื่อวัตถุประสงค์ของบล็อกหรือลูป
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
letรวมอยู่ในร่างฉบับที่ 6และส่วนใหญ่จะอยู่ในข้อกำหนดขั้นสุดท้าย