ฉันกำลังดูการฝึกฝน ES2015 เมื่อเร็ว ๆ นี้มีคำพูดที่บอกว่า
ไม่มีขอบเขตระดับบล็อกใน JavaScript
คุณอาจไม่เข้าใจปัญหานี้มาดูตัวอย่างก่อน
var a = [] สำหรับ (var i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] ();ฉันคิดว่าหลายคนคิดว่าผลลัพธ์ของคำถามนี้คือ 6 แต่น่าเสียดายที่คำตอบคือ 10 ลองอย่างอื่น A [7] (), A [8] () และ A [8] () ทั้งหมดมี 10 !!
เนื่องจาก JS มักจะห่อตัวแปรดั้งเดิมลงในวัตถุที่สอดคล้องกันเมื่อทำการประมวลผลตัวอย่างเช่นสำหรับ var str = "Hello World"; str.slice (1)
กระบวนการที่แท้จริงของ JS อาจเป็น var str = "hello world"; สตริงใหม่ (str) .slice (1) กระบวนการดังกล่าวอาจทำให้เรามีปัญหาในการเข้าใจปัญหา
เพื่ออธิบายปัญหานี้ที่นี่ฉันเป็นของประเภทตัวเลขในประเภทดั้งเดิมฉันประกาศอย่างชัดเจนว่าเป็นประเภทตัวเลข เนื่องจากกระบวนการกำหนดประเภทพื้นฐานคือการใช้หน่วยความจำใหม่และปรับเปลี่ยนทิศทางของตัวแปรเราจึงใช้กระบวนการของวัตถุหมายเลขใหม่เพื่อจำลองกระบวนการนี้ รหัสที่แก้ไขมีดังนี้:
var a = [] var i = หมายเลขใหม่ (0); สำหรับ (; i <10; i = หมายเลขใหม่ (i+1)) {a [i] = function () {console.log (i.toString ()); }} a [6] (); // 10a [7] (); // 10a [8] (); // 10a [9] (); // 10a [9] (); // 10ลองมาดูที่ที่อยู่หน่วยความจำสัมพัทธ์ของตัวแปรเหล่านี้ร่วมกับโปรแกรม
(function () {var id = 0; function generateId () {return id ++;}; object.prototype.id = function () {var newid = generateid (); this.id = function () {return newId;}; return newId;};};}; = หมายเลขใหม่ (i+1), i.id ()) {a [i] = function () {console.log (i.id ()); console.log (i.toString ()); }} a [6] (); // 10 10a [7] (); // 10 10a [8] (); // 10 10a [9] (); // 10 10console.log (i.id ()) // 10ที่นี่เราได้จำลองผล "การมอบหมาย" ทั้งหมดของฉันและที่อยู่สัมพัทธ์ของฉันเปลี่ยนจาก 0 เป็น 10 (ในที่สุดมันจะต้องเพิ่มหนึ่งครั้งก่อนที่มันจะสามารถกระโดดออกจากลูป)
ในขณะที่ดูที่อยู่สัมพัทธ์ของฉันเราพบปัญหา: เมื่อฟังก์ชั่นที่สอดคล้องกับ [x] (x: 0 ~ 9) ถูกดำเนินการที่อยู่สัมพัทธ์ของฉันอ้างอิงคือ 10 ทำไม?
ที่นี่เราจะเกี่ยวข้องกับปัญหาขอบเขตระดับบล็อก ที่นี่เราอ้างข้อความจาก Ruan Yifeng ในบทนำสู่ ES6:
ES5 มีขอบเขตและขอบเขตฟังก์ชันทั่วโลกเท่านั้น แต่ไม่มีขอบเขตระดับบล็อก
ES5 เป็น JS รุ่นที่ใช้กันอย่างแพร่หลายมากที่สุด ประโยคนี้บอกว่าใน JavaScript ไม่มีขอบเขตบล็อก มีขอบเขตระดับโลกและขอบเขตระดับบล็อกเท่านั้น
จะเข้าใจได้อย่างไร? ตัวอย่างเช่น
สำหรับ (var i = 0; i <10; i ++) {console.log (i);} console.log (i); // 10console.log (window.i); // 10โดยสังหรณ์ใจเราคิดว่าการวนรอบเป็นบล็อกโค้ดและควรอยู่ในขอบเขตระดับบล็อก อย่างไรก็ตามไม่เพียง แต่สามารถส่งออก 0 ถึง 9 ตามปกติ แต่ยังสามารถส่งออก 10 ภายนอกในการวนรอบ ในเวลาเดียวกันเราพบว่าถึงแม้ว่าเราจะกำหนดให้ฉันอยู่ในวงสำหรับลูปดูเหมือนว่าฉันกำลังแขวนอยู่บนวัตถุหน้าต่างทั่วโลก (ถ้าเป็นสภาพแวดล้อมการดำเนินการของ nodejs มันจะแขวนอยู่บนวัตถุทั่วโลก)
ดังนั้นบล็อกเช่นลูปในจาวาสคริปต์จะไม่มีผลกระทบของขอบเขตระดับบล็อก การกำหนดตัวแปรในบล็อกรหัสเช่นสำหรับลูปไม่แตกต่างจากการกำหนดตัวแปรโดยตรงในขอบเขตปัจจุบัน
แต่เราสามารถแยกขอบเขตผ่านฟังก์ชั่น:
(function () {สำหรับ (var i = 0; i <10; i ++) {console.log (i);} console.log (i);}) () console.log (i); //// ฉันไม่ได้กำหนดไว้ในเวลาเดียวกันถ้า console.log (window.i); จะถูกดำเนินการผลลัพธ์ที่ไม่ได้กำหนดจะได้รับ ที่นี่เราใช้ฟังก์ชั่นการดำเนินการทันทีเพื่อสร้างขอบเขต มันมีบทบาทคล้ายกับบล็อกรหัส หลังจากขอบเขตฟังก์ชันนี้ตัวแปรที่ฉันไม่สามารถเข้าถึงได้อีกต่อไป อย่างไรก็ตามฉันสามารถเข้าถึงได้ตลอดเวลาภายในขอบเขตฟังก์ชัน
กลับไปที่คำถามก่อนหน้านี้จากนั้นเราจะเข้าใจร่วมกับขอบเขตระดับโลกและขอบเขตระดับบล็อกใน JavaScript ในการวนรอบสำหรับการวนรอบฉันจะต้องกำหนดไว้ในขอบเขตปัจจุบันนั่นคือขอบเขตหน้าต่าง ในร่างกายลูปเรากำหนดฟังก์ชั่นให้กับ [i] เมื่อเราดำเนินการฟังก์ชั่นนี้สถานการณ์มีดังนี้:
ฉันไม่มีอยู่ในฟังก์ชั่นดังนั้นเราจึงติดตามขอบเขตโซ่เพื่อค้นหาฉัน ฉันเอาท์พุทในเวลานี้คือฉัน เนื่องจากฉันกระโดดออกมาจาก +1 สุดท้ายของลูปฉันจึงกลายเป็น 10 ดังนั้นผลลัพธ์ผลลัพธ์คือ 10 เสมอ แต่สิ่งที่เราต้องการจริงๆไม่ใช่ครั้งสุดท้ายฉัน แต่ฉันอยู่ในกระบวนการระดับกลาง หากเราต้องการแก้ปัญหานี้เราจำเป็นต้องวางตัวแปรฉัน (เพราะสุดท้ายที่ฉันกลายเป็น 10 อย่างหลีกเลี่ยงไม่ได้) เราจำเป็นต้องให้ฟังก์ชันที่สอดคล้องกับ [0] อ้างถึงค่า 0 และให้ฟังก์ชันที่สอดคล้องกับ [1] อ้างถึงค่า 1. ดังแสดงในรูปด้านล่าง:
กลับไปที่รหัสก่อนหน้าของเรา
ลูกศรในรูปแสดงให้เห็นว่าเราสามารถเข้าถึงฉัน (0 ~ 9) ที่นี่เนื่องจาก For Loop ไม่ได้สร้างขอบเขตระดับบล็อกด้วยตัวเองเราจึงเข้าถึง I ที่ฉันกำหนดโดย For Loop เมื่อเราทำตามโซ่ขอบเขต
ที่นี่เราห่อรหัสของเราด้วยฟังก์ชั่นการดำเนินการทันทีซึ่งสามารถสร้างขอบเขตและเราผ่านค่าที่ฉันไป ต่อไปนี้:
var a = [] var i = หมายเลขใหม่ (0); console.log (i.id ()); // 0for (; i <10; i = หมายเลขใหม่ (i+1), i.id ()) {(ฟังก์ชั่น (i) {a [i] = function () {console.log (i.id (); // 6 6a [7] (); // 7 7a [8] (); // 8 8a [9] (); // 9 9console.log (i.id ()); // 10}เนื่องจากฟังก์ชั่นการดำเนินการทันทีนี้หมายถึงค่าตัวเลข 0 ~ 9 เมื่อเราดำเนินการฟังก์ชั่น A [i] เราจะพบขอบเขตของฟังก์ชันการดำเนินการทันทีตามห่วงโซ่ขอบเขต ฟังก์ชั่นการดำเนินการทันทีรักษาข้อมูลอ้างอิงเชิงตัวเลขจาก 0 ~ 9 และเราสามารถส่งออกค่าของ i ในฟังก์ชัน A [i] ได้อย่างถูกต้อง ผ่านผลการดำเนินการเราจะเห็นว่าไม่เพียง แต่ผลการดำเนินการที่ถูกต้อง แต่ที่อยู่หน่วยความจำสัมพัทธ์ของค่าที่เราอ้างอิงนั้นถูกต้องเช่นกัน จากนั้นเราเปลี่ยนวัตถุตัวเลขที่ประกาศไว้อย่างชัดเจนสำหรับการทดสอบ ดังนี้:
var a = []; สำหรับ (var i = 0; i <10; i ++) {(ฟังก์ชั่น (i) {a [i] = function () {console.log (i);}}) (i);}สุดท้ายลองมาดูไวยากรณ์ ES6 ที่แนะนำโดยใช้ LET แทน VAR และรวบรวมและสร้างรหัส ES5 ผ่าน BAABLE:
// es6 code var a = [] สำหรับ (let i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] (); // Babel รวบรวมและสร้างรหัส ES5 "ใช้อย่างเข้มงวด"; var a = []; var _loop = ฟังก์ชั่น _loop (i) {a [i] = function () {console.log (i); };}; สำหรับ (var i = 0; i <10; i ++) {_loop (i);} a [6] ();มาดูกันว่าโซลูชันของเราคล้ายกับโซลูชัน ES6 หรือไม่ ที่นี่ฟังก์ชั่นการดำเนินการทันทีของเราเทียบเท่ากับการดำเนินการของฟังก์ชัน _loop และ _loop (i) ในรหัส ES5 ที่สร้างขึ้น