บทความนี้นำเสนอความรู้ที่เกี่ยวข้องเกี่ยวกับ JavaScript โดยส่วนใหญ่จะแนะนำตัววนซ้ำส่วนหน้าของ JavaScript และตัวสร้างเครื่องมือสร้าง เพื่อนๆ ที่ต้องการจะอ้างอิงถึงมันได้
การเข้าสู่ Front-end (vue) สู่หลักสูตรการเรียนรู้: เข้าสู่การเรียนรู้
Iterator จัดเตรียมกลไกอินเทอร์เฟซแบบรวมเพื่อจัดเตรียมกลไกการเข้าถึงแบบรวมสำหรับโครงสร้างข้อมูลที่แตกต่างกันต่างๆ
การกำหนด Iterator คือการจัดหาเมธอด next() ให้กับอ็อบเจ็กต์ แต่ละครั้งที่เรียก next() ออบเจ็กต์ผลลัพธ์จะถูกส่งคืน โดยมีแอตทริบิวต์ 2 รายการ คือ value แสดงถึงค่าปัจจุบัน และ Done แสดงถึงว่าการข้ามผ่านเสร็จสมบูรณ์หรือไม่ .
ฟังก์ชั่น makeIterator (อาร์เรย์) { ให้ดัชนี = 0; กลับ { ถัดไป: ฟังก์ชั่น () { กลับ ( Array.length > ดัชนี ? {value: อาร์เรย์[ดัชนี++]}: {เสร็จสิ้น: จริง} - - - - ให้ตัววนซ้ำ = makeIterator(['1','2']) console.log(iterator.next()); // {ค่า: '1'} console.log(iterator.next()); // {ค่า: '2'} console.log(iterator.next()); // {เสร็จสิ้น: จริง}
บทบาทของตัววนซ้ำ:
จัดเตรียมอินเทอร์เฟซการเข้าถึงแบบรวมและเรียบง่ายสำหรับโครงสร้างข้อมูลต่างๆ
ช่วยให้สมาชิกของโครงสร้างข้อมูลสามารถจัดเรียงตามลำดับที่แน่นอน
เพื่อการบริโภคโดยสำหรับ...ของ
ES6 จัดเตรียมคำสั่ง for of เพื่อสำรวจวัตถุตัววนซ้ำ เราจะใช้คำสั่ง for of เพื่อสำรวจตัววนซ้ำที่สร้างขึ้นด้านบน:
ให้ตัววนซ้ำ = makeIterator(['1','2']) สำหรับ (ให้ค่าของตัววนซ้ำ) { console.log(ค่า); } // ตัววนซ้ำไม่สามารถวนซ้ำได้
ผลลัพธ์คือข้อผิดพลาดที่แจ้งว่าตัววนซ้ำไม่สามารถทำซ้ำได้ เหตุใดจึงเป็นเช่นนั้น ES6 กำหนดว่าอินเทอร์เฟซ Iterator เริ่มต้นถูกปรับใช้ในคุณสมบัติ Symbol.iterator ของโครงสร้างข้อมูล หากโครงสร้างข้อมูลมีคุณสมบัติ Symbol.iterator โครงสร้างข้อมูลสามารถสำรวจได้
เราแปลงโฉม makeIterator แบบกำหนดเองดังนี้:
const MakeIterator = (อาร์เรย์) => ({ [สัญลักษณ์.ตัววนซ้ำ](){ ให้ดัชนี = 0; กลับ { ต่อไป(){ ให้ความยาว = Array.length; ถ้า (ดัชนี < ความยาว) { ส่งกลับ {ค่า: อาร์เรย์ [ดัชนี ++]} }อื่น{ กลับ {เสร็จสิ้น: จริง} - - - - - สำหรับ (ให้ค่าของ MakeIterator([1,2])){ console.log(ค่า) - // 1 // 2
เราเพิ่มเมธอด return ให้กับ MakeIterator หาก for...of ออกจากลูปก่อนกำหนด (โดยปกติจะเกิดจากข้อผิดพลาดหรือคำสั่งแบ่ง) เมธอด return() จะถูกเรียกเพื่อยุติการข้ามผ่าน
ตามคุณลักษณะนี้ หากออบเจ็กต์จำเป็นต้องล้างข้อมูลหรือปล่อยทรัพยากรก่อนที่จะเสร็จสิ้นการแวะผ่าน เราสามารถปรับใช้เมธอด return() และรวมการปิดไฟล์เมื่อการอ่านไฟล์ล้มเหลว
const MakeIterator = (อาร์เรย์) => ({ [สัญลักษณ์.ตัววนซ้ำ](){ ให้ดัชนี = 0; กลับ { ต่อไป(){ ให้ความยาว = Array.length; ถ้า (ดัชนี < ความยาว) { ส่งกลับ {ค่า: อาร์เรย์ [ดัชนี ++]} }อื่น{ กลับ {เสร็จสิ้น: จริง} - - กลับ(){ กลับ {เสร็จสิ้น: จริง} - - - - สำหรับ (ให้ค่าของ MakeIterator([1, 2, 3])){ console.log(ค่า) // 1 // วิธีที่ 1 หยุดพัก; // วิธีที่ 2 // โยนข้อผิดพลาดใหม่ ('ข้อผิดพลาด'); -
อาร์เรย์
ชุด
แผนที่
วัตถุที่มีลักษณะคล้ายอาร์เรย์ เช่น วัตถุอาร์กิวเมนต์ วัตถุ DOM NodeList วัตถุ typedArray
// อาร์กิวเมนต์ฟังก์ชันวัตถุผลรวม () { สำหรับ (ให้มูลค่าของการโต้แย้ง) { console.log(ค่า) - - ผลรวม(1,2) // 1 // 2 // วัตถุ typedArray ให้ typeArry = ใหม่ Int8Array (2); ประเภทArry[0] = 1; ประเภทArry[1] = 2; สำหรับ (ให้ค่าของ typeArry){ console.log(ค่า) - // 1 // 2
วัตถุเครื่องกำเนิด
ฟังก์ชั่น*gen(){ ผลผลิต 1; ผลผลิต 2; - สำหรับ (ให้ค่าของ gen()){ console.log(ค่า) -
สตริง
ถาม: เหตุใด Object จึงไม่มี Iterator ดั้งเดิม
ตอบ: สาเหตุที่ Object ไม่ปรับใช้อินเทอร์เฟซ Iterator ตามค่าเริ่มต้น เนื่องจากไม่แน่ใจว่าคุณสมบัติของอ็อบเจ็กต์ใดถูกสำรวจก่อน และคุณสมบัติใดถูกสำรวจในภายหลัง
โดยพื้นฐานแล้ว ทราเวอร์เซอร์เป็นกระบวนการเชิงเส้น สำหรับโครงสร้างข้อมูลที่ไม่ใช่เชิงเส้นใดๆ การใช้อินเทอร์เฟซทราเวอร์เซอร์จะเทียบเท่ากับการปรับใช้การแปลงเชิงเส้น
อย่างไรก็ตาม หากพูดอย่างเคร่งครัด อินเทอร์เฟซทราเวอร์เซอร์การปรับใช้วัตถุนั้นไม่จำเป็น เนื่องจากในขณะนี้วัตถุนั้นถูกใช้เป็นโครงสร้างแผนที่จริงๆ
งานทำลายล้าง
ให้ set = new Set().add('a').add('b').add('c'); ให้ [x,y] = ตั้งค่า; // x='a';
ตัวดำเนินการแพร่กระจาย
var str = 'สวัสดี'; [...str] // ['h','e','l','l','o']
ตัวดำเนินการสเปรดเรียกอินเทอร์เฟซ Iterator ดังนั้น Object จึงไม่ปรับใช้อินเทอร์เฟซ Iterator ดังนั้นเหตุใดจึงสามารถใช้ตัวดำเนินการ...
เหตุผล: ตัวดำเนินการสเปรดมีสองประเภท
หนึ่งถูกใช้ในกรณีของพารามิเตอร์ฟังก์ชันและการขยายอาร์เรย์ ในกรณีนี้ วัตถุจะต้องทำซ้ำได้ (ทำซ้ำได้)
อีกอันหนึ่งใช้สำหรับการขยายอ็อบเจ็กต์ นั่นคือ {...obj} ในกรณีนี้ อ็อบเจ็กต์จะต้องนับได้ (นับได้)
ให้ obj1 = { ชื่อ: 'เฉียนซุน' - ให้ obj2 = { อายุ: 3 - // อาร์เรย์อ็อบเจ็กต์สามารถนับได้ ให้ obj = {...obj1, ...obj2} console.log(obj) //{ชื่อ: 'qianxun' อายุ: 3} // วัตถุธรรมดาไม่สามารถทำซ้ำได้ตามค่าเริ่มต้น ให้ obj = [...obj1, ...obj2] console.log(obj) // object ไม่สามารถทำซ้ำได้
ฟังก์ชั่น forOf (obj, cb) { ให้ iteratorValue = obj[Symbol.iterator](); ให้ผลลัพธ์ = iteratorValue.next() ในขณะที่(!result.done){ cb(result.value) ผลลัพธ์ = iteratorValue.next() - - forOf([1,2,3], (ค่า)=>{ console.log(ค่า) - // 1 // 2 // 3
ตามแนวคิด
ฟังก์ชันตัวสร้างเป็นโซลูชันการเขียนโปรแกรมแบบอะซิงโครนัสที่จัดทำโดย ES6 ฟังก์ชัน Generator เป็นเครื่องสถานะที่สรุปสถานะภายในหลายสถานะ
ฟังก์ชัน Generator ยังเป็นฟังก์ชันการสร้างอ็อบเจ็กต์ Traverser ซึ่งจะส่งคืนอ็อบเจ็กต์ Traverser หลังจากดำเนินการ
เป็นทางการ
1. มีเครื่องหมายดอกจันระหว่างคำสำคัญฟังก์ชันและชื่อฟังก์ชัน
2. นิพจน์ Yield ถูกใช้ภายในเนื้อหาของฟังก์ชันเพื่อกำหนดสถานะภายในต่างๆ
ฟังก์ชั่น * simpleGenerator () { ผลผลิต 1; ผลผลิต 2; - เครื่องกำเนิดไฟฟ้าแบบง่าย ()
ดังที่กล่าวมาข้างต้น เราได้สร้าง Generator แบบง่ายๆ และเราสำรวจมันด้วยคำถามสองข้อ:
จะเกิดอะไรขึ้นหลังจากที่ฟังก์ชัน Generator ทำงาน?
นิพจน์ผลผลิตในฟังก์ชันทำอะไร?
ฟังก์ชั่น * simpleGenerator () { console.log('สวัสดีชาวโลก'); ผลผลิต 1; ผลผลิต 2; - ให้เครื่องกำเนิดไฟฟ้า = simpleGenerator(); // simpleGenerator {<suspended}} console.log (เครื่องกำเนิดไฟฟ้าถัดไป ()) //สวัสดีชาวโลก // {value: 1, เสร็จแล้ว: false} console.log (เครื่องกำเนิดไฟฟ้าถัดไป ()) // {value: 2, เสร็จแล้ว: false}
ฟังก์ชันตัวสร้างตัวสร้างส่งคืนอ็อบเจ็กต์ตัวสร้างหลังจากทำงาน ในขณะที่ฟังก์ชันธรรมดาจะรันโค้ดภายในฟังก์ชันโดยตรง แต่ละครั้งที่มีการเรียกใช้เมธอดถัดไปของอ็อบเจ็กต์ตัวสร้าง ฟังก์ชันจะถูกดำเนินการจนกว่าคีย์เวิร์ด Yield ถัดไปจะหยุดการดำเนินการ และ วัตถุ {value: Value, เสร็จแล้ว: Boolean}
นิพจน์ผลผลิตนั้นไม่มีค่าที่ส่งคืน หรือจะส่งกลับโดยไม่ได้กำหนดเสมอ วิธีถัดไปสามารถรับพารามิเตอร์ได้หนึ่งตัว ซึ่งจะถือเป็นค่าตอบแทนของนิพจน์ผลตอบแทนก่อนหน้า ด้วยพารามิเตอร์ของวิธีการถัดไปสามารถฉีดค่าที่แตกต่างกันจากภายนอกเข้าสู่ด้านในในขั้นตอนต่างๆ ของฟังก์ชัน Generator เพื่อปรับพฤติกรรมของฟังก์ชัน เนื่องจากพารามิเตอร์ของวิธีถัดไปแสดงถึงค่าที่ส่งคืนของนิพจน์ผลตอบแทนก่อนหน้า พารามิเตอร์ที่ส่งผ่านจึงไม่ถูกต้องในครั้งแรกที่ใช้วิธีถัดไป
ผลรวมฟังก์ชัน(x){ ฟังก์ชันส่งคืน (y) { กลับ x + y; - - console.log(ผลรวม(1)(2)) // ใช้พารามิเตอร์ถัดไปเพื่อเขียนฟังก์ชันใหม่* sum(x){ ให้ y = ให้ผลผลิต x; ในขณะที่ (จริง) { y = ผลผลิต x + y; - - ให้ Gen = ผลรวม (2) console.log(gen.next()) // 2 console.log(gen.next(1)) // 3 console.log(gen.next(2)) // 4
บทบาทของการแสดงออกของผลผลิต: การกำหนดสถานะภายในและการหยุดการดำเนินการชั่วคราว ความแตกต่างระหว่างการแสดงออกของผลผลิตและคำสั่งส่งคืน
นิพจน์ Yield หมายความว่าฟังก์ชันหยุดการดำเนินการชั่วคราวและดำเนินการย้อนกลับจากตำแหน่งนั้นในครั้งต่อไปต่อไป ในขณะที่คำสั่ง Return ไม่มีฟังก์ชันของหน่วยความจำตำแหน่ง
ในฟังก์ชัน สามารถดำเนินการคำสั่ง return ได้เพียงคำสั่งเดียว แต่สามารถดำเนินการได้หลายนิพจน์ผลตอบแทน
ฟังก์ชันใดๆ สามารถใช้คำสั่ง return ได้ นิพจน์ Yield สามารถใช้ได้เฉพาะในฟังก์ชัน Generator เท่านั้น หากใช้ที่อื่นจะมีการรายงานข้อผิดพลาด
หากนิพจน์ผลตอบแทนมีส่วนร่วมในการดำเนินการ ให้ใส่ไว้ในวงเล็บ หากใช้เป็นพารามิเตอร์ฟังก์ชันหรือวางไว้ทางด้านขวาของนิพจน์การกำหนด วงเล็บอาจไม่รวมอยู่ด้วย
ฟังก์ชั่น *gen () { console.log('สวัสดี' + ผลผลิต) × console.log('hello' + (ผลผลิต)) √ console.log('hello' + ผลตอบแทน 1) × console.log('hello' + (ผลผลิต 1)) √ ฟู(ผลผลิต 1) √ พารามิเตอร์ const = ผลผลิต 2 √ -
จากข้อเท็จจริงที่ว่าฟังก์ชันตัวสร้างตัวสร้างสามารถรองรับอัตราผลตอบแทนได้หลายค่า เราสามารถใช้สถานการณ์สมมติที่ฟังก์ชันมีค่าส่งคืนหลายค่าได้:
ฟังก์ชั่น * gen (num1, num2) { ผลผลิต num1 + num2; ผลผลิต num1 - num2; - ให้ res = gen(2, 1); console.log(res.next()) // {value: 3, เสร็จแล้ว: false} console.log(res.next()) // {value: 1, เสร็จแล้ว: false}
เนื่องจากฟังก์ชัน Generator เป็นฟังก์ชันการสร้างตัววนซ้ำ Generator จึงสามารถกำหนดให้กับคุณสมบัติ Symbol.iterator ของอ็อบเจ็กต์ได้ เพื่อให้อ็อบเจ็กต์มีอินเทอร์เฟซ Iterator รหัสการใช้งานตัวสร้างมีความกระชับมากขึ้น
ให้ obj = { ชื่อ: 'เฉียนซุน' อายุ: 3, [Symbol.iterator]: ฟังก์ชั่น(){ ปล่อยให้สิ่งนั้น = สิ่งนี้; ให้คีย์ = Object.keys (นั้น) ให้ดัชนี = 0; กลับ { ถัดไป: ฟังก์ชั่น () { ส่งคืนดัชนี <keys.length? {value: that[keys[index++]], เสร็จแล้ว: false}: {ค่า: ไม่ได้กำหนด เสร็จสิ้น: จริง} - - - - สำหรับ (ให้ค่าของ obj){ console.log(ค่า) -
เครื่องกำเนิดไฟฟ้า:
ให้ obj = { ชื่อ: 'เฉียนซุน' อายุ: 3, [Symbol.iterator]: ฟังก์ชั่น* (){ ให้คีย์ = Object.keys (สิ่งนี้) สำหรับ (ให้ i=0; i<keys.length; i++){ ให้สิ่งนี้ [คีย์ [i]]; - - - สำหรับ (ให้ค่าของ obj){ console.log(ค่า) -
เมธอด
return()
สามารถคืนค่าที่กำหนดและยุติฟังก์ชันตัวสร้างการแวะผ่าน
ฟังก์ชั่น*gen() { ผลผลิต 1; ผลผลิต 2; ผลผลิต 3; - var g = เจน(); g.next() // { ค่า: 1, เสร็จแล้ว: false } // หากไม่มีการระบุพารามิเตอร์เมื่อเรียกใช้เมธอด return() แอตทริบิวต์ value ของค่าที่ส่งคืนจะไม่ได้กำหนดไว้ g.return('foo') // { ค่า: "foo" เสร็จแล้ว: true } g.next() // { ค่า: ไม่ได้กำหนด, เสร็จแล้ว: จริง }
หากมี try...finally
code block ภายในฟังก์ชัน Generator และ try
code block กำลังถูกดำเนินการ ดังนั้นเมธอด return()
จะทำให้โค้ด block finally
ถูกป้อนทันที หลังจากดำเนินการ ฟังก์ชันทั้งหมดจะสิ้นสุด
ฟังก์ชัน* ตัวเลข () { ผลผลิต 1; พยายาม { ผลผลิต 2; ผลผลิต 3; } ในที่สุด { ผลผลิต 4; ผลผลิต 5; - ผลผลิต 6; - var g = ตัวเลข(); g.next() // { ค่า: 1, เสร็จแล้ว: false } g.next() // { ค่า: 2, เสร็จแล้ว: false } g.return(7) // { ค่า: 4 เสร็จแล้ว: false } g.next() // { ค่า: 5, เสร็จแล้ว: false } g.next() // { ค่า: 7, เสร็จแล้ว: จริง }
หากคุณต้องการเรียกใช้ฟังก์ชันตัวสร้างอื่นภายในฟังก์ชันตัวสร้าง เราจำเป็นต้องดำเนินการสำรวจภายในเนื้อหาของฟังก์ชันเดิมด้วยตนเอง หากการเรียกใช้ฟังก์ชันซ้อนอยู่ในหลายระดับ การเขียนจะยุ่งยากและอ่านยาก
มอบหมายให้เครื่องกำเนิดไฟฟ้าอื่น
ฟังก์ชั่น* g1() { ผลผลิต 2; ผลผลิต 3; - ฟังก์ชั่น* g2() { ผลผลิต 1; ผลตอบแทน* g1(); ผลผลิต 4; - ตัววนซ้ำ const = g2(); console.log(iterator.next()); // { ค่า: 1, เสร็จแล้ว: false } console.log(iterator.next()); // { ค่า: 2, เสร็จแล้ว: false } console.log(iterator.next()); // { ค่า: 3, เสร็จแล้ว: false } console.log(iterator.next()); // { ค่า: 4, เสร็จแล้ว: false } console.log(iterator.next()); // { ค่า: ไม่ได้กำหนด เสร็จแล้ว: จริง }
มอบหมายให้กับวัตถุที่สามารถทำซ้ำได้อื่น ๆ
ฟังก์ชั่น*gen(){ ผลผลิต* [1,2,3] - console.log(gen().next()) // {value: 1, เสร็จแล้ว: false}
ฟังก์ชัน Generator ส่งคืนทราเวอร์เซอร์ ES6 กำหนดว่าทราเวอร์เซอร์นี้เป็นอินสแตนซ์ของฟังก์ชัน Generator และสืบทอดวิธีการบนออบเจ็กต์ Generator.prototype แต่ไม่สามารถรับคุณสมบัติของสิ่งนี้ได้เนื่องจากนี่คือออบเจ็กต์ส่วนกลางในขณะนี้ ไม่ใช่อินสแตนซ์ วัตถุ.
ฟังก์ชั่น*gen(){ นี่.a = 1 - gen.prototype.say = ฟังก์ชั่น(){ console.log('สวัสดี') - ให้ obj = gen() console.log(obj instanceof gen) // จริง obj.say() //สวัสดี obj.ถัดไป() console.log(obj.a) //ไม่ได้กำหนด
หากคุณต้องการเข้าถึงคุณสมบัติของอินสแตนซ์ เช่น ตัวสร้าง คุณสามารถแก้ไขสิ่งนี้เพื่อผูกเข้ากับ Generator.prototype
ฟังก์ชั่น*gen(){ นี่.a = 1 - gen.prototype.say = ฟังก์ชั่น(){ console.log('สวัสดี') - ให้ obj = gen.call (gen.prototype) console.log(obj instanceof gen) // จริง obj.say() //สวัสดี obj.ถัดไป() console.log(obj.a) //1
ฟังก์ชั่น * StateMachine (สถานะ) { ปล่อยให้การเปลี่ยนแปลง; ในขณะที่ (จริง) { ถ้า (การเปลี่ยนแปลง === "เพิ่มขึ้น"){ รัฐ++; }else if(transition === "DECREMENT"){ สถานะ--; - การเปลี่ยนแปลง = สถานะผลผลิต; - - ตัววนซ้ำ const = StateMachine (0); console.log(iterator.next()); // 0 console.log(iterator.next('INCREMENT')); // 1 console.log(iterator.next('DECREMENT')); // 0