
מבוא ללולאות for בג'אווה סקריפט
לולאות for (באנגלית: for loops) הן מנגנון בסיסי של JavaScript, המאפשר לבצע מקטע (בלוק) קוד מספר פעמים. הן שימושיות להפליא עבור מעבר סידרתי על מערכים, אובייקטים ומבנים אחרים שניתנים לחזרה או ביצוע משימות חוזרות ביעילות.
בשפת JavaScript קיימים כמה סוגי לולאות. נכיר תחילה את לולאת for הסטנדרטית, שהיא גם הלולאה הנפוצה ביותר בקוד JavaScript.
לולאת for הסטנדרטית מורכבת משלושה חלקים: אתחול (initialization), תנאי (condition) והגדלה (increment).
להלן התחביר הבסיסי של הלולאה:
for (initialization; condition; increment) {
// Code to be executed
}
שלבי ביצוע הלולאה הם אלו:
- ביצוע האתחול
- בדיקת התנאי: אם הוא מתקיים, ממשיכים לשלב הבא, אחרת, הלולאה מסתיימת
- ביצוע בלוק הקוד (בין הסוגריים המסולסלים)
- ביצוע ההגדלה
- חזרה לשלב 2
כל מעבר סדרתי על הלולאה (ביצוע שלב 3) מכונה "איטרציה".
המשתנה שסופר את כמות האיטרציות מכונה "אינדקס". נהוג לקבוע אינדקס באות i (מהמילה index), אך אין חובה לכך. ברוב המקרים, האינדקס משמש רק לצורך האיטרציות בלולאה ולאחר סיום הלולאה אין בו שימוש, ולכן, נגדיר אותו באמצעות המילה השמורה let ולא var, על מנת לקבוע שהסקופ (התחום בקוד שבו המשתנה זמין) הוא בתוך הלולאה בלבד.
דוגמה:
for (let i = 0; i < 5; i++) {
console.log(i);
}
// Output:
// 0
// 1
// 2
// 3
// 4
נסביר את הדוגמה. האתחול כולל הגדרה של המשתנה i (האינדקס) ואתחול שלו לערך 0. תנאי הלולאה בודק אם מתקיים i < 5. בשלב ההגדלה אנו מגדילים את המשתנה i ב-1. תהליך ביצוע הלולאה הוא כזה: תחילה, מבצעים את האתחול i = 0. לאחר מכן, מבצעים את בלוק הקוד ומדפיסים את הערך של i בקונסול, כלומר, מודפס הערך 0. לאחר מכן, בודקים אם i < 5. כיוון שהתנאי מתקיים, שהרי 0 קטן מ-5, ממשיכים לשלב הבא ומגדילים את i ב-1. כעת i = 1 ומבצעים שוב את בלוק הקוד, כלומר, מדפיסים בקונסול את הערך 1. לאחר מכן, שוב בודקים אם i < 5. גם הפעם התנאי מתקיים, כיוון ש-1 קטן מ-5, ולכן ממשיכים לשלב הבא ומגדילים את i שוב ב-1. כך ממשיכים עד שמודפס על המסך הערך 5, וכאשר בודקים את התנאי i < 5, התנאי אינו מתקיים, שהרי 5 לא קטן מ-5 (הוא שווה לו), והלולאה מסתיימת.
אין צורך בכל פעם שמתכננים לולאה או כשקוראים קוד של לולאה, לחשוב על כל השלבים. החשיבה צריכה להיות פשוטה יותר: מגדירים i = 0 והלולאה רצה עד ש-i מגיע ל-5 אבל לא כולל 5, כלומר, היא רצה מ-0 עד 4, ובסה"כ 5 פעמים. אפשר פשוט לזכור שאם מאתחלים את i ל-0 והתנאי הוא i < n, אז הלולאה רצה n פעמים, כלומר מבוצעות n איטרציות.
כמה הערות:
- שלב האתחול מתבצע פעם אחת בלבד, מיד עם התחלת הלולאה.
- התנאי נבדק תמיד לפני ביצוע בלוק הקוד. לכן, אם התנאי לא מתקיים בכלל, יתכן שהלולאה לא תרוץ אפילו פעם אחת.
לולאות for לא שגרתיות בג'אווה סקריפט
החלקים אתחול, תנאי והגדלה, יכולים לכלול כל קטע קוד שרוצים. אפשר להשמיט אותם לגמרי, ואפשר לכלול בהם יותר מפקודה אחת.
דוגמה:
let i = 10; // Unusual initialization
for (; i > 0;) { // Unusual condition
console.log(i);
i -= 2; // Unusual decrement
}
// Output:
// 10
// 8
// 6
// 4
// 2
בדוגמה זו, לולאת ה-for אינה כוללת שלב אתחול (האתחול נעשה לפני הלולאה) ואינה כוללת שלב הגדלה (ההגדלה נעשית בתוך הלולאה עצמה). הלולאה רצה 5 פעמים, כיוון שבהתחלה i = 10 ואנו מקטינים את i כל פעם ב-2, כל עוד מתקיים i > 0.
דוגמה נוספת:
for (let i = 0, j = 10; i < j; console.log('Incrementing'), i++, j -= 2) {
console.log(i, j);
}
// Output:
// Incrementing
// 0 10
// Incrementing
// 1 8
// Incrementing
// 2 6
// Incrementing
// 3 4
בדוגמה זו, האתחול כולל 2 אתחולים (i =0 וגם j = 10), התנאי מורכב יותר (i < j) וההגדלה כוללת גם קטע קוד שמדפיס בקונסול את המילה Incrementing, גם הגדלה של המשתנה i ב-1 וגם הקטנה של המשתנה j ב-2. זו לולאה מאוד לא שגרתית, והמצאנו אותה בעיקר בשביל הדוגמה. עם זאת, לא נמליץ לכתוב קוד בסגנון כזה, כיוון שהוא לא סטנדרטי, לא קריא ומקשה מאוד על התמיכה והטיפול בו.
לולאת for .. in
זוהי גירסת מיוחדת ללולאת for, אשר עושה איטרציות למאפיינים של אובייקט.
להלן התחביר הבסיסי של הלולאה:
for (variable in object) {
// Code to be executed
}
דוגמה:
const person = {
name: 'John',
age: 30,
gender: 'male'
};
for (let key in person) {
console.log(`${key}: ${person[key]}`);
}
// Output:
// name: John
// age: 30
// gender: male
בדוגמה זו, אנו עושים מעבר סדרתי על המאפיינים של המבנה person. בכל איטרציה, המשתנה key מקבל את ערך המפתח הבא ב-person.
לולאת for .. of
זוהי גירסת מיוחדת נוספת ללולאת for, אשר עושה איטרציות לערכי האלמנטים של אובייקט.
להלן התחביר הבסיסי של הלולאה:
for (variable of iterable) {
// Code to be executed
}
דוגמה:
const colors = ['red', 'green', 'blue'];
for (let color of colors) {
console.log(color);
}
// Output:
// red
// green
// blue
בדוגמה זו, אנו עושים מעבר סדרתי על אברי האובייקט colors. בכל איטרציה, המשתנה color מקבל את ערך האיבר הבא באובייקט.
נשים לב להבדלים בין לולאת for in ללולאת for of. בשני המקרים עוברים בצורה סדרתית על איברים של אובייקט נתון. עם זאת, המשתנה שמוגדר בלולאה משחק תפקיד שונה בשני המקרים. בלולאת for in, המשתנה מקבל את המפתחות של האיברים, ואילו בלולאת for of, המשתנה מקבל את הערכים של האיברים. לולאת for in מתאימה יותר לאובייקטים שהם מבנים בסגנון key-value.
לולאות for מקוננות
שימוש נפוץ ללולאות הוא קינון לולאות. לולאות מקוננות הן למעשה לולאות בתוך לולאות. הדבר משמש למקרים בהם רוצים לעשות מעבר סדרתי על מערכים רב מימדיים.
כאשר מקננים לולאות for סטנדרטיות, נהוג להשתמש במשתנה i כאינדקס הלולאה החיצונית ביותר, במשתנה j כאינדקס הלולאה הפנימית יותר, במשתנה k כאינדקס הלולאה הפנימית עוד יותר (במקרה שיש קינון של 3 רמות) וכן הלאה.
דוגמה:
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(`Element at index [${i}][${j}]:`, matrix[i][j]);
}
}
בדוגמה זו, התחלנו עם הגדרת המשתנה matrix שהוא מערך דו מימדי. למשתנה זה יש 3 איברים, שכל אחד מהם הוא בעצמו מערך בן 3 איברים מספריים. לאחר הגדרת המשתנה, אנו רואים 2 לולאות מקוננות. הלולאה החיצונית היא עם האינדקס i והיא רצה matrix.length פעמים, כלומר, 3 פעמים ככמות האיברים של המשתנה matrix. הלולאה הפנימית היא עם האינדקס j והיא רצה בכל איטרציה של הלולאה החיצונית matrix[i].length פעמים, כלומר, 3 פעמים ככמות האיברים של המערך matrix[i]. למעשה, הלולאה החיצונית רצה בסה"כ 3 פעמים ואילו הפנימית רצה בסה"כ 9 פעמים.
הדגמנו קינון בעומק 2 (כלומר 2 לולאות בסה"כ, אחת בתוך השניה) של לולאות for סטנדרטיות. עם זאת, ניתן לבצע קינון גם של לולאות for in או for of, לבצע קינון המשלב בין סוגי לולאות שונים ולבצע קינון באיזה עומק שרוצים.
שימוש בהצהרות break ו- continue בלולאות for
הצהרת break יוצאת מהלולאה מיד.
דוגמה:
for (let i = 0; i < 10; i++) {
if (i === 5) {
break;
}
console.log(i);
}
// Output:
// 0
// 1
// 2
// 3
// 4
בדוגמה זו, הלולאה מוגדרת לרוץ 10 פעמים (החל מערך i = 0 ועד i = 9). עם זאת, ברגע שמתקיים התנאי i === 5, כלומר המשתנה i זהה ממש ל-5 (גם מבחינת הטיפוס שהוא מספרי וגם מבחינת הערך עצמו 5), אז יוצאים מהלולאה.
דוגמה זו היא רק לצורך ההבנה. בדרך כלל, התנאי שבגללו יוצאים מהלולאה החוצה אינו תלוי באינדקס של הלולאה, אלא במצב כלשהו אחר, שהרי אחרת פשוט היינו מפעילים את הלולאה 5 פעמים במקום 10 והשימוש ב-break היה מתייתר.
הצהרת continue מסיימת את האיטרציה הנוכחית וממשיכה לאיטרציה הבאה.
דוגמה:
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) {
continue;
}
console.log(i);
}
// Output:
// 1
// 3
// 5
// 7
// 9
בדוגמה זו, הלולאה מוגדרת לרוץ 10 פעמים (החל מערך i = 0 ועד i = 9). עם זאת, ברגע שמתקיים התנאי i % 2 === 0, כלומר i מתחלק ב-2 ללא שארית, מדלגים מיד לאיטרציה הבאה.
הצהרה על לולאת for עם תווית
ניתן לקבוע תווית (label) לבלוקים שונים ב-JavaScript. התווית מקנה "שם" לאותו בלוק, כדי שניתן יהיה להתייחס אליו.
השימוש הנפוץ ביותר לתוויות, הוא במקרה של לולאות מקוננות עם הצהרות break או continue. כאשר יש לנו לולאה מקוננת ובתוכה break או continue, ההתייחסות היא ללולאה הפנימית ביותר. אם נרצה שההתייחסות תהיה ללולאה ברמה חיצונית יותר, נוכל להעזר בתוויות.
דוגמה:
outerLoop: // Label for the outer loop
for (let i = 0; i < 3; i++) {
console.log(`Outer loop iteration: ${i}`);
innerLoop: // Label for the inner loop
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
console.log('Breaking out of outer loop');
break outerLoop; // Break out of the outer loop
}
console.log(`Inner loop iteration: ${j}`);
}
}
// Output:
// Outer loop iteration: 0
// Inner loop iteration: 0
// Inner loop iteration: 1
// Inner loop iteration: 2
// Outer loop iteration: 1
// Inner loop iteration: 0
// Breaking out of outer loop
בדוגמה זו, הדבקנו את התווית outerLoop ללולאה החיצונית ואת התווית innerLoop ללולאה הפנימית. ברגע שמתקיים התנאי i === 1 && j === 1 מבוצעת שורת הקוד break outerLoop. הצהרה זו מבצעת יציאה מהלולאה החיצונית (שמתוייגת בשם outerLoop), ולמעשה ביצוע הקוד של הלולאות כולו מסתיים. אם במקום break outerLoop היינו רושמים break innerLoop, או אפילו סתם break, אז היינו יוצאים מהלולאה הפנימית (שזו ברירת המחדל) ולמעשה ממשיכים באיטרציה הבאה של הלולאה החיצונית.
נראה עוד דוגמה, הפעם עם continue:
outerLoop: // Label for the outer loop
for (let i = 0; i < 3; i++) {
console.log(`Outer loop iteration: ${i}`);
innerLoop: // Label for the inner loop
for (let j = 0; j < 3; j++) {
if (j === 1) {
console.log('Continuing outer loop');
continue outerLoop; // Continue the outer loop
}
console.log(`Inner loop iteration: ${j}`);
}
}
// Output:
// Outer loop iteration: 0
// Inner loop iteration: 0
// Continuing outer loop
// Outer loop iteration: 1
// Inner loop iteration: 0
// Continuing outer loop
// Outer loop iteration: 2
// Inner loop iteration: 0
// Continuing outer loop
בדוגמה זו, ביצוע continue outerLoop מבצע דילוג לאיטרציה הבאה של הלולאה החיצונית outerLoop. החלפה של ההצהרה בהצהרת continue פשוטה, תבצע דילוג לאיטרציה הבאה של הלולאה הפנימית.
טיפים לשימוש בתוויות:
- קריאות - השתמש בהצהרות מתויגות במשורה, כדי למנוע עומס בקוד. שימוש יתר בתוויות יכול להפוך את הקוד לקשה יותר לקריאה ולתחזוקה.
- תוויות תיאוריות - בחר שמות תיאוריים עבור התוויות שלך, כדי שהקוד יהיה מובן יותר.
- גישות אלטרנטיביות - שקול לשנות את הקוד שלך לשימוש בפונקציות או השתמש במנגנוני זרימת בקרה אחרים אם אתה מוצא את עצמך זקוק לתוויות לעתים קרובות מדי.
באופן כללי, אנו ממליצים שלא להשתמש בתוויות כלל, אלא אם באמת אין ברירה וזו הדרך הנקיה והקריאה ביותר לבצע את הקוד.
טיפים לשימוש נכון בלולאות for בג'אווה סקריפט
להלן מספר טיפים שיעזרו לשפר את קריאות הקוד ואת מהירות הביצוע שלו.
- ביצועים - צמצם את הפעולות בתוך הלולאות, כדי לשפר את הביצועים. לדוגמה, חשב את אורך המערך עליו רצים לפני הלולאה ולא בחלק התנאי של הלולאה.
- קריאה - השתמש בשמות משתנים משמעותיים ושמור על גוף הלולאה תמציתי.
- לולאות אינסופיות - ודא שללולאה יש תנאי סיום חוקי, כדי למנוע לולאות אינסופיות, שעלולות לגרום לתוכנית לקרוס.
נבחן לולאה נפוצה ונראה איך ניתן לשפר אותה.
for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}
ניתן לראות כי הלולאה רצה array.length איטרציות. בכל סוף איטרציה, בודקים את התנאי i < array.length, דבר שדורש את חישוב הערך array.length בכל סוף איטרציה מחדש.
נוכל לשפר את הלולאה באופן הבא:
for (let i = 0, len = array.length; i < len; i++) {
console.log(array[i]);
}
בדרך זו, חישבנו את גודל המערך פעם אחת, בשלב האתחול. המשתנה len הוגדר להיות גודל המערך, ובכל איטרציה בודקים האם i < len. ככה נחסך הצורך לחשב מחדש את גודל המערך בכל איטרציה.