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 แบบคลาสสิก อ้างอิงถึงตัวแปรจะถูกเก็บไว้ในการปิดการคลิกจัดการมากกว่ามูลค่าที่แท้จริงของi
i
ตัวจัดการการคลิกทุกครั้งจะอ้างถึงวัตถุเดียวกันเพราะมีเพียงวัตถุตัวนับเดียวที่มี 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
ภายในฟังก์ชั่น ( แต่ด้านนอกของบล็อก) มีขอบเขตเช่นเดียวกับlet
var
(() => {
var functionScopedVariable = 42;
let blockScopedVariable = 43;
console.log(functionScopedVariable); // 42
console.log(blockScopedVariable); // 43
})();
console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
ตัวแปรที่ประกาศโดยใช้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
คุณไม่สามารถประกาศตัวแปรเดียวกันหลายครั้งโดยใช้ นอกจากนี้คุณยังไม่สามารถประกาศตัวแปรที่ใช้กับตัวระบุเช่นเดียวกับอีกตัวแปรหนึ่งที่มีการประกาศใช้let
var
var a;
var a; // Works fine.
let b;
let b; // SyntaxError: Identifier 'b' has already been declared
var c;
let c; // SyntaxError: Identifier 'c' has already been declared
const
const
ค่อนข้างคล้ายกับ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 โดยที่ตามมาหรือvar
let
let
ตัวแปรโกลบอลจะไม่ถูกเพิ่มในโกลบอล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และส่วนใหญ่จะอยู่ในข้อกำหนดขั้นสุดท้าย