การจัดการแต่ละรายการในคอลเลกชันเป็นการดำเนินการที่พบบ่อยมาก JavaScript ให้วิธีการวนซ้ำหลายวิธีผ่านคอลเลกชันจาก Simple สำหรับและสำหรับแต่ละลูปไปยัง MAP (), ตัวกรอง () และความเข้าใจอาร์เรย์ (Array Derivation) ใน JavaScript 1.7 ตัววนซ้ำและเครื่องกำเนิดไฟฟ้านำกลไกการวนซ้ำใหม่ในไวยากรณ์หลัก JavaScript และยังมีกลไกในการปรับแต่งพฤติกรรมของ ... ในและสำหรับแต่ละลูป
ตัววนซ้ำ
ตัววนซ้ำเป็นวัตถุที่เข้าถึงองค์ประกอบในลำดับการรวบรวมทุกครั้งและติดตามตำแหน่งปัจจุบันของการวนซ้ำในลำดับนั้น ใน iterator JavaScript เป็นวัตถุที่ให้วิธีถัดไป () ซึ่งส่งคืนองค์ประกอบถัดไปในลำดับ วิธีนี้จะทำให้เกิดข้อยกเว้นการหยุดยั้งเมื่อองค์ประกอบทั้งหมดในลำดับถูกสำรวจ
เมื่อสร้างวัตถุตัววนซ้ำแล้วมันสามารถเรียกได้โดยปริยายโดยการทำซ้ำอย่างชัดเจนถัดไป () หรือโดยใช้ JavaScript สำหรับ ... ในและสำหรับแต่ละลูป
ตัววนซ้ำง่าย ๆ ที่วนซ้ำผ่านวัตถุและอาร์เรย์สามารถสร้างได้โดยใช้ iterator ():
การคัดลอกรหัสมีดังนี้:
var lang = {ชื่อ: 'JavaScript', วันเกิดปี: 1995};
var it = iterator (lang);
เมื่อการเริ่มต้นเสร็จสมบูรณ์วิธีถัดไป () สามารถเรียกใช้เพื่อเข้าถึงคู่คีย์-ค่าของวัตถุในทางกลับกัน:
การคัดลอกรหัสมีดังนี้:
var pair = it.next (); // คู่คีย์-ค่าคือ ["ชื่อ", "JavaScript"]
pair = it.next (); // คู่คีย์-ค่าคือ ["วันเกิด", 1995]
pair = it.next (); // a `uptiteration` มีการโยนข้อยกเว้น
สำหรับ ... ในลูปสามารถใช้เพื่อแทนที่การโทรที่ชัดเจนไปยังวิธีถัดไป () เมื่อมีการโยนข้อยกเว้นการหยุดยั้งการวนซ้ำลูปจะสิ้นสุดโดยอัตโนมัติ
การคัดลอกรหัสมีดังนี้:
var it = iterator (lang);
สำหรับ (var pair ในนั้น)
พิมพ์ (คู่); // หนึ่ง [คีย์, ค่า] คู่คีย์-ค่าในนั้นจะถูกส่งออกในแต่ละครั้ง
หากคุณต้องการทำซ้ำค่าคีย์ของวัตถุเท่านั้นคุณสามารถส่งพารามิเตอร์ที่สองลงในฟังก์ชัน iterator () ด้วยค่าจริง:
การคัดลอกรหัสมีดังนี้:
var it = iterator (lang, true);
สำหรับ (คีย์ var ในนั้น)
พิมพ์ (กุญแจ); // ค่าคีย์เอาต์พุตเท่านั้น
ข้อดีอย่างหนึ่งของการใช้ iterator () เพื่อเข้าถึงวัตถุคือคุณสมบัติที่กำหนดเองที่เพิ่มเข้ามาใน object.prototype ไม่รวมอยู่ในวัตถุลำดับ
Iterator () ยังสามารถใช้กับอาร์เรย์ได้:
การคัดลอกรหัสมีดังนี้:
var langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (langs);
สำหรับ (var pair ในนั้น)
พิมพ์ (คู่); // เฉพาะเอาต์พุตซ้ำ [ดัชนีภาษา] คู่คีย์-ค่า
เช่นเดียวกับการสำรวจวัตถุผลลัพธ์ของการส่งผ่านจริงเข้าไปในการสำรวจเป็นพารามิเตอร์ที่สองจะเป็นดัชนีอาร์เรย์:
การคัดลอกรหัสมีดังนี้:
var langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (langs, true);
สำหรับ (var i ในนั้น)
พิมพ์ (i); // เอาท์พุท 0 จากนั้น 1 จากนั้น 2
ใช้คำหลักเพื่อกำหนดดัชนีและค่าเพื่อบล็อกตัวแปรแยกต่างหากภายในลูปและคุณยังสามารถทำลายโครงสร้างการกำหนด (การกำหนดโครงสร้างที่กำหนด):
การคัดลอกรหัสมีดังนี้:
var langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterators (langs);
สำหรับ (ให้ [i, lang] ในนั้น)
พิมพ์ (i + ':' + lang); // เอาต์พุต "0: JavaScript" ฯลฯ
ประกาศตัววนซ้ำที่กำหนดเอง
วัตถุบางอย่างที่เป็นตัวแทนของการรวบรวมองค์ประกอบควรได้รับการทำซ้ำในวิธีที่ระบุ
1. วนซ้ำวัตถุที่แสดงช่วง (ช่วง) ควรส่งคืนหมายเลขที่มีอยู่ในช่วงนี้ทีละหนึ่ง
2. โหนดใบของต้นไม้สามารถเข้าถึงได้โดยใช้ความลึกแรกหรือแบบกว้างก่อน
3. การวนซ้ำผ่านวัตถุที่แสดงผลลัพธ์การสืบค้นฐานข้อมูลควรส่งคืนทีละแถวแม้ว่าชุดผลลัพธ์ทั้งหมดจะไม่ถูกโหลดลงในอาร์เรย์เดียว
4. ตัววนซ้ำที่ทำหน้าที่ตามลำดับทางคณิตศาสตร์ที่ไม่มีที่สิ้นสุด (เช่นลำดับ Fibonacci) ควรส่งคืนผลลัพธ์หลังจากนั้นโดยไม่ต้องสร้างโครงสร้างข้อมูลความยาวที่ไม่มีที่สิ้นสุด
JavaScript อนุญาตให้คุณเขียนโค้ดที่ปรับแต่งตรรกะซ้ำและนำไปใช้กับวัตถุ
เราสร้างวัตถุช่วงอย่างง่ายด้วยสองค่า:
การคัดลอกรหัสมีดังนี้:
ช่วงฟังก์ชัน (ต่ำ, สูง) {
this.low = ต่ำ;
this.high = สูง;
-
ตอนนี้เราสร้างตัววนซ้ำแบบกำหนดเองที่ส่งคืนลำดับของจำนวนเต็มทั้งหมดในช่วง อินเทอร์เฟซตัววนซ้ำต้องการให้เราจัดเตรียมเมธอดถัดไป () เพื่อส่งคืนองค์ประกอบถัดไปในลำดับหรือโยนข้อยกเว้นการหยุดยั้ง
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น rangeRiterator (ช่วง) {
this.Range = ช่วง;
this.current = this.range.low;
-
rangeIterator.prototype.next = function () {
if (this.current> this.range.high)
โยนการหยุด;
อื่น
ส่งคืนสิ่งนี้ปัจจุบัน ++;
-
RangeIterator ของเราได้รับการสร้างอินสแตนซ์ด้วยอินสแตนซ์ในขณะที่ยังคงรักษาคุณสมบัติปัจจุบันเพื่อติดตามตำแหน่งของลำดับปัจจุบัน
ในที่สุดเพื่อให้ RangeIterator รวมกับช่วงเราจำเป็นต้องเพิ่มวิธี __iterator__ พิเศษลงในช่วง เมื่อเราพยายามทำซ้ำในช่วงมันจะถูกเรียกและควรส่งคืนอินสแตนซ์ของ rangeIterator ที่ใช้ตรรกะซ้ำ
การคัดลอกรหัสมีดังนี้:
range.prototype .__ iterator__ = function () {
ส่งคืน RangeIterator ใหม่ (นี้);
-
หลังจากเสร็จสิ้นการวนซ้ำที่กำหนดเองของเราเราสามารถทำซ้ำผ่านอินสแตนซ์ขอบเขต:
การคัดลอกรหัสมีดังนี้:
ช่วง var = ช่วงใหม่ (3, 5);
สำหรับ (var i อยู่ในช่วง)
พิมพ์ (i); // เอาท์พุท 3 จากนั้น 4 จากนั้น 5
เครื่องกำเนิด: วิธีที่ดีกว่าในการสร้างตัววนซ้ำ
แม้ว่าตัววนซ้ำที่กำหนดเองเป็นเครื่องมือที่มีประโยชน์ แต่คุณต้องวางแผนอย่างรอบคอบเมื่อสร้างพวกเขาเพราะพวกเขาจำเป็นต้องได้รับการดูแลอย่างชัดเจน
เครื่องกำเนิดไฟฟ้ามีฟังก์ชั่นที่มีประสิทธิภาพ: ช่วยให้คุณกำหนดฟังก์ชั่นที่มีอัลกอริทึมซ้ำของคุณเองและสามารถรักษาสถานะของมันได้โดยอัตโนมัติ
เครื่องกำเนิดไฟฟ้าเป็นฟังก์ชั่นพิเศษที่สามารถใช้เป็นโรงงานวนซ้ำ หากฟังก์ชั่นมีนิพจน์ผลผลิตอย่างน้อยหนึ่งรายการจะเรียกว่าเครื่องกำเนิดไฟฟ้า (หมายเหตุของนักแปล: node.js จะต้องแสดงด้วย * ก่อนชื่อฟังก์ชั่น)
หมายเหตุ: คำหลักที่ให้ผลตอบแทนสามารถใช้สำหรับบล็อกโค้ดใน HTML ที่รวมอยู่ใน <script type = "application/javascript; เวอร์ชัน = 1.7"> (หรือใหม่กว่า) แท็กสคริปต์ XUL (XML User Interface) ไม่จำเป็นต้องระบุบล็อกรหัสพิเศษนี้เพื่อเข้าถึงคุณสมบัติเหล่านี้
เมื่อมีการเรียกฟังก์ชั่นเครื่องกำเนิดไฟฟ้าร่างกายฟังก์ชั่นจะไม่ถูกเรียกใช้งานทันทีมันจะส่งคืนวัตถุเครื่องกำเนิดไฟฟ้า ทุกครั้งที่มีการเรียกใช้วิธีการต่อไป () ของเครื่องกำเนิดไฟฟ้า-เทอร์เทอเรเตอร์ฟังก์ชั่นจะดำเนินการกับนิพจน์ผลผลิตถัดไปจากนั้นส่งคืนผลลัพธ์ เมื่อฟังก์ชั่นจบลงหรือเผชิญหน้ากับคำสั่ง Return จะมีการโยนข้อยกเว้นการหยุดยั้ง
ใช้ตัวอย่างเพื่ออธิบายได้ดีขึ้น:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น SimpleGenerator () {
ให้ผลผลิต "First";
ให้ผลผลิต "วินาที";
ให้ผลผลิต "สาม";
สำหรับ (var i = 0; i <3; i ++)
ให้ฉัน;
-
var g = simplegenerator ();
พิมพ์ (G.Next ()); // เอาต์พุต "First"
พิมพ์ (G.Next ()); // เอาต์พุต "สอง"
พิมพ์ (G.Next ()); // เอาต์พุต "สาม"
พิมพ์ (G.Next ()); // เอาท์พุท 0
พิมพ์ (G.Next ()); // เอาท์พุท 1
พิมพ์ (G.Next ()); // เอาท์พุท 2
พิมพ์ (G.Next ()); // โยนข้อยกเว้นการหยุดยั้ง
ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าสามารถใช้โดยตรงโดยคลาสเป็นวิธี __iterator__ และสามารถลดปริมาณรหัสที่จำเป็นต้องใช้ตัววนซ้ำที่กำหนดเองได้อย่างมีประสิทธิภาพ มาเขียนช่วงใหม่โดยใช้เครื่องกำเนิด:
การคัดลอกรหัสมีดังนี้:
ช่วงฟังก์ชัน (ต่ำ, สูง) {
this.low = ต่ำ;
this.high = สูง;
-
range.prototype .__ iterator__ = function () {
สำหรับ (var i = this.low; i <= this.high; i ++)
ให้ฉัน;
-
ช่วง var = ช่วงใหม่ (3, 5);
สำหรับ (var i อยู่ในช่วง)
พิมพ์ (i); // เอาท์พุท 3 จากนั้น 4 จากนั้น 5
ไม่ใช่เครื่องกำเนิดไฟฟ้าทุกตัวที่จะยุติคุณสามารถสร้างเครื่องกำเนิดไฟฟ้าที่แสดงถึงลำดับที่ไม่มีที่สิ้นสุด เครื่องกำเนิดไฟฟ้าต่อไปนี้ใช้ลำดับ Fibonacci ซึ่งแต่ละองค์ประกอบคือผลรวมของสองคนแรก:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น fibonacci () {
var fn1 = 1;
var fn2 = 1;
ในขณะที่ (1) {
var current = fn2;
FN2 = FN1;
FN1 = FN1 + ปัจจุบัน;
กระแสผลผลิต;
-
-
ลำดับ var = fibonacci ();
พิมพ์ (sequence.next ()); // 1
พิมพ์ (sequence.next ()); // 1
พิมพ์ (sequence.next ()); // 2
พิมพ์ (sequence.next ()); // 3
พิมพ์ (sequence.next ()); // 5
พิมพ์ (sequence.next ()); // 8
พิมพ์ (sequence.next ()); // 13
ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าสามารถใช้พารามิเตอร์และจะใช้พารามิเตอร์เหล่านี้เมื่อเรียกใช้ฟังก์ชันเป็นครั้งแรก เครื่องกำเนิดไฟฟ้าสามารถยกเลิกได้ (ทำให้มันมีข้อยกเว้นการหยุดยั้ง) โดยใช้คำสั่ง Return ตัวแปร Fibonacci () ต่อไปนี้ใช้พารามิเตอร์ขีด จำกัด ที่เป็นตัวเลือกซึ่งจะสิ้นสุดฟังก์ชั่นเมื่อมีการเรียกเงื่อนไข
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น fibonacci (ขีด จำกัด ) {
var fn1 = 1;
var fn2 = 1;
ในขณะที่ (1) {
var current = fn2;
FN2 = FN1;
FN1 = FN1 + ปัจจุบัน;
ถ้า (จำกัด && ปัจจุบัน> ขีด จำกัด )
กลับ;
กระแสผลผลิต;
-
-
คุณสมบัติขั้นสูงของเครื่องกำเนิดไฟฟ้า
เครื่องกำเนิดไฟฟ้าสามารถคำนวณค่าผลตอบแทนผลตอบแทนตามข้อกำหนดซึ่งทำให้เป็นตัวแทนของข้อกำหนดการคำนวณลำดับที่มีราคาแพงก่อนหน้านี้แม้กระทั่งลำดับที่ไม่มีที่สิ้นสุดที่แสดงด้านบน
นอกเหนือจากวิธีการถัดไป () วัตถุเครื่องกำเนิดไฟฟ้า-เทอร์เรเตอร์ยังมีวิธีการส่ง () ซึ่งสามารถแก้ไขสถานะภายในของเครื่องกำเนิดไฟฟ้าได้ ค่าที่ส่งผ่านไปยังการส่ง () จะได้รับการปฏิบัติเป็นผลมาจากการแสดงออกของผลผลิตสุดท้ายและเครื่องกำเนิดไฟฟ้าจะถูกหยุดชั่วคราว ก่อนที่คุณจะผ่านค่าที่ระบุโดยใช้เมธอด Send () คุณต้องโทรไปที่ Next () อย่างน้อยหนึ่งครั้งเพื่อเริ่มต้นเครื่องกำเนิด
เครื่องกำเนิดฟีโบนักชีต่อไปนี้ใช้วิธีการส่ง () เพื่อรีสตาร์ทลำดับ:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น fibonacci () {
var fn1 = 1;
var fn2 = 1;
ในขณะที่ (1) {
var current = fn2;
FN2 = FN1;
FN1 = FN1 + ปัจจุบัน;
VAR RESET = กระแสผลผลิต;
ถ้า (รีเซ็ต) {
FN1 = 1;
FN2 = 1;
-
-
-
ลำดับ var = fibonacci ();
พิมพ์ (sequence.next ()); // 1
พิมพ์ (sequence.next ()); // 1
พิมพ์ (sequence.next ()); // 2
พิมพ์ (sequence.next ()); // 3
พิมพ์ (sequence.next ()); // 5
พิมพ์ (sequence.next ()); // 8
พิมพ์ (sequence.next ()); // 13
พิมพ์ (sequence.send (จริง)); // 1
พิมพ์ (sequence.next ()); // 1
พิมพ์ (sequence.next ()); // 2
พิมพ์ (sequence.next ()); // 3
หมายเหตุ: ที่น่าสนใจการโทรส่ง (ไม่ได้กำหนด) นั้นเหมือนกับการโทรถัดไป () อย่างไรก็ตามเมื่อมีการเรียกวิธีการส่ง () เพื่อเริ่มต้นเครื่องกำเนิดใหม่ข้อยกเว้น TypeError จะถูกโยนทิ้งยกเว้นที่ไม่ได้กำหนด
คุณสามารถเรียกวิธีการโยนและผ่านค่าผิดปกติที่ควรโยนเพื่อบังคับให้เครื่องกำเนิดไฟฟ้าเพื่อโยนข้อยกเว้น ข้อยกเว้นนี้จะถูกโยนออกจากบริบทปัจจุบันและหยุดเครื่องกำเนิดไฟฟ้าใกล้กับการดำเนินการให้ผลผลิตปัจจุบัน แต่แทนที่ด้วยคำสั่งค่าการโยน
หากไม่พบผลผลิตในระหว่างกระบวนการโยนข้อยกเว้นข้อยกเว้นจะถูกส่งผ่านจนกว่าจะมีการเรียกวิธีการโยน () และต่อมาเรียกต่อไป () จะทำให้เกิดข้อยกเว้นการหยุดยั้ง
เครื่องกำเนิดไฟฟ้ามีวิธีการปิด () เพื่อบังคับให้เครื่องกำเนิดไฟฟ้าสิ้นสุด การสิ้นสุดเครื่องกำเนิดไฟฟ้าจะมีเอฟเฟกต์ดังต่อไปนี้:
1. ประโยคทั้งหมดที่ถูกต้องในที่สุดในเครื่องกำเนิดจะถูกดำเนินการ
2. หากคำในที่สุดพ่นข้อยกเว้นใด ๆ ยกเว้นการหยุดยั้งข้อยกเว้นจะถูกส่งผ่านไปยังผู้โทรของวิธีการปิด ()
3. เครื่องกำเนิดไฟฟ้าจะสิ้นสุด
นิพจน์เครื่องกำเนิดไฟฟ้า
ข้อเสียอย่างหนึ่งที่ชัดเจนของการได้มาของอาร์เรย์คือทำให้อาร์เรย์ทั้งหมดถูกสร้างขึ้นในหน่วยความจำ ค่าใช้จ่ายของการป้อนข้อมูลไปยังที่ได้มานั้นไม่มีนัยสำคัญเมื่อค่าใช้จ่ายของมันเป็นอาร์เรย์ขนาดเล็ก - อย่างไรก็ตามปัญหาอาจเกิดขึ้นได้เมื่ออาร์เรย์อินพุตมีขนาดใหญ่หรือเมื่อสร้างเครื่องกำเนิดอาร์เรย์ราคาแพง (หรือไม่มีที่สิ้นสุด) ใหม่
เครื่องกำเนิดไฟฟ้าช่วยให้การคำนวณขี้เกียจคำนวณองค์ประกอบตามต้องการเมื่อจำเป็น นิพจน์เครื่องกำเนิดไฟฟ้าเกือบจะเหมือนกับการหาอาร์เรย์ - มันใช้วงเล็บแทนวงเล็บสี่เหลี่ยม (และใช้สำหรับ ... ในแทนแต่ละ ... ใน) - แต่มันสร้างเครื่องกำเนิดไฟฟ้าแทนอาร์เรย์เพื่อให้การคำนวณล่าช้า คุณสามารถคิดว่ามันเป็นไวยากรณ์สั้น ๆ สำหรับการสร้างเครื่องกำเนิดไฟฟ้า
สมมติว่าเรามีตัววนซ้ำมันจะวนซ้ำมากกว่าจำนวนเต็มขนาดใหญ่ เราจำเป็นต้องสร้างตัววนซ้ำใหม่เพื่อทำซ้ำมากกว่าตัวเลข การหาอาร์เรย์จะสร้างอาร์เรย์ทั้งหมดที่มีตัวเลขทั้งหมดในหน่วยความจำ:
การคัดลอกรหัสมีดังนี้:
var -doubles = [i * 2 สำหรับ (i ในนั้น)];
นิพจน์เครื่องกำเนิดไฟฟ้าจะสร้างตัววนซ้ำใหม่และคำนวณค่าสม่ำเสมอตามต้องการเมื่อจำเป็น:
การคัดลอกรหัสมีดังนี้:
var it2 = (i * 2 สำหรับ (i ในนั้น));
พิมพ์ (it2.next ()); // หมายเลขแรกคู่ในนั้น
พิมพ์ (it2.next ()); // หมายเลขคู่ที่สองในนั้น
เมื่อใช้เครื่องกำเนิดไฟฟ้าเป็นพารามิเตอร์ของฟังก์ชั่นจะใช้วงเล็บเป็นการเรียกใช้ฟังก์ชันซึ่งหมายความว่าสามารถละเว้นวงเล็บด้านนอกสุดได้:
การคัดลอกรหัสมีดังนี้:
var result = dosomething (i * 2 สำหรับ (i ในนั้น));
จบ.