From 1109132f09d75da9a28b649c7677bb6ce07c40c0 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:41:45 -0500 Subject: initial commit --- .../javascript/asynchronous/concepts/index.html | 203 +++++ files/he/learn/javascript/asynchronous/index.html | 43 + .../javascript/asynchronous/introducing/index.html | 281 +++++++ .../javascript/asynchronous/promises/index.html | 589 +++++++++++++ .../asynchronous/timeouts_and_intervals/index.html | 651 ++++++++++++++ .../build_your_own_function/index.html | 247 ++++++ .../javascript/building_blocks/events/index.html | 588 +++++++++++++ .../building_blocks/functions/index.html | 386 +++++++++ .../he/learn/javascript/building_blocks/index.html | 51 ++ .../building_blocks/looping_code/index.html | 931 +++++++++++++++++++++ .../building_blocks/return_values/index.html | 180 ++++ .../index.html" | 789 +++++++++++++++++ .../first_steps/a_first_splash/index.html | 634 ++++++++++++++ .../learn/javascript/first_steps/arrays/index.html | 564 +++++++++++++ files/he/learn/javascript/first_steps/index.html | 64 ++ .../learn/javascript/first_steps/math/index.html | 427 ++++++++++ .../first_steps/silly_story_generator/index.html | 139 +++ .../javascript/first_steps/strings/index.html | 172 ++++ .../first_steps/useful_string_methods/index.html | 673 +++++++++++++++ .../javascript/first_steps/variables/index.html | 333 ++++++++ .../first_steps/what_is_javascript/index.html | 447 ++++++++++ .../first_steps/what_went_wrong/index.html | 251 ++++++ files/he/learn/javascript/index.html | 83 ++ .../adding_bouncing_balls_features/index.html | 214 +++++ .../he/learn/javascript/objects/basics/index.html | 257 ++++++ files/he/learn/javascript/objects/index.html | 54 ++ .../javascript/objects/inheritance/index.html | 437 ++++++++++ files/he/learn/javascript/objects/json/index.html | 346 ++++++++ .../objects/object-oriented_js/index.html | 275 ++++++ .../objects/object_prototypes/index.html | 283 +++++++ 30 files changed, 10592 insertions(+) create mode 100644 files/he/learn/javascript/asynchronous/concepts/index.html create mode 100644 files/he/learn/javascript/asynchronous/index.html create mode 100644 files/he/learn/javascript/asynchronous/introducing/index.html create mode 100644 files/he/learn/javascript/asynchronous/promises/index.html create mode 100644 files/he/learn/javascript/asynchronous/timeouts_and_intervals/index.html create mode 100644 files/he/learn/javascript/building_blocks/build_your_own_function/index.html create mode 100644 files/he/learn/javascript/building_blocks/events/index.html create mode 100644 files/he/learn/javascript/building_blocks/functions/index.html create mode 100644 files/he/learn/javascript/building_blocks/index.html create mode 100644 files/he/learn/javascript/building_blocks/looping_code/index.html create mode 100644 files/he/learn/javascript/building_blocks/return_values/index.html create mode 100644 "files/he/learn/javascript/building_blocks/\327\252\327\240\327\220\327\231\327\235/index.html" create mode 100644 files/he/learn/javascript/first_steps/a_first_splash/index.html create mode 100644 files/he/learn/javascript/first_steps/arrays/index.html create mode 100644 files/he/learn/javascript/first_steps/index.html create mode 100644 files/he/learn/javascript/first_steps/math/index.html create mode 100644 files/he/learn/javascript/first_steps/silly_story_generator/index.html create mode 100644 files/he/learn/javascript/first_steps/strings/index.html create mode 100644 files/he/learn/javascript/first_steps/useful_string_methods/index.html create mode 100644 files/he/learn/javascript/first_steps/variables/index.html create mode 100644 files/he/learn/javascript/first_steps/what_is_javascript/index.html create mode 100644 files/he/learn/javascript/first_steps/what_went_wrong/index.html create mode 100644 files/he/learn/javascript/index.html create mode 100644 files/he/learn/javascript/objects/adding_bouncing_balls_features/index.html create mode 100644 files/he/learn/javascript/objects/basics/index.html create mode 100644 files/he/learn/javascript/objects/index.html create mode 100644 files/he/learn/javascript/objects/inheritance/index.html create mode 100644 files/he/learn/javascript/objects/json/index.html create mode 100644 files/he/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/he/learn/javascript/objects/object_prototypes/index.html (limited to 'files/he/learn/javascript') diff --git a/files/he/learn/javascript/asynchronous/concepts/index.html b/files/he/learn/javascript/asynchronous/concepts/index.html new file mode 100644 index 0000000000..273c184474 --- /dev/null +++ b/files/he/learn/javascript/asynchronous/concepts/index.html @@ -0,0 +1,203 @@ +--- +title: עקרונות תכנות א-סינכרוני כלליים +slug: Learn/JavaScript/Asynchronous/Concepts +translation_of: Learn/JavaScript/Asynchronous/Concepts +--- +
{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}
+ +

+ במאמר זה אנחנו נעבור על מספר עקרונות חשובים בנושא תכנות א-סינכרוני וכיצד זה נראה בדפדפנים וב-JavaScript. חשוב להבין עקרונות אלו לפני שממשיכים למאמרים הבאים במודול זה. +

+ + + + + + + + + + + + +
ידע מוקדם:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
מטרה: + הבנה של העקרונות הבסיסיים מאחורי תכנות א-סינכרוני וכיצד אלו באים לידי ביטוי בדפדפנים וב-JavaScript.
+ +

Asynchronous - א-סינכרוני?

+ +

+ באופן נורמלי, קוד של תוכנית רץ בצורה עצמאית, כאשר רק דבר אחד קורה בכל רגע נתון. אם פונקציה מסויימת נשנעת על התוצאה של פונקציה אחרת, היא חייבית לחכות לפונקציה האחרת לסיים ולהחזיר את התוצאה ועד שזה לא קורה, כל התוכנית עוצרת מנקודת מבטו של המשתמש.

+ +

Mac אלו המשתמשים ב-, לדוגמא, + לפעמים רואים עיגול צבעוני... עיגול זה בעצם אומר למשתמש ״התוכנית הנוכחית שאתה מתמש בה הייתה צריכה לעצור ולחכות שמשהו הסתיים, וזה לוקח זמן..״

+ +

multi-colored macos beachball busy spinner

+ +

+ זו חווית שימוש מעצבנת, ולא ממש שימוש יעיל ביכולות המחשב, במיוחד בעידן שלמחשבים יש מספר ליבות מעבד זמינות. אין שום סיבה שאנחנו נחכה למשהו כשאנחנו יכולים להמשיך במקביל בעבודה שלנו בזמן שהמחשב עובד על אותה פעולה ויעדכן אותנו כשהפעולה הסתיימה. זה מאפשר לנו לבצע פעולות אחרות במקביל, וזה בעיקרון הבסיס של תכנות א-סינכרוני - asynchronous programming. זה תלוי בסביבת הפיתוח שבה אנחנו משתמשים (דפדני אינטרנט במקרה שלנו) להעניק למפתח APIs שיאפשרו לנו להריץ משימות באופן א-סינכרוני + + +

+ +

Blocking code

+ +

+ טכניקות א-סינכרוניות הן שימושיות מאוד, במיוחד בפיתוח web. כאשר יישום web רץ בדפדפן ומריץ שורות קוד מבלי להחזיר את השליטה לדפדפן, הדפדפן יכול להיראות כאילו הוא ״קפא״. זה נקרא blocking. הדפדפן חסום מלהמשיך ולטפל בפעולות מצד המשתמש ולבצע פעולות נוספות עד אשר יישום ה-web מחזיר את השליטה למעבד.

+ +

+ נסתכל על מספר דוגמאות על מנת להסביר למה אנחנו מתכוונים ב- blocking + +

+ +

ב simple-sync.html דוגמא שלנו (או כ דף אינטרנט), + + הוספנו מאזין אירוע מסוג click לכפתור כך שבכל פעם שהכפתור יילחץ, הוא יריץ פעולה שלוקחת המון זמן (מחשבת 10 מיליון תאריכים ואז מחזירה את האחרון לקונסולה) ולאחר מכן מוסיפה פסקה ל-DOM:

+ +
const btn = document.querySelector('button');
+btn.addEventListener('click', () => {
+  let myDate;
+  for(let i = 0; i < 10000000; i++) {
+    let date = new Date();
+    myDate = date
+  }
+
+  console.log(myDate);
+
+  let pElem = document.createElement('p');
+  pElem.textContent = 'This is a newly-added paragraph.';
+  document.body.appendChild(pElem);
+});
+ +

+ כשאתם מריצים את הדוגמא למלעלה, פתחו את הקונסולה ואז לחצו על הכפתור - אתם תשימו לב שהפסקה אינה מופיעה עד אשר חישוב התאיריכים הסתיים והוצגה ההודעה לקונסולה. הקוד רץ בסדר שבה הוא מופיע בקוד המקור והפעולה האחרונה לא תרוץ עד אשר הפעולה הקודמת לא סיימה.

+ +
+

לתשומת לב: הדוגמא הקודמת דיי לא מציאותית. אנחנו לעול לא נחשב 10 מיליון תאריכים בתוכנית אמיתית. דוגמא זו נועדה לתת לכם רק את ההבנה של תכנות א-סינכרוני.

+
+ +

בדוגמא השנייה שלנו, simple-sync-ui-blocking.html (ראו כדף אינטרנט), + אנחנו מנסים לדמות משהו קצת יותר ריאלי שאתם אולי תיתקלו בו בדף אמיתי. אנחנו חוסמים את האינטראקציה של המשתמש עם העיבוד של ממשק המשתמש. בדוגמא זו יש לנו שני כפתורים:

+ + + +
function expensiveOperation() {
+  for(let i = 0; i < 1000000; i++) {
+    ctx.fillStyle = 'rgba(0,0,255, 0.2)';
+    ctx.beginPath();
+    ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false);
+    ctx.fill()
+  }
+}
+
+fillBtn.addEventListener('click', expensiveOperation);
+
+alertBtn.addEventListener('click', () =>
+  alert('You clicked me!')
+);
+ +

+ אם תלחצו עלהכפתור הראשון ואז ישר לאחר מכן על השני אתם תראו שההודעה למשתמש לא מופיע עד אשר העיגולים לא סיימו להיות מעובדים. הפעולה הראשונה חוסמת את ביצוע הפעולה השנייה עד אשר הפעולה הראשונה תסתיים.

+ +
+

לתשומת לב: + זהו אמנם נראה לא טוב, אך זוהי רק דוגמא למה שמפתחים נתקלים בה ביישומים אמיתיים.

+
+ +

+ מדוע זה קורה? התשובה נעוצה בכך ש-JavaScript היא single threaded. בנקודה, אנחנו נרצה להציג בפניכם את הרעיון של threads.

+ +

Threads

+ +

Thread הוא בעיקרון תהליך יחיד שתוכנית יכולה להשתמש בה על מנת להשלים משימות. כל תהליך כזה יכול לבצע משימה אחת בכל פעם:

+ +
Task A --> Task B --> Task C
+ +

+ כל משימה תרוץ באופן טורי, רציף, אך לא סימולטני. משימה צריכה להסתיים כדי שהבאה בתור תחל בריצה.

+ +

+ כפי שאמרנו מקודם, הרבה מחשבים הם בעלי מספר רב של ליבות, כך שהם יכולים לבצע מספר דברים בבת אחת. שפות תכנות שתומכות בליבות מרובות יכולות להתשמש בליבות אלו על מנת לבצע מספר פעולות באופן סימולטני ובמקביל:

+ +
Thread 1: Task A --> Task B
+Thread 2: Task C --> Task D
+ +

JavaScript - single threaded

+ +

JavaScript היא single-threaded. + אפילו אם יש מספר ליבות, אנחנו יכולים רק להריץ משימה על thread יחיד, שנקרא main thread. הדוגמא שלנו מלמעלה מורצת כך:

+ +
Main thread: Render circles to canvas --> Display alert()
+ +

+ לאחר זמן מסויים, JavaScript קיבלה מס׳ כלים על מנת להתמודד עם בעיות אלו. + + Web workers + מאפשר לנו לשלוח חלק מהביצוע של JavaScript ל-thread נפרד שנקרא בשם worker, כך שאנחנו יכולים להריץ קודי JavaScript באופן סימולטני מקביל. בדרך כלל אנחנו נשתמש ב-worker להריץ תהליכי + + allow you to send some of the JavaScript processing off to a separate thread, called a worker, so that you can run multiple JavaScript chunks simultaneously. You'd generally use a worker to run expensive processes off the main thread so that user interaction is not blocked.

+ +
  Main thread: Task A --> Task C
+Worker thread: Expensive task B
+ +

+ כעת, לאחר שאנחנו יודעים זאת, נסתכל על simple-sync-worker.html או כדף אינטרנט, ונפתח את הקונסולה. + בקובץ זה כתבנו מחדש את הדוגמא שלנו לחישוב 10 מיליון תאריכים בworker thread נפרד. כעת כשאנחנו לוחצים על הכפתור, הדפדן מסוכל להציג את הפסקה לפני שחישוב התאיריכים הסתיימו. הפעולה הראשונה כבר לא חוסמת את הפעולה השנייה. + +

+ +

Asynchronous code קוד א-סינכרוני

+ +

Web workers הם מאוד שימושיים, אבל יש להם הגבלות. + אחת מהן היא שהם לא יכולים לגשת ל-{{Glossary("DOM")}} - אנחנו לא יכולים לגרום ל-worker לעשות שום דבר שיכול לעדכן את ממשק המשתמש, את ה-UI. אנחנו לא נוכל לעבד מיליון כדורים כחולים בתוך ה-worker שלנו. הוא בעיקרון יכול רק לעשות מניפולציות מספריות. +

+ +

+ הבעיה השנייה היא שלמרות שהקוד שרץ בתוך ה-000000 לא חוסם, הוא עדיין בסופו של דבר סינכרוני. זה הופך לבעיה כשאר פונקציה מתבססת על התוצאה של חישובים של תהליכים שקדמו לפונקציה. הסתכלו על התרשים הבא:

+ +
Main thread: Task A --> Task B
+ +

+ במקרה זה, משימה A עושה משהו כמו הבאת תמונה מהשרת, ואז משימה B עושה משהו עם התמונה הזו, כגון החלת פילטר. אם אנחנו נתחיל את משימה A ואז ישר נריץ את משימה B , אנחנו נקבל שגיאה כי התמנוה עדיין לא זמינה, כי משימה A לא הסתיימה עדיין.

+ +
  Main thread: Task A --> Task B --> |Task D|
+Worker thread: Task C -----------> |      |
+ +

+ במקרה הזה לדוגמא, יש לנו את משימה D שמבצעת שימוש בתוצאות של משימה B ומשימה C. אם אנחנו יכולים להבטיח שהתוצאות של שתי משימות אלו יהיהו זמינות בואתו הזמן, אז זה יכול להיות תקין, אבל זה בדרך כלל לא קורה. אם משימה D תנסה לרוץ לפני שהערכים שהיא צריכה לקבל זמינים עבורה, זה יחזיר לנו רוב הסיכויים שגיאה.

+ +

+ על מנת לתקן בעיות שכאלו, דפדנים מאפשרים לנו להריץ מס׳ פעולות באופן א-סינכרוני. תכונות כמו Promises מאפשרות לנו לקבוע משימה שתרוץ, כמו לדוגמא הבאת תמונה מהשרת, ולחכות עד אשר התוצאה של אותה משימה תוחזר שנריץ פעולה מסויימת אחרת.

+ +
Main thread: Task A                   Task B
+    Promise:      |__async operation__|
+ +

+ מאחר שהפעולה מתרחשת במקום אחר, ה-main thread לא חסום בזמן שהפעולה הא-סינכרונית מתרחשת. + +

+ +

+ אנחנו נסתכל כיצד אנחנו יכולים לרשום קוד א-סינכרוני במאמר הבא. +

+ +

לסיכום

+ +

+ עיצוב תוכנה מודרני סובב יותר ויותר סביב שימוש בתכנות א-סינכרוני, כדי לאפשר לתוכניות לעשות יותר מדבר אחד בכל פעם. כאשר נשתמש ב-API חדשים ובעלי יכולות מתקדמות, אנחנו נמצא יותר מקרים שבהם הדרך היחידה לבצע משהו מסויים היא רק באופן א-סינכרוני. זה היה דיי קשה בעבר לכתוב קוד א-סינכרוני. אמנם עדיין לוקח זמן להתרגל לכך, אבל זה נעשה הרבה יותר קל. ביתר המאמרים במודול זה אנחנו נסביר מדוע קוד א-סינכרוני הוא רלוונטי וחשוב וכיצד נעצב את הקוד על מנת להימנע מחלק מהבעיות שסקרנו במאמר זה. + + + יותר מדבר אחד בכל פעם. כשאתה משתמש בממשקי API חדשים וחזקים יותר, תמצא מקרים נוספים שבהם הדרך היחידה לעשות דברים היא בצורה אסינכרונית. פעם היה קשה לכתוב קוד אסינכרוני. זה עדיין לוקח להתרגל, אבל זה נעשה הרבה יותר קל. בשאר מודול זה נבדוק יותר מדוע חשוב קוד אסינכרוני וכיצד לתכנן קוד שנמנע מכמה מהבעיות שתוארו לעיל.

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/asynchronous/index.html b/files/he/learn/javascript/asynchronous/index.html new file mode 100644 index 0000000000..01ec7babb7 --- /dev/null +++ b/files/he/learn/javascript/asynchronous/index.html @@ -0,0 +1,43 @@ +--- +title: Asynchronous JavaScript +slug: Learn/JavaScript/Asynchronous +translation_of: Learn/JavaScript/Asynchronous +--- +
{{LearnSidebar}}
+ +

במודות זה אנחנו נסתכל על {{Glossary("asynchronous")}} {{Glossary("JavaScript")}}, ומדוע זה חשוב, וכיצד אנחנו יכולים להשתמש בזה על מנת לטפל בפעולות חוסמות () כמו הבאת משאבים מהשרת.

+ +

Prerequisites

+ +

Asynchronous JavaScript הוא יחסית נושא מתקדם ואנו ממליצים לכם לעבור על המודולים צעדים ראשונים ב-JavaScript ועל אבני הבניין של JavaScript לפני שתתחילו ללמוד מודול זה.

+ +

אם אינכם בקיאים בקונספט של תכנות א-סינכרוני, אנא התחילו עם המאמר עקרונות תכנות א-סינכרוני במודול זה. אם אתם כן בקיאים, אתם ככל הנראה יכולים להתחיל ב-הצגת Asynchronous JavaScript.

+ +
+

הערה: אם אתם עובדים על מחשב\טבלט\מכשיר אחר שאין לכם אפשרות ליצור עליו קבצים אישיים, אתם יכולים לנסות את (רוב) דוגמאות הקוד על תוכנות קוד אינטרנטיות כמו JSBin או Thimble.

+
+ +

מדריכים

+ +
+
עקרונות תכנות א-סינכרוני כלליים 
+
+

במאמר זה אנחנו נעבור על מספר עקרונות חשובים בנושא תכנות א-סינכרוני וכיצד עקרונות אלו באים לידי ביטוי בדפדפנים וב-JavaScript. אתם אמורים להבין עקרונות אלו בטרם תמשיכו למאמרים נוספים במודול זה.

+
+
הצגת Asynchronous JavaScript
+
במאמר זה נסקור בקצרה את הבעיות הקשורות ל-JavaScript סינכרונית, ונסתכל לראשונה על האופציות השונות ב-JavaScript א-סינכרוני שניתקל בהן, ונראה כיצד טכניקות אלו יכולות לפתור לנו בעיות.
+
Asynchronous JavaScript: שימוש ב-Intervals ו-Timeouts
+
במאמר זה נסתכל על המתודות המסורתיות שקיימות ב-JavaScript להרצת קוד בצורה א-סינכרונית לאחר שזמן מסויים עבר או בקבועי זמן מסויימים ונדון במקרים שהם רלוונטיים לשימוש וכן נדון בסוגיות הטבועות בהם.
+
טיפול בפעולות א-סינכרוניות באמצעות Promises
+
Promises הם בעיקרון תכונה חדשה יחסית של שפת JavaScript המאפשרת לך לדחות פעולות נוספות עד      לאחר השלמת הפעולה הקודמת, או להגיב לכישלונה. זה מאוד שימושי להגדרת רצף של פעולות שיעבדו בצורה מסויימת. מאמר זה מראה לך כיצד promises עובדות, היכן תראה אותן בשימוש ב- WebAPIs,      ואיך לכתוב promises משלך.
+
הפיכת Asynchronous Programming לקל יותר עם async ועם await
+
Promises יכולות להיות קצת מורכבות לביצוע והבנה, ודפדפנים מודרניים הטמיעו את הפונקציות async ואת האופרטור await - הראשון מאפשר לפונקציות סטנדרטיות להתנהג בצורה עקיפה כא-סינכרוניות עם Promises ואילו בשני אנחנו יכולים לעשות שימוש בתוך פונקציות async על מנת לחכות ל-promises לפני שהפונקציה ממשיכה, כך שנוכל לקשור promises בצורה קלה יותר. מאמר זה מסביר את async/await.
+
בחירת האפשרות המתאימה
+
בסיום המודול אנחנו נדון בהבדלים שבין הטכניקות השונות שנגענו בהם, ונדון באיזו טכניקה יהיה מומלץ להשתמש במקרים השונים.
+
+ +

ראו גם

+ + diff --git a/files/he/learn/javascript/asynchronous/introducing/index.html b/files/he/learn/javascript/asynchronous/introducing/index.html new file mode 100644 index 0000000000..3f5a342e1e --- /dev/null +++ b/files/he/learn/javascript/asynchronous/introducing/index.html @@ -0,0 +1,281 @@ +--- +title: הצגת asynchronous JavaScript +slug: Learn/JavaScript/Asynchronous/Introducing +translation_of: Learn/JavaScript/Asynchronous/Introducing +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}
+ +

במאמר זה אנחנו נסקור שוב את הבעיות הקשורות ב-Synchronous JavaScript, ונסתכל לראשונה על הטכניקות לא-סינכרוניות שניתקל בהן, וכיצד הן יכולות לסייע לנו לפתור בעיות אלו.

+ + + + + + + + + + + + +
ידע מוקדם:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
מטרה:להבין מהי שAsynchronous JavaScript וכיצד היא שונה מ-Synchronous JavaScript, ומתי להשתמש בה.
+ +

Synchronous JavaScript

+ +

על מנת שנוכל להבין מה זה {{Glossary("asynchronous")}} JavaScript אנחנו צריכים תחילה להבין מה זה {{Glossary("synchronous")}} JavaScript. חלק זה של המאמר יסקור שוב חלק מהמידע שכבר עברנו עליו במאמר הקודם.

+ +

הרבה מהפונקציונליות שראינו במודולים הקודמים של מדריך זה היו דיי סינכרוניים - אנחנו מריצים קוד מסויים והתוצאה של אותה ריצה מוחזרת כאשר הדפדפן יכול להחזיר את התוצאה. נסתכל כעת על דוגמא פשוטה ב-דף זהאו בקוד המקור:

+ +
const btn = document.querySelector('button');
+btn.addEventListener('click', () => {
+  alert('You clicked me!');
+
+  let pElem = document.createElement('p');
+  pElem.textContent = 'This is a newly-added paragraph.';
+  document.body.appendChild(pElem);
+});
+
+ +

בקוד זה, השורות מורצות אחת אחרי השנייה:

+ +
    +
  1. אנחנו יוצרים הפנייה לאלמנט {{htmlelement("button")}} שכבר זמין ב-DOM.
  2. +
  3. אנחנו מוסיפים מטפל אירוע בשם click כך שכאשר הכפתור נלחץ: +
      +
    1. הודעת ()alert מוצגת למשתמש.
    2. +
    3. ברגע שהודעה זו נסגרת, אנחנו יוצרים אלמנט {{htmlelement("p")}}.
    4. +
    5. לאחר מכן אנחנו מכניסים לתוך האלמנט  {{htmlelement("p")}} תוכן.
    6. +
    7. בסוף אנחנו משייכים את ה- {{htmlelement("p")}} ל-body.
    8. +
    +
  4. +
+ +

בעוד שכל תהליך שכזה נמצא בהרצה או עיבוד, שום דבר אחר לא יכול להתרחש - יתר העיבוד מושהה. זה מכיוון שכפי שראינו במאמר הקודם ש-JavaScript היא single threaded. רק דבר אחד יכול להתרחש בכל פעם על ה-single main thread, וכל היתר חסום לביצוע או לעיבוד עד אשר אותה פעולה תושלם.

+ +

בדוגמא למעלה, אחרי שלחצנו על הכפתור, הפסקה לא הופיעה עד לאחר שכפתור ה-OK לא נלחץ בחלונית הקופצת. אתם יכולים לנסות זאת בעצמכם:

+ + + +

{{EmbedLiveSample('Synchronous_JavaScript', '100%', '70px')}}

+ +
+

לתשומת לב: חשוב לזכור שאמנם ()alert מאוד שימושי לשם הסבר על פעולות סינכרוניות שחוסמות את המשך הפעילות, זוהי פונקציה שאינה בשימוש רב ביישומים אמיתיים מטעמים של חווית משתמש לרוב.

+
+ +

Asynchronous JavaScript

+ +

לאור הסיבות שנסקרו למעלה, הרבה Web API משתמשים בקוד א-סינכרוני על מנת לפעול, במיוחד אלו שניגשים או מביאים משאב מסויים מגורם חיצוני, כמו הבאה של קובץ מהרשת, גישה למאגר מידע והחזרת מידע מתוכו, הפעלת של הזרמת וידאו באמצעות מצלמת רשת וכד׳.

+ +

מדוע העבודה עם קוד א-סינכרוני היא מורכבת? נסתכל על דוגמא זריזה. כאשר אנחנו מבקשים תמונה משרת מסויים, איננו יכולים לקבל את התשובה באופן מיידי. זה אומר שהפסאודו-קוד הרשום להלן לא יעבוד:

+ +
var response = fetch('myImage.png');
+var blob = response.blob();
+// display your image blob in the UI somehow
+ +

זה מכיוון שאנחנו לא יודעים כמה זמן ייקח לתמונה להגיע מהשרת, אז כאשר אנחנו נרצה להריץ את שורת הקוד הבאה, היא תחזיר לנו שגיאה (אולי לסירוגין, אולי בכל פעם) מכיוון שה-response אינו זמין עבורנו עדיין. במקום זאת, אנחנו צריכים שהקוד שלנו יחכה ל-response שיוחזר אלינו לפני שאנחנו מנסים לעשות עם response פעולה כלשהי.

+ +

יש שני סוגים עיקריים של קוד א-סינכרוני שאנחנו ניתקל בהם בקוד javascript, יש את ה-callbacks הותיקים ואת ה-promises החדשים יותר.  במאמר זה נסקור את שניהם.

+ +

Async callbacks

+ +

Async callbacks אלו פונקציות המועברות כפרמטר (ארגומנט) לפונקציה אחרת, כאשר אנחנו קוראים לפונקציה אחרת אשר מריצה קוד ברקע. כאשר הקוד שברקע סיים לרוץ, הוא קורא לאותן פונקציות callbacks על מנת לציין שהפעולה שהייתה ברקע הסתיימה או לציין שמשהו הסתיים.

+ +

שימוש ב-callbacks יחסית נחשב מיושן כעת, אבל אנחנו עדיין נראה שימוש רב שלהם ב-APIs ישנים אבל עדיין מאוד שימושיים.

+ +

דוגמא ל-async callback אפשר לראות בפרמטר השני של {{domxref("EventTarget.addEventListener", "addEventListener()")}} (כפי שראינו בפעולה למעלה):

+ +
btn.addEventListener('click', () => {
+  alert('You clicked me!');
+
+  let pElem = document.createElement('p');
+  pElem.textContent = 'This is a newly-added paragraph.';
+  document.body.appendChild(pElem);
+});
+ +

הפרמטר הראשון הוא הסוג של האירוע שאנחנו רוצים להאזין להתרחשות שלו, והפרמטר השני הוא פונקציית callback שמופעלת ברגע שהאירוע מתרחש.

+ +

כאשר אנחנו מעבירים callback function כפרמטר לפונקציה אחרת, אנחנו רק מעבירים את הגדרת הפונקציה כפרמטר, כלומר ה-callback function לא מופעל באופן מיידי. הוא נקרא לאחר מכן (“called back”) באופן א-סינכרוני איפשהו בתוך הגוף של הפונקציה שקיבלה אותו כפרמטר. הפונקציה שקיבלה אותו כפרמטר היא האחראית להפעיל את ה-callback function כשנצטרך.

+ +

אנחנו יכולים לכתוב פונקציות משלנו שיכילו callback function באופן דיי פשוט יחסית. נסתכל על דוגמא שמעלה משאב באמצעות XMLHttpRequest API (דף האינטרנט, ו- קוד המקור):

+ +
function loadAsset(url, type, callback) {
+  let xhr = new XMLHttpRequest();
+  xhr.open('GET', url);
+  xhr.responseType = type;
+
+  xhr.onload = function() {
+    callback(xhr.response);
+  };
+
+  xhr.send();
+}
+
+function displayImage(blob) {
+  let objectURL = URL.createObjectURL(blob);
+
+  let image = document.createElement('img');
+  image.src = objectURL;
+  document.body.appendChild(image);
+}
+
+loadAsset('coffee.jpg', 'blob', displayImage);
+ +

כאן יצרנו את פונקציית ()displayImage שפשוט מקבלת blob שמועבר אליה ויוצרת לו URL באמצעות URL.createObjectURL(blob). לאחר מכן הפונקציה יוצרת אלמנט HTML מסוג img, משימה לו את הערך של ה-src לאותו URL שנוצר לנו ומשייכת את ה-image ל-body. 

+ +

בנוסף, יצרנו פונקציה בשם ()loadAsset שמקבלת כפרמטרים כתובת URL, סוג הקובץ וכן פונקציית callback (שימו לב שהשם שנתנו לפרמטר - callback - הוא לשם הנוחות בלבד וניתן לקרוא לפרמטר זה בכל שם). פונקציית ()displayImage משתמשת ב-XMLHttpRequest (לרוב משתמשים בקיצור שלו - "XHR") על מנת להביא משאב מ-URL מסויים לפני שמעבירים את התגובה של אותה XMLHttpRequest לפונקציית callback שלנו - לפונקציית  ()displayImage.

+ +

במקרה הזה, פונקציית ה-callback שלנו מחכה ש-XHR יסיים להוריד את המשאב שהוא הביא (באמצעות שימוש במטפל אירוע מסוג onload), וזאת לפני שהיא תקבל את המשאב.

+ +

Callbacks הם מאוד ורסטיליים - לא רק שהם מאפשרים לנו לשלוט בסדר שבו פונקציות ירוצו ואיזה מידע יועבר ביניהן, הן גם מאפשרות לנו להעביר מידע לפונקציות שונות בהתאם לנסיבות. כך שאנחנו יכולים להריץ פעולות שונות על המשאב שהתקבל או התגובה שהתקבלה כמו processJSON(), displayText(), וכד׳. 

+ +

שימו לב שלא כל ה-Callbacks הם א-סינכרוניים וחלקם הם סינכרוניים. כך לדוגמא, כאשר אנחנו משתמשים ב- {{jsxref("Array.prototype.forEach()")}} על מנת לעבור באמצעות לולאה על איברים במערך (ראו כדף אינטרנט, וכן את קוד המקור):

+ +
const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
+
+gods.forEach(function (eachName, index){
+  console.log(index + '. ' + eachName);
+});
+ +

בדוגמא זו, אנחנו עוברים על מערך של Greek gods ומדפיסים את מספרי האינדקס והערכים לקונסולה. הפרמטר שאנחנו נותנים ל-()forEach הוא פונקציית callback, אשר בעצמו מקבל שני פרמטרים, שם הפריט במערך ומספר האינדקס. יחד עם זאת, היא  לא מחכה לשום דבר, היא פשוט רצה באופן אוטומטי. 

+ +

Promises

+ +

Promises אלו בעצם הסגנון החדש לקוד א-סינכרוני שאנחנו נראה שמבוצע בהם שימוש ב-Web APIs מודרניים. דוגמא טובה לכך היא fetch() API, אשר הוא בעצם כמו גרסה מודרנית ויעילה יותר של {{domxref("XMLHttpRequest")}}. נסתכל על דוגמא מהמאמר שלנו בנושא הבאת מידע מהשרת אשר תגיעו אליו בהמשך:

+ +
fetch('products.json').then(function(response) {
+  return response.json();
+}).then(function(json) {
+  products = json;
+  initialize();
+}).catch(function(err) {
+  console.log('Fetch problem: ' + err.message);
+});
+ +
+

לתשומת לב: אתם יכולים למצוא את הגרסה הסופית ב- GitHub (ראו כאן את קוד המקור, וגם כדף אינטרנט).

+
+ +

כאן אנחנו רואים ש-fetch() לוקח פרמטר אחד - את ה-URL של המשאב שנאחנו רוצים להביא מהרשת - והוא מחזיר promise. ה-promise היא בעצם אובייקט המייצג השלמה או כישלון של פעולת ה-שaysync. בצורה מסויימת, זו הדרך של הדפדפן להגיד ״אני מבטיח לחזור אליך עם תשובה ברגע שאוכל״. מכאן השם promise.

+ +

צריך להתרגל לרעיון הזה על ידי תרגול. זה מרגיש קצת מוזר בהתחלה, כמו החתול של שרדינגר - {{interwiki("wikipedia", "Schrödinger's cat")}}. אף אחת מהאפשרויות לא קרתה עדיין, אז פעולת ההבאה - פעולת ה-fetch, כרגע מחכה לתוצאה של פעולת הדפדפן - להשלמה שלה בעתיד. לאחר מכן שיש לנו שלושה קודי בלוק שמשורשרים לסוף fetch():

+ + + +
+

לתשומת לב: אתם תלמדו עוד הרבה על promises בהמשך המודול הזה, אז אל דאגה אם לא הבנתם אותם עד הסוף. זוהי רק סקירה.

+
+ +

The event queue

+ +

פעולות א-סינכרוניות כמו promises מושמות לתוך ה-event queue, אשר רץ לאחר שה-main thread סיים את העיבודים שלו, כך שהם לא יחסמו קוד JavaScript שבא אחריהם. הפעולות שב-promises יושלמו ברגע שיתאפשר, ויחזירו את התוצאה שלהן לסביבת הjavascript. Async operations like promises are put into an event queue, which runs after the main thread has finished processing so that they do not block subsequent JavaScript code from running. The queued operations will complete as soon as possible then return their results to the JavaScript environment.

+ +

Promises מול callbacks

+ +

ל-Promises יש קצת דמיון ל-callbacks הוותיקים. הם בעיקרון אבוייקט שהוחזר, אליו אנחנו מחברים פונקציות callbacks, ולא צריכים להעביר callbacks לתוך פונקציה.

+ +

יחד עם זאת, promises נוצרו במיוחד עבור טיפול בפעולת א-סינכרוניות, ויש להם הרבה יתרונות על ה-callbacks הוותיקים:

+ + + +

ההתנהגות של קוד א-סינכרוני

+ +

בואו נעמיק בדוגמא שתסביר לנו יותר לעומק את ההתהנגות של קוד א-סינכרוני, שמראה מה יכול לקראת כשאנחנו לא בקיאים לגמי בסדר של הרצת הקוד וההבעיות שיש בניסיון לטפל בקוד א-סינכרוני כמו בקוד סינכרוני. הודמא הבא היא יחסית דומה למה שראינו בעבר. sכדף אינטרנט, וגם קוד המקור). הבדל אחד הוא שכללנו מספר ביטויים של {{domxref("console.log()")}} על ממנת להמחיש את הסדר שאנחנו נחשוב שבו הקוד ירוץ

+ +
console.log ('Starting');
+let image;
+
+fetch('coffee.jpg').then((response) => {
+  console.log('It worked :)')
+  return response.blob();
+}).then((myBlob) => {
+  let objectURL = URL.createObjectURL(myBlob);
+  image = document.createElement('img');
+  image.src = objectURL;
+  document.body.appendChild(image);
+}).catch((error) => {
+  console.log('There has been a problem with your fetch operation: ' + error.message);
+});
+
+console.log ('All done!');
+ +

הדפדפן יתחיל להריץ את הקוד, הוא יראה את ה-console.log() הראשון ויריץ אותו. ולאחר מכן ייצור את המשתנה image .

+ +

הוא לאחר מכן ימשיך לשורה הבאה, ויתחיל להריץ את הבלוק קוד של fetch(), אבל מכיוון ש-fetch() מורץ באופן א-סינכרוני בלי חסימה, הדפדפן ימשיך לקוד שלאחר הקוד של ה-promise-related code, ולכן יגיע ל-console.log() האחרון ( All done!) ואותו יציג לקונסולה. ימשיך לרוץ

+ +

רק ברגע שהבלוק של ה-code>fetch() סיים לחלוטין לרוץ והביא תוצאה - result באמצעות הבלוקים של .then(), אנחנו נראה לבסוף את הההודעה שב-console.log() השני (It worked ;)). כך שההודעות הופיעו בסדר שונה ממה שאולי חשבתם:

+ + + +

אם זה מבלבל אתכם, הסתכלו על הדוגמא הבאה:

+ +
console.log("registering click handler");
+
+button.addEventListener('click', () => {
+  console.log("get click");
+});
+
+console.log("all done");
+ +

זה מאוד דומה בהתנהגות - ה-console.log() הראשון והשלישי יציגו את ההודעות שלהן מיד, אבל ה-console.log() השני יהיה חסום להצגה עד אשר מישהו ילחץ על הכפתור. הדוגמא הקודמת פועלת בדרך דוה, למעט כך שבמקרה שההודעה השנייה חסומה בשרשרת ההבטחות המביאות משאב מסויים ומוצגת רק כאשר שרשרת ההבטטחות הושלמה, ולא כאשר המשתמש ילחץ על כפתור. .

+ +

בדוגמה של קוד פחות טריוויאלי, קוד מהסוג של הדוגמא הראשונה עלולה לגרום לבעיה - אינכם יכולים לכלול בלוק קוד אסינכרוני המחזיר תוצאה, עליה אתם מסתמכים בהמשך בבלוק קוד סינכרוני. אינכם כולים להבטיח שפונקציית ה-שaysync תחזור לפני שהדפדפן יעבד את חסימת האסינכרון.

+ +

על מנת לראות זאת בפעולה, נסו לעשות עותק מקומי של To see this in action, try taking a local copy of הדוגמאו תשלנו, ושנו את ה- console.log() שלישית כך:

+ +
console.log ('All done! ' + image + 'displayed.');
+ +

אתם אמורים לקבל שגיאה בקונסולה במקום ההודעה השלישית :

+ +
TypeError: image is undefined; can't access its "src" property
+ +

זה מכיוון שבזמן שהדפדפן מנסה להריץ את ה-console.log() השלישית, הבלוק קוד של fetch() עדיין לא סיים לרות, כך שהמשתנה image לא ניתן לו ערך עדיין.

+ +

למידה עצמאית: פכו את הכל ל- async!

+ +

על מנת לתקן את ה- fetch(), ולהפוך את שלושת -console.log() להיות מופעים בסדר הרצוי, אתם יכולים לעשות את ה-console.log() השלישי שירוץ גם הוא בצורה א-סינכרוני. את זה ניתן לעשות באמצעות העברה שלו לתוך .then() אחר, אשר משורשר לתוך הסוף של ה-.then() השני, או באמצעות פשוט העברה שלו לתוך ה-.then() השני. נסו לתקן זאת.

+ +
+

לתשומת לב: אם נתקעתם, אתם יכולים למצוא את התשובה כאן או כ דף אינטרנט also). אתם גם יכולים למצוא עוד הרבה על promises במדריך שלנו בנושא טיפול בפעולות א-סינכרוניות באמצעות Promises, בהמשך המודול הזה.

+
+ +

לסיכום

+ +

במבנה הבסיסי שלה, JavaScript היא סינכרונית, שפה single-threaded,כך רק פעולה אחת יכולה להיות מעובדת בכל זמן נתון. יחד עם זאת, הדפדפנים הגדירו פונקציות ו-APIs שמאפשרים לנו לרשום פונקציות שלא ירוצו באופן סינכרוני, ובמקום זאת, יופעלו באופן א-סינכרוני כאשר אירוע מסויים מתרחש. זה אומר שאנחנו יכולים לתת לקוד שלנו לעשות דברים אחרים באותו הזמן, מבלי לחסום או לעצור את ה-main thread.

+ +

בין אם אנחנו רוצים להירץ קוד באופן סינכרוני או א-סינכרוני, זה תלוי במה שאנחנו מנסים לעשות.

+ +

יש פעמים שאנחנו נרצה שדברים יועלו ויתרחשו במיידי. לדוגמא, כאשר המתשמש מגדיר סגנון לאתר ואתם רוצים שהסגנון יוחל באופן מיידי.

+ +

אם אנחנו מריצים פעולה מסויימת שלוקח לה זמן, כמו לדוגמא גישה למאגר מידע ושימוש בתוצאות על מנת ליצור תבניות לדגומא, זה יהיה עדיף להעביר את הפעולה הזו מחוץ ל-main thread, כך שתתצבע באופן א-סינכרוני. עם הזמן, אתם תלמדו מתי זה היה הגיוני לבחור בטכניקה א-סינכרונית ולא בסינכורנית.

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/asynchronous/promises/index.html b/files/he/learn/javascript/asynchronous/promises/index.html new file mode 100644 index 0000000000..87c8839084 --- /dev/null +++ b/files/he/learn/javascript/asynchronous/promises/index.html @@ -0,0 +1,589 @@ +--- +title: Graceful asynchronous programming with Promises +slug: Learn/JavaScript/Asynchronous/Promises +translation_of: Learn/JavaScript/Asynchronous/Promises +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}
+ +

Promises הן תכונה חדשה יחסית ב-JavaScript, אשר מאפשרת לנו לדחות פעולות מסוייומות עד אשר פעולותדמות להן יושלמו, או שיגיבו לכך שהן נכשלו. זוהי תכונה מאוד שימושית על מנת ליצור רצף של פעולות א-סינכרוניות שיעבדו בצור הטובה. מאמר זה נועד להסביר כיצד promises עובדות, כיצד אנו נראה אותם בשימוש ב-web APIs וכיצד נכתוב promises משלנו.

+ + + + + + + + + + + + +
ידע מוקדם:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
מטרה:להבין מהן promises וכיצד להשתמש בהן.
+ +

מהן promises?

+ +

סקרנו קצת Promises במאמר הראשון במודול זה, אבל במאמר זה נסקור אותן יותר לעומק.?

+ +

בעיקרון promise הוא בעצם אובייקט שמייצג מצב ביניים של פעולה - בפועל, promise היא מעין הבטחה שתוצאה מסויימת תוחזר בנקודה מסויימת בעתיד. אין שום הבטחה מתי הפעולה תושלם ומתי התוצאה תוחזר, אבל יש הבטחה שכאשר התוצאה מוכנה, או שכאשר הפעוללה נכשלה, הקוד שסיפקנו ירוץ על מנת לעשות משהו עם תוצאה מוצלחת או להתמודד עם כשלון בפעולה.

+ +

באופן כללי, אנחנו פחות מתעניינים בזמן שייקח לפעולה א-סינכרונית להחזיר את התוצאה שלה (אלא אם כן מדובר בזמן יותר מדי ארוך), ואנחנו יותר מעוניינים בכך שתהיה לנו אפשרות להגיב לאותה תגובה כאשר היא תחזור, לא משנה מהי התגובה. וכמובן, זה נחמד שאותה פעולה לא חוסמת את הקוד מלרוץ.

+ +

אחד מהשימושים של promise שאנחנו נראה הוא ב-web APIs שמחזירים promise. נניח ויש לנו יישום וידאו צ׳אט. ליישום זה יש חלון עם רשימת החברים של המשתמש, ובלחיצה על כפתור ליד שם החבר, תחל שיחת וידאו עם אותו חבר.

+ +

מטפל האירוע של הכפתור הזה קורא ל- {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} על מנת לקבל גישה למיקרופון ולמצלמה של המשתמש. מאחר ו-()getUserMedia חייב לוודא שלמשתמש יש אישור להשתמש באותם מכשירים ולבקש מהמשתמש לבחור איזה מיקרופון ואיזו מצלמה להשתמש בהם או בכל שילוב אחר. בזמן הבקשה הזו, פעולה זו יכולה לחסום את המשך התוכנית לא רק עד אשר כל האישורים האלו התקבלו, אלא גם עד אשר המצלמה והמיקרופון יתחברו. בנוסף, המשתמש אולי לא יגיב באופן מיידי לבקשות אלו. זה יכול לקחת הרבה זמן.

+ +

מאחר והקריאה ל- ()getUserMedia נעשית מה- browser's main thread, הדפדפן חסום עד אשר ()getUserMedia תסיים. ברור כמובן שזו לא אפשרות מקובלת בימים אלו. ללא promises, כל דבר בדפדפן היה נהפך ללא שימושי עד אשר המשתמש יחליט מה לעשות עם המצלמה והמיקרופון. אז במקום לחכות למשתמש, לקבל את המכשירים שנבחרו, ולהחזיר את ה-{{domxref("MediaStream")}} שנוצר למקורות אלו, ()getUserMedia מחזיר {{jsxref("promise")}} אשר נפתר עם {{domxref("MediaStream")}} ברגע שהוא זמין.

+ +

הקוד של יישום הוידאו צאט שלנו, עשוי להיראות כך?:?

+ +
function handleCallButton(evt) {
+  setStatusMessage("Calling...");
+  navigator.mediaDevices.getUserMedia({video: true, audio: true})
+    .then(chatStream => {
+      selfViewElem.srcObject = chatStream;
+      chatStream.getTracks().forEach(track => myPeerConnection.addTrack(track, chatStream));
+      setStatusMessage("Connected");
+    }).catch(err => {
+      setStatusMessage("Failed to connect");
+    });
+}
+
+ +

הפונקציה הזו מתחילה בכך שהיא משתמש בפונקציה שנקראת ()setStatusMessage על מנת לעדכן שורת עדכון למשתמש עם הטקסט: "Calling...", שמעידה על כך שמבוצע ניסיול החל בשיחה. לאחר מכן היא קוראת לפונקציה ()getUserMedia, ומבקשת שתחל בסטרים (stream) ?שיש לו גם וידאו וגם אודיו וברגע שהסטרים יהיה מוכן, היא ממשיכה בפעולות לשם הקמת הסטרים וחיבור המצלמה והאודיו. לאחר מכן היא מציגה למשתמש הודעה ״Connected״. 

+ +

אם ()getUserMedia תיכשל, בלוק הקוד של catch ירוץ. הוא משתמש שוב ב- ()setStatusMessage על מנת להציג למשתמש שהתרחשה שגיאה. 

+ +

הדבר החשוב כן הוא שהקריאה של ()getUserMedia מוחזרת כמעט באופן מיידי, גם אם הסטרים של המצלמה לא התקבל עדיין. אפילו אם הפונקציה של ()handleCallButton סיימה להריץ את הקוד שבתוכה והחזירה את השליטה לקוד שקרא לה, כאשר ()getUserMedia תסיים את הפעולה שלה, בין אם בהצלחה ובין אם תתרחש שגיאה, פונקציה זו תקרא שוב לאותו מטפל אירוע שסיפקנו לה. כלומר, היישום ימשיך לעבוד ולא יחכה לזרם הוידאו ולא ימנע מיתר הקוד להמשיך לרוץ. 

+ +
+

לתשומת לב:  לפרטים נוספים אודות נושא מורכב זה של וידאו ואודיו, ראו Signaling and video calling.

+
+ +

הבעיה עם callbacks

+ +

על מנת להבין לעומק מדוע promises הן דבר טוב, יעזור לנו לחשוב על ה-callbacks המיושנות ולהבין מדוע הן בעייתיות. 

+ +

ננסה להמחיש זאת באמצעות דוגמא להזמנת פיצה כאנלוגיה. יש מספר שלבים שאנחנו צריכים לעשות ולהשלים בהצלחה, אשר לא ממש הגיוני לבצע אותם בסדר אחר או לבצע אותם בסדר הנכון, אבל בלי לחכות שהשלב הקודם הסתיים:

+ +
    +
  1. בחירת התוספות לפיצה - זה יכול לקחת זמן אם אנחנו לא ממש בטוחים איזו תוספת אנחנו רוצים על הפיצה ושלב זה יכול להיכשל אם בסופו של דבר לא קיבלנו החלטה איזו תוספת אנחנו רוצים, או אם בסוף החלטנו לאכול משהו אחר. 
  2. +
  3. בהנחה והחלטנו על תוספת, לאחר מכן אנחנו מבצעים הזמנה של הפיצה שאנחנו רוצים. שלב זה יכול לקחת זמן עד אשר הפיצה תהיה מוכנה, ואף יכול להיכשל אם לפיצרייה אין את המרכיבים הנכונים. 
  4. +
  5. בשלב האחרון אנחנו אוספים את הפיצה ואוכלים, גם שלב זה יכול להיכשל אם לא שילמנו לדוגמא על הפיצה או לא באנו לאסוף את הפיצה. 
  6. +
+ +

עם ה-callbacks הישנות, קוד מופשט שלנו ייראה בערך כך: 

+ +
chooseToppings(function(toppings) {
+  placeOrder(toppings, function(order) {
+    collectOrder(order, function(pizza) {
+      eatPizza(pizza);
+    }, failureCallback);
+  }, failureCallback);
+}, failureCallback);
+ +

קוד זה הוא מבולגן וקשה להבנה, ולפעמים גם נקרא כ-callback hell, הוא גם דורש שהפונקציה ()failureCallback תיקרא כמה פעמים (בעבור כל פונקציה משורשרת) ויש לו גם חסרונות נוספים.

+ +

שדרוג עם  promises

+ +

Promises הופכות את סיטואציות כמו למעלה להרבה יותר פשוטות לכתיבה, פירוש וריצה. אם נציג את הקוד המופשט שלנו שוב באמצעות promises א-סינכרוניות, הוא ייראה כך:

+ +
chooseToppings()
+.then(function(toppings) {
+  return placeOrder(toppings);
+})
+.then(function(order) {
+  return collectOrder(order);
+})
+.then(function(pizza) {
+  eatPizza(pizza);
+})
+.catch(failureCallback);
+ +

זה כבר הרבה יותר טוב- הרבה יותר קל להבין מה קורה כאן והיינו צריכים רק בלוק קוד אחד של ()catch. בעבור כל השגיאות שאולי יתרחשו. כל הקוד הזה אינו חוסם את ה-main thread (כך שאנחנו יכולים להמשיך לראות טלווזיה עד אשר הפיצה תהיה מוכנה), וכל פעולה תחכה בוודאות עד אשר הפעולה הקודמת תסיים לפני שהיא תרוץ. אנחנו יכולים לקשור מספר פעולות א-סינכרוניות כך שיתרחשו אחת אחרי השנייה מכיוון שכל בלוק קוד של (.....)then. מחזיר promise חדשה, שנפתח כאשר הקוד שבתוך ה-(.....)then. הרלוונטי מסיים לרוץ. 

+ +

באמצעות שימוש בפונקציות חץ (arrow functions), אנחנו אפילו יכולים להפוך את הקוד לעוד יותר פשוט: 

+ + + +
chooseToppings()
+.then(toppings =>
+  placeOrder(toppings)
+)
+.then(order =>
+  collectOrder(order)
+)
+.then(pizza =>
+  eatPizza(pizza)
+)
+.catch(failureCallback);
+ +

או אפילו בצורה הזו:

+ +
chooseToppings()
+.then(toppings => placeOrder(toppings))
+.then(order => collectOrder(order))
+.then(pizza => eatPizza(pizza))
+.catch(failureCallback);
+ +

זה עובד מכיוון שעם פונקציות חץ, x <= () הוא סינטקסס חוקי והוא קיצור של {return x} <= ().

+ +

אנחנו אפילו יכולים לרשום את הקוד שלנו בצורה כזו, מאחר שהפונקציות רק מעביר את הארגומנטים שלהם בצורה ישירה, אין צורך באמת לשכבה נוספת של פונקציה:

+ +
chooseToppings().then(placeOrder).then(collectOrder).then(eatPizza).catch(failureCallback);
+ +

יחד עם זאת, סינטקס זה לא ניתן לשימוש שוב, ולא ממש ברור לקריאה. 

+ +
+

לתשומת לב: אנחנו אפילו יכולים לעשות שיפורים נוספים בסינטקס באמצעות הסינטקס של async/await, אשר נסקור אותו בהמשך המודול.

+
+ +

בבסיס שלהן, promises דומות למאזיני אירוע - event lisenters אך עם הבדלים ביניהם:

+ + + +

הסבר של הסינטקט הבסיסי של promise

+ +

Promises חשובות מאוד להבנה מכיוון שמרבית ה-Web API המודרניים משתמשים בהן בעבור פונקציות שעלולות לבצע פעולות ארוכות. 

+ +

בהמשך המאמר הזה אנחנו נראה כיצד לכתוב promises משלנו, אבל כאן אנחנו נסכתל על דוגמאות פשוטות שאנחנו ניתקל בהן ב-Web API שונים.

+ +

בדוגמא הראשונה שלנו, אנחנו נשתמש במתודת fetch(), על מנת להביא תמונה מהאינטרנט, במתודת {{domxref("Body.blob", "blob()")}} על מנת להמיר את התוכן הגולמי של התגובה שקיבלנו לאובייקט {{domxref("Blob")}} , ולאחר מכן נציג את אותו blob בתוך אלמנט {{htmlelement("img")}}. זהו מאוד דומה לדוגמא שהסתכלנו עליה במאמר הראשון במודול, אבל אנחנו נעשה את זה מעט שונה כך שאתם תבנו את ה-promise. 

+ +
    +
  1. +

    ראשית, הורידו את ה-HTML template ואת ה-image file שאנחנו נייבא. 

    +
  2. +
  3. +

    הוסיפו אלמנט {{htmlelement("script")}} בתחתית ה-{{htmlelement("body")}} בקובץ ה-HTML.

    +
  4. +
  5. +

    בתוך האלמנט {{HTMLElement("script")}} , הוסיפו את השורה הבאה: 

    +
  6. +
  7. +
    let promise = fetch('coffee.jpg');
    + +

    שורה זו קוראת למתודת ()fetch ומעבירה אליה כפרמטר את ה-URL של התמונה שאנחנו רוצים לייבא מהרשת. אנחנו מאחסנים את אובייקט ה-promise שיוחזר אלינו מ-()fetch בתוך משתנה שנקרא לו promise. כפי שאמרנו בעבר, האובייקט הזה מייצג מצב ביניים שבהתחלה הוא לא הצלחה ולא כישלון - מצב זה נקרא pending.

    +
  8. +
  9. על מנת להגיב להצלחה של הפעולה כאשר היא תקרה, במקרה הזה כאשר {{domxref("Response")}} תוחזר אלינו, אנחנו נפעיל את המתודת ()then. של אובייקט ה-promise. ה-callback בתוך בלוק הקוד של ()then. (שנקרא גם executor), ירוץ רק כאשר פעולת ה-promise תושלם בהצלחה ותחזיר אובייקט {{domxref("Response")}} - במונחי promise, כאשר ה-promise תגיע למצב של הושלמה, fulfilled. היא תעביר את האובייקט {{domxref("Response")}} כפרמטר. 
  10. +
  11. +
    +

    לתשומת לב: הדרך שבה הבלוק ()then. עובד היא דומה לדרך שבה אנחנו מוסיפים מאזין אירוע - event listener - לאובייקט באמצעות המתודה ()AddEventListener. מאזין האירוע לא ירוץ עד אשר האירוע יתרחש (ובאותה נשימה, כאשר ה-promise הושלמה בהצלחה). ההבדל העיקרי ביניהם הוא ש-()then. ירוץ רק פעם אחת בכל פעם שמשתמשים בו ואילו מאזין האירוע יכול להיות מופעל כמה פעמים. 

    +
    + +

    אנחנו מייד נריץ את מתודת  ()blob על התגובה הזו, על מנת לוודא שגוף התגובה הורד בהצלחה, וכאשר הוא זמין נהפוך אותו לאובייקט Blob שאנחנו יכולים לעשות איתו משהו. התוצאה של זה תוחזר לנו כך :

    + +
    response => response.blob()
    + +

    שזה קיצור של: 

    + +
    function(response) {
    +  return response.blob();
    +}
    + +

    עד כאן עם ההסבר, אנא הוסיפו את הקוד הבא מתחת לשורה הראשונה של JavaScript: 

    + +
    let promise2 = promise.then(response => response.blob());
    +
  12. +
  13. +

    כל קריאה ל-()then. יוצרת promise חדשה. זהו מאוד שימושי - מכיוון שמתודת -  ()blob מחזירה גם היא promise, אנחנו יכולים לטפל באובייקט ה-Blob שהיא מחזירה בעת ההשלמה שלו באמצעות הפעלה של מתודת ()then. על ה-promise השנייה. מכיוון שאנחנו רוצים לעשות משהו קצת יותר מורכב לאותו blob מאשר רק להריץ מתודה אחת עליו ולהחזיר את התוצאה, אנחנו צריכים לעטוף את גוף הפונקצייה בסוגריים מסולסלות - אחרת זה יגרום לשגיאה. הוסיפו את השורה הבאה מתחת לקוד הנוכחי שלכם: 

    +
  14. +
  15. +
    let promise3 = promise2.then(myBlob => {
    +
    +})
    +
  16. +
  17. +

    כעת, נכניס את הקוד הבא שהוא הפונקציה שתופעל, לתוך הסוגריים המסולסלות:

    + +
    let objectURL = URL.createObjectURL(myBlob);
    +let image = document.createElement('img');
    +image.src = objectURL;
    +document.body.appendChild(image);
    + +

    מה שעשינו כאן אנחנו מריצים בעצם מריצים מתודת {{domxref("URL.createObjectURL()")}} , מעבירים אליה כפרמרט את ה-Blob שהוחזר אלינו כאשר ה-promise השנייה הושלמה. זה מחזיר לנו URL שמצביע על האובייקט. לאחר מכן יצרנו אלמנט {{htmlelement("img")}} , וקבענו את ה-src שלו שיהיה שווה ל-URL של האובייקט ושייכנו אותו ל-DOM, כך שהתמונה תוצג על גבי הדף. 

    +
  18. +
+ +

אם תשמרו ותרעננו את הדף, אתם תראו שהתמונה מוצגת על גבי הדף. 

+ +
+

לתשומת לב:  אתם בטח תשימו לב שדוגמאות אלו ארוכות מדי בשביל פעולה פשוטה שיכולנו לבצע באמצעות יצירת אלמנט <img> וקביעת ה-src שלו לאותו URL של התמונה במקום לעשות זאת באמצעות ()fetch ובאמצעות ()blob. יחד עם זאת, דוגמא זו נועדה על מנת להסביר בפשטות את תהליך ה-promises.

+
+ +

תגובה לכישלון 

+ +

יש משהו חסר בדוגמא שלנו - כרגע, אין משהו שמגדיר כיצד להתמודד עם שגיאה כאשר ה-promise נכשלת, או rejects במונחים של promises. אנחנו יכולים להוסיף טיפול בשגיאה באמצעות הרצת מתודת ()catch. של ה-promise הקודמת:

+ +
let errorCase = promise3.catch(e => {
+  console.log('There has been a problem with your fetch operation: ' + e.message);
+});
+ + + +

To see this in action, try misspelling the URL to the image and reloading the page. The error will be reported in the console of your browser's developer tools.

+ +

This doesn't do much more than it would if you just didn't bother including the .catch() block at all, but think about it — this allows us to control error handling exactly how we want. In a real app, your .catch() block could retry fetching the image, or show a default image, or prompt the user to provide a different image URL, or whatever.

+ +
+

Note: You can see our version of the example live (see the source code also).

+
+ +

Chaining the blocks together

+ +

This is a very longhand way of writing this out; we've deliberately done this to help you understand what is going on clearly. As shown earlier on in the article, you can chain together .then() blocks (and also .catch() blocks). The above code could also be written like this (see also simple-fetch-chained.html on GitHub):

+ +
fetch('coffee.jpg')
+.then(response => response.blob())
+.then(myBlob => {
+  let objectURL = URL.createObjectURL(myBlob);
+  let image = document.createElement('img');
+  image.src = objectURL;
+  document.body.appendChild(image);
+})
+.catch(e => {
+  console.log('There has been a problem with your fetch operation: ' + e.message);
+});
+ +

Bear in mind that the value returned by a fulfilled promise becomes the parameter passed to the next .then() block's executor function.

+ +
+

Note: .then()/.catch() blocks in promises are basically the async equivalent of a try...catch block in sync code. Bear in mind that synchronous try...catch won't work in async code.

+
+ +

Promise terminology recap

+ +

There was a lot to cover in the above section, so let's go back over it quickly to give you a short guide that you can bookmark and use to refresh your memory in the future. You should also go over the above section again a few more time to make sure these concepts stick.

+ +
    +
  1. When a promise is created, it is neither in a success or failure state. It is said to be pending.
  2. +
  3. When a promise returns, it is said to be resolved. +
      +
    1. A successfully resolved promise is said to be fulfilled. It returns a value, which can be accessed by chaining a .then() block onto the end of the promise chain. The executor function inside the .then() block will contain the promise's return value.
    2. +
    3. An unsuccessful resolved promise is said to be rejected. It returns a reason, an error message stating why the promise was rejected. This reason can be accessed by chaining a .catch() block onto the end of the promise chain.
    4. +
    +
  4. +
+ +

Running code in response to multiple promises fulfilling

+ +

The above example showed us some of the real basics of using promises. Now let's look at some more advanced features. For a start, chaining processes to occur one after the other is all fine, but what if you want to run some code only after a whole bunch of promises have all fulfilled?

+ +

You can do this with the ingeniously named Promise.all() static method. This takes an array of promises as an input parameter and returns a new Promise object that will fulfill only if and when all promises in the array fulfill. It looks something like this:

+ +
Promise.all([a, b, c]).then(values => {
+  ...
+});
+ +

If they all fulfill, then chained .then() block's executor function will be passed an array containing all those results as a parameter. If any of the promises passed to Promise.all() reject, the whole block will reject.

+ +

This can be very useful. Imagine that we’re fetching information to dynamically populate a UI feature on our page with content. In many cases, it makes sense to receive all the data and only then show the complete content, rather than displaying partial information.

+ +

Let's build another example to show this in action.

+ +
    +
  1. +

    Download a fresh copy of our page template, and again put a <script> element just before the closing </body> tag.

    +
  2. +
  3. +

    Download our source files (coffee.jpg, tea.jpg, and description.txt), or feel free to substitute your own.

    +
  4. +
  5. +

    In our script we'll first define a function that returns the promises we want to send to Promise.all(). This would be easy if we just wanted to run the Promise.all() block in response to three fetch() operations completing. We could just do something like:

    + +
    let a = fetch(url1);
    +let b = fetch(url2);
    +let c = fetch(url3);
    +
    +Promise.all([a, b, c]).then(values => {
    +  ...
    +});
    + +

    When the promise is fulfilled, the values passed into the fullfillment handler would contain three Response objects, one for each of the fetch() operations that have completed.

    + +

    However, we don't want to do this. Our code doesn't care when the fetch() operations are done. Instead, what we want is the loaded data. That means we want to run the Promise.all() block when we get back usable blobs representing the images, and a usable text string. We can write a function that does this; add the following inside your <script> element:

    + +
    function fetchAndDecode(url, type) {
    +  return fetch(url).then(response => {
    +    if (type === 'blob') {
    +      return response.blob();
    +    } else if (type === 'text') {
    +      return response.text();
    +    }
    +  })
    +  .catch(e => {
    +    console.log('There has been a problem with your fetch operation: ' + e.message);
    +  });
    +}
    + +

    This looks a bit complex, so let's run through it step by step:

    + +
      +
    1. First of all we define the function, passing it a URL and a string representing the type of resource it is fetching.
    2. +
    3. Inside the function body, we have a similar structure to what we saw in the first example — we call the fetch() function to fetch the resource at the specified URL, then chain it onto another promise that returns the decoded (or "read") response body. This was always the blob() method in the previous example.
    4. +
    5. However, two things are different here: +
        +
      • First of all, the second promise we return is different depending on what the type value is. Inside the executor function we include a simple if ... else if statement to return a different promise depending on what type of file we need to decode (in this case we've got a choice of blob or text, but it would be easy to extend this to deal with other types as well).
      • +
      • Second, we have added the return keyword before the fetch() call. The effect this has is to run the entire chain and then run the final result (i.e. the promise returned by blob() or text()) as the return value of the function we've just defined. In effect, the return statements pass the results back up the chain to the top.
      • +
      +
    6. +
    7. +

      At the end of the block, we chain on a .catch() call, to handle any error cases that may come up with any of the promises passed in the array to .all(). If any of the promises reject, the catch block will let you know which one had a problem. The .all() block (see below) will still fulfill, but just won't display the resources that had problems. If you wanted the .all to reject, you'd have to chain the .catch() block on to the end of there instead.

      +
    8. +
    + +

    The code inside the function body is async and promise-based, therefore in effect the entire function acts like a promise — convenient.

    +
  6. +
  7. +

    Next, we call our function three times to begin the process of fetching and decoding the images and text, and store each of the returned promises in a variable. Add the following below your previous code:

    + +
    let coffee = fetchAndDecode('coffee.jpg', 'blob');
    +let tea = fetchAndDecode('tea.jpg', 'blob');
    +let description = fetchAndDecode('description.txt', 'text');
    +
  8. +
  9. +

    Next, we will define a Promise.all() block to run some code only when all three of the promises stored above have successfully fulfilled. To begin with, add a block with an empty executor inside the .then() call, like so:

    + +
    Promise.all([coffee, tea, description]).then(values => {
    +
    +});
    + +

    You can see that it takes an array containing the promises as a parameter. The executor will only run when all three promises resolve; when that happens, it will be passed an array containing the results from the individual promises (i.e. the decoded response bodies), kind of like [coffee-results, tea-results, description-results].

    +
  10. +
  11. +

    Last of all, add the following inside the executor. Here we use some fairly simple sync code to store the results in separate variables (creating object URLs from the blobs), then display the images and text on the page.

    + +
    console.log(values);
    +// Store each value returned from the promises in separate variables; create object URLs from the blobs
    +let objectURL1 = URL.createObjectURL(values[0]);
    +let objectURL2 = URL.createObjectURL(values[1]);
    +let descText = values[2];
    +
    +// Display the images in <img> elements
    +let image1 = document.createElement('img');
    +let image2 = document.createElement('img');
    +image1.src = objectURL1;
    +image2.src = objectURL2;
    +document.body.appendChild(image1);
    +document.body.appendChild(image2);
    +
    +// Display the text in a paragraph
    +let para = document.createElement('p');
    +para.textContent = descText;
    +document.body.appendChild(para);
    +
  12. +
  13. +

    Save and refresh and you should see your UI components all loaded, albeit in a not particularly attractive way!

    +
  14. +
+ +

The code we provided here for displaying the items is fairly rudimentary, but works as an explainer for now.

+ +
+

Note: If you get stuck, you can compare your version of the code to ours, to see what it is meant to look like — see it live, and see the source code.

+
+ +
+

Note: If you were improving this code, you might want to loop through a list of items to display, fetching and decoding each one, and then loop through the results inside Promise.all(), running a different function to display each one depending on what the type of code was. This would make it work for any number of items, not just three.

+ +

In addition, you could determine what the type of file is being fetched without needing an explicit type property. You could for example check the {{HTTPHeader("Content-Type")}} HTTP header of the response in each case using response.headers.get("content-type"), and then react accordingly.

+
+ +

Running some final code after a promise fulfills/rejects

+ +

There will be cases where you want to run a final block of code after a promise completes, regardless of whether it fulfilled or rejected. Previously you'd have to include the same code in both the .then() and .catch() callbacks, for example:

+ +
myPromise
+.then(response => {
+  doSomething(response);
+  runFinalCode();
+})
+.catch(e => {
+  returnError(e);
+  runFinalCode();
+});
+ +

In more recent modern browsers, the .finally() method is available, which can be chained onto the end of your regular promise chain allowing you to cut down on code repetition and do things more elegantly. The above code can now be written as follows:

+ +
myPromise
+.then(response => {
+  doSomething(response);
+})
+.catch(e => {
+  returnError(e);
+})
+.finally(() => {
+  runFinalCode();
+});
+ +

For a real example, take a look at our promise-finally.html demo (see the source code also). This works exactly the same as the Promise.all() demo we looked at in the above section, except that in the fetchAndDecode() function we chain a finally() call on to the end of the chain:

+ +
function fetchAndDecode(url, type) {
+  return fetch(url).then(response => {
+    if(type === 'blob') {
+      return response.blob();
+    } else if(type === 'text') {
+      return response.text();
+    }
+  })
+  .catch(e => {
+    console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
+  })
+  .finally(() => {
+    console.log(`fetch attempt for "${url}" finished.`);
+  });
+}
+ +

This logs a simple message to the console to tell us when each fetch attempt has finished.

+ +
+

Note: finally() allows you to write async equivalents to try/catch/finally in async code.

+
+ +

Building your own custom promises

+ +

The good news is that, in a way, you've already built your own promises. When you've chained multiple promises together with .then() blocks, or otherwise combined them to create custom functionality, you are already making your own custom async promise-based functions. Take our fetchAndDecode() function from the previous examples, for example.

+ +

Combining different promise-based APIs together to create custom functionality is by far the most common way you'll do custom things with promises, and shows the flexibility and power of basing most modern APIs around the same principle. There is another way, however.

+ +

Using the Promise() constructor

+ +

It is possible to build your own promises using the Promise() constructor. The main situation in which you'll want to do this is when you've got code based on an an old-school asynchronous API that is not promise-based, which you want to promis-ify. This comes in handy when you need to use existing, older project code, libraries, or frameworks along with modern promise-based code.

+ +

Let's have a look at a simple example to get you started — here we wrap a setTimeout() call with a promise — this runs a function after two seconds that resolves the promise (using the passed resolve() call) with a string of "Success!".

+ +
let timeoutPromise = new Promise((resolve, reject) => {
+  setTimeout(function(){
+    resolve('Success!');
+  }, 2000);
+});
+ +

resolve() and reject() are functions that you call to fulfill or reject the newly-created promise. In this case, the promise fulfills with a string of "Success!".

+ +

So when you call this promise, you can chain a .then() block onto the end of it and it will be passed a string of "Success!". In the below code we simply alert that message:

+ +
timeoutPromise
+.then((message) => {
+   alert(message);
+})
+ +

or even just

+ +
timeoutPromise.then(alert);
+
+ +

Try running this live to see the result (also see the source code).

+ +

The above example is not very flexible — the promise can only ever fulfill with a single string, and it doesn't have any kind of reject() condition specified (admittedly, setTimeout() doesn't really have a fail condition, so it doesn't matter for this simple example).

+ +
+

Note: Why resolve(), and not fulfill()? The answer we'll give you for now is it's complicated.

+
+ +

Rejecting a custom promise

+ +

We can create a promise that rejects using the reject() method — just like resolve(), this takes a single value, but in this case it is the reason to reject with, i.e., the error that will be passed into the .catch() block.

+ +

Let's extend the previous example to have some reject() conditions as well as allowing different messages to be passed upon success.

+ +

Take a copy of the previous example, and replace the existing timeoutPromise() definition with this:

+ +
function timeoutPromise(message, interval) {
+  return new Promise((resolve, reject) => {
+    if (message === '' || typeof message !== 'string') {
+      reject('Message is empty or not a string');
+    } else if (interval < 0 || typeof interval !== 'number') {
+      reject('Interval is negative or not a number');
+    } else {
+      setTimeout(function(){
+        resolve(message);
+      }, interval);
+    }
+  });
+};
+ +

Here we are passing two arguments into a custom function — a message to do something with, and the time interval to pass before doing the thing. Inside the function we then return a new Promise object — invoking the function will return the promise we want to use.

+ +

Inside the Promise constructor, we do a number of checks inside if ... else structures:

+ +
    +
  1. First of all we check to see if the message is appropriate for being alerted. If it is an empty string or not a string at all, we reject the promise with a suitable error message.
  2. +
  3. Next, we check to see if the interval is an appropriate interval value. If it is negative or not a number, we reject the promise with a suitable error message.
  4. +
  5. Finally, if the parameters both look OK, we resolve the promise with the specified message after the specified interval has passed using setTimeout().
  6. +
+ +

Since the timeoutPromise() function returns a Promise, we can chain .then(), .catch(), etc. onto it to make use of its functionality. Let's use it now — replace the previous timeoutPromise usage with this one:

+ +
timeoutPromise('Hello there!', 1000)
+.then(message => {
+   alert(message);
+})
+.catch(e => {
+  console.log('Error: ' + e);
+});
+ +

When you save and run the code as is, after one second you'll get the message alerted. Now try setting the message to an empty string or the interval to a negative number, for example, and you'll be able to see the promise reject with the appropriate error messages! You could also try doing something else with the resolved message rather than just alerting it.

+ +
+

Note: You can find our version of this example on GitHub as custom-promise2.html (see also the source code).

+
+ +

A more real-world example

+ +

The above example was kept deliberately simple to make the concepts easy to understand, but it is not really very async. The asynchronous nature is basically faked using setTimeout(), although it does still show that promises are useful for creating a custom function with sensible flow of operations, good error handling, etc.

+ +

One example we'd like to invite you to study, which does show a useful async application of the Promise() constructor, is Jake Archibald's idb library. This takes the IndexedDB API, which is an old-style callback-based API for storing and retrieving data on the client-side, and allows you to use it with promises. If you look at the main library file you'll see the same kind of techniques we discussed above being used there. The following block converts the basic request model used by many IndexedDB methods to use promises:

+ +
function promisifyRequest(request) {
+  return new Promise(function(resolve, reject) {
+    request.onsuccess = function() {
+      resolve(request.result);
+    };
+
+    request.onerror = function() {
+      reject(request.error);
+    };
+  });
+}
+ +

This works by adding a couple of event handlers that fulfill and reject the promise at appropriate times:

+ + + +

Conclusion

+ +

Promises are a good way to build asynchronous applications when we don’t know the return value of a function or how long it will take to return. They make it easier to express and reason about sequences of asynchronous operations without deeply nested callbacks, and they support a style of error handling that is similar to the synchronous try...catch statement.

+ +

Promises work in the latest versions of all modern browsers; the only place where promise support will be a problem is in Opera Mini and IE11 and earlier versions.

+ +

We didn't touch on all promise features in this article, just the most interesting and useful ones. As you start to learn more about promises, you'll come across further features and techniques.

+ +

Most modern Web APIs are promise-based, so you'll need to understand promises to get the most out of them. Among those APIs are WebRTC, Web Audio API, Media Capture and Streams, and many more. Promises will be more and more important as time goes on, so learning to use and understand them is an important step in learning modern JavaScript.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/asynchronous/timeouts_and_intervals/index.html b/files/he/learn/javascript/asynchronous/timeouts_and_intervals/index.html new file mode 100644 index 0000000000..2d97d2827d --- /dev/null +++ b/files/he/learn/javascript/asynchronous/timeouts_and_intervals/index.html @@ -0,0 +1,651 @@ +--- +title: 'Cooperative asynchronous JavaScript: Timeouts and intervals' +slug: Learn/JavaScript/Asynchronous/Timeouts_and_intervals +translation_of: Learn/JavaScript/Asynchronous/Timeouts_and_intervals +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}
+ +

+ במאמר זה אנחנו נסתכל על מתודות מסורתיות שיש ב-JavaScript, בעבור הרצה של קוד באופן א-סינכרוני לאחר שזמן מסויים עבר או באינטרוול מסויים (כלומר לקבוע מספר פעמים שירוץ בכל פרק זמן מסויים), נדון בשימושים שלהם ומה הסוגיות הטבועות בהם. + +

+ + + + + + + + + + + + +
ידע מוקדם:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
מטרה: + הבנה של לולאות א-סינכרוניות ואינטרוולים ולמה הם משמים.
+ +

הקדמה

+ +

+ במשך זמן רב, פלטפורמת ה-web העניקה למפתחי JavaScript מספר של פונקציות שאפשרו לה להריץ קוד באופן א-סינכרוני, כך שהקוד ירוץ לאחר זמן מסויים שעבר או להריץ קוד מסויים באופן א-סינכרוני,ע ם הפרש זמן מסויים בין כל ריצה שלו, עד שאנחנו נגיד לו לעצור. אלו הם :

+ +
+
setTimeout()
+
+ מריץ בלוק קוד מסויים פעם אחת לאחר שזמן מסויים עבר.
+
setInterval()
+
+ מריץ בלוק קוד מסויים באופן חוזר, עם הפרש של זמן מסויים בין כל ריצה.
+
requestAnimationFrame()
+
+ זוהי גרסה מודרנית ל-setInterval(). היא מריצה בלוק קוד מסויים לפני שהדפדפן צובע מחדש את התצוגה, כך שמתאפשרת לאניממציה לרוץ במסגרת מתאימה ללא קשר לסביבה בה היא פועלת. +
+
+ +

+ הקוד הא-סינכרוני שנכתב באמצעות פונקציות אלו בעצם ירוץ על ה-00000, אבל כן התאפשר לנו להריץ קוד אחר בין כל ריצה שלהם במידה מסוייתמ, תלוי כמה הפונקציות שנכתוב יעשו שימוש רב במעבד. בכל מקרה, הפונקציות הללו משמשות אנימציות קבועות ועוד תהליכי רקע על אתר אינטרנט או יישום אינטרנטר. בחלקים הבאים של המאמר אנחנו נגע בכל אחת מהן ולמה הן משמשות. .

+ +

setTimeout()

+ +

+ כפי שאמרנו למעלה, setTimeout() מריצה בלוק קוד מסויים פעם אחת, לאחר שזמן מסויים שהגדרנו לה חלף. היא מקבלת את הפרמטרים הבאים:

+ + + +
+

לתשומת לב: + מכיוון ש-0000000 מורצות ביחד, אין כל הבטחה שהן יופעלו במדוייק לאחר הזמן שהגדרנו. במקום, הם ייקראו לאחר שהזמן שהגדרנו חלף, לפחות. + + Because timeout callbacks are executed cooperatively, there's no guarantee that they will be called after exactly the specified amount of time. Instead, they will be called after at least that much time has elapsed. + + + Timeout handlers לא יכולים לרוץ עד אשר ה-main thread מגיע לנקודה בריצה שלו שם הוא עובר על מטפלים אלו למצוא את אלו שהוא צריך להריץ אותם. שה-00000 +

+
+ +

+ בדוגמא הבאה, הדפדפן יחכה שתי שניות לפני שיריץ את הפונקציה האנונימית, ואז יציג את הודעת ה-alert. +e (ראו כדף אינטרנט, וכן את קוד המקור):

+ +
let myGreeting = setTimeout(function() {
+  alert('Hello, Mr. Universe!');
+}, 2000)
+ +

+ הפונקציות שאנחנו מציינים לא חייבות להיות אנונימיות. אנחנו יכולים לתת לפונקציה שלנו שם ואפילו להגדיר אותה במקום אחר, ואז להעביר הפנייה לפונקציה בתוך ה- setTimeout() . הגרסאות הבאות של הקוד שלנו הם שוות לראשונה: + + he functions we specify don't have to be anonymous. We can give our function a name, and can even define it somewhere else and pass a function reference to the setTimeout(). The following two versions of our code snippet are equivalent to the first one:

+ +
// With a named function
+let myGreeting = setTimeout(function sayHi() {
+  alert('Hello, Mr. Universe!');
+}, 2000)
+
+// With a function defined separately
+function sayHi() {
+  alert('Hello Mr. Universe!');
+}
+
+let myGreeting = setTimeout(sayHi, 2000);
+ +

+ זה יכול להיות שימושי כאשר יש לנו פונקציה שצריכה להיקרא/להיות מופעלת גם מתוך -timeout וגם בתגובה לאירוע, לדוגמא. אבל, זה גם יכול לעזור לנו להשאיר את הקוד שלנו מסודר, במיוחד אם אחרי ה-timeout callback יש לנו יותר מכמה שורות קוד. +

+ +

setTimeout() + מחזירה ערך מזהה שיכול לשמש לשם הפנייה לאותו timeout לאחר מכן, אם נרצה לדוגמא לעצור את ה-timeout. ראו {{anch("Clearing timeouts")}} בהמשך על מנת ללמוד כיצד לעשות זאת. + + +

+ +

העברת פרמטרים לפונקציית setTimeout()

+ +

+ כל פרמטר שנרצה להעביר לפונקציה שתרוף בתוך ה-setTimeout(), יהיו חייבים להיות מועברים כפרמטרים נוספוים לפונקציית ה-setTimeout(), וזאת בסוף הרשימת פרמטרים. לדוגמא, אנחנו יכולים כתוב מחדש את הפונקציה הקודמת שלנו כך שהיא תגיד hi לכל שם שיועבר אליה +:

+ +
function sayHi(who) {
+  alert('Hello ' + who + '!');
+}
+ +

+ השם של ה-person יכול להיות מועבר כפרמטר שלישי לתוך ה-setTimeout() + +:

+ +
let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');
+ +

מחיקת Timeouts

+ +

+ לבסוף, אם timeout נוצר, אנחנו יכולים לבטל אותו לפני שהזמן שהגדרנו לו הסתיים באמצעשות שימוש ב-clearTimeout() והעברה של אותו ערך מזהה של ה-setTimeout() כפרמטר. כך, על מנת לבטל את ה-setTimeout() בדוגמא למעלה, אנחנו צריכים לרשום משהו כזה: +

+ +
clearTimeout(myGreeting);
+ +
+

לתשומת לב: ראו greeter-app.html + לדוגמא יותר מעניינת אשר מאפשרת לכן לקבוע את השם של האדם שנגיד לו שלום בטופס, ואז לבטל את הברכה באמצעו תכפתור אחג. (קוד המקור).

+
+ +

setInterval()

+ +

setTimeout() עובדת מצוים כאשר אנחנו צריכים להריץ בלוק קוד מסויים פעם אחת לאחר אינטרוול זמן שעבר. אבל מה קורה כאשר אנחנו רוצים להריץ קוד מסויים שוב ושוב, כמו במקרה של אנימציות?

+ +

כאן נכנס לתמונה setInterval() . + פונקציה זו עובדת בצורה דומה ל-setTimeout(), למעט העובדה שהפונקציה שאנחנו מעבירים כפרמטר ראשון, תרוץ באופן חוזר ונשנה לכל הפחות בכל משך הזמן שהוגדר לה (גם כן במילישניות), ולא רק פעם אחת. אנחנו גם יכולים להעביר לה את הפרמטרים הדרושים לפונקציה שתרוץ. +.

+ +

+ נראה דוגמא על מנת להמחיש את העניין. הפונקציה הבא יוצרת אובייקט Date() חדש, מחלצת חרוזת זמן מתוכו באמצעות שימוש ב- toLocaleTimeString() ציגה את זה לממשק המתשמש. לאחר מכן אנחנו מריצים פונקציה אחת לשנייה באמצעות שימוש ב-setInterval() + על מנת ליצור את האפשר של שעון דיגיטלי שמעדכן את עצמו כל שנייה. + (ראו כדף אינטרנט, ואת also קוד המקור):

+ +
function displayTime() {
+   let date = new Date();
+   let time = date.toLocaleTimeString();
+   document.getElementById('demo').textContent = time;
+}
+
+const createClock = setInterval(displayTime, 1000);
+ +

בדיוק כמו setTimeout(), setInterval() מחזירה ערך מזהה כך שאנחנו יכולים להשתמש בו בהמשך על מנת למחוק את ה-interval.

+ +

מחיקת intervals

+ +

setInterval() תמשיך לרוץ באופן מתמשך וקבוע, אלא אם אנחנו נעשה איתה משהו - + אנחנו נרצה אולי דרך לעצור משימות כאלו, אחרת אנחנו נקבל שגיאות השהדפדפן לא מצליח להשלים גרסאות נוספות של המשימה הזו, או שהאניממציה שמטופלת על ידי המשימה הזו הסתיימה. אנחנו יכולים לעשות זאת באותה דרך שבה אנחנו מסיימים setInterval() - באמצעות העבר הערך המזהה שהוחזר לנו בהפעלה של setInterval() לפונקציה clearInterval(): + +

+ +
const myInterval = setInterval(myFunction, 2000);
+
+clearInterval(myInterval);
+ +

למידה עצמאית: יצירה של שעון עצר

+ +

לאחר שעברנו על פונקציות אלו, נסו לאתגר את עצמכם במשימה זו. עשו עותקשל הדוגמא שלנו שנמצא ב- setInterval-clock.html ושנו אותה כך שתהיה שעון סטופר.

+ +

+ אתם צריכים להציג את הזמן כמו בדוגמא הקודמת, רק שבתרגיל זה אתם תצטרכו:

+ + + +

רמזים:

+ + + +
+

Note: If you get stuck, you can find our version here (see the source code also).

+
+ +

Things to keep in mind about setTimeout() and setInterval()

+ +

There are a few things to keep in mind when working with setTimeout() and setInterval(). Let's review these now.

+ +

Recursive timeouts

+ +

There is another way we can use setTimeout(): We can call it recursively to run the same code repeatedly, instead of using setInterval().

+ +

The below example uses a recursive setTimeout() to run the passed function every 100 milliseconds:

+ +
let i = 1;
+
+setTimeout(function run() {
+  console.log(i);
+  i++;
+  setTimeout(run, 100);
+}, 100);
+ +

Compare the above example to the following one — this uses setInterval() to accomplish the same effect:

+ +
let i = 1;
+
+setInterval(function run() {
+  console.log(i);
+  i++
+}, 100);
+ +

How do recursive setTimeout() and setInterval() differ?

+ +

The difference between the two versions of the above code is a subtle one.

+ + + +

When your code has the potential to take longer to run than the time interval you’ve assigned, it’s better to use recursive setTimeout() — this will keep the time interval constant between executions regardless of how long the code takes to execute, and you won't get errors.

+ +

Immediate timeouts

+ +

Using 0 as the value for setTimeout() schedules the execution of the specified callback function as soon as possible but only after the main code thread has been run.

+ +

For instance, the code below (see it live) outputs an alert containing "Hello", then an alert containing "World" as soon as you click OK on the first alert.

+ +
setTimeout(function() {
+  alert('World');
+}, 0);
+
+alert('Hello');
+ +

This can be useful in cases where you want to set a block of code to run as soon as all of the main thread has finished running — put it on the async event loop, so it will run straight afterwards.

+ +

Clearing with clearTimeout() or clearInterval()

+ +

clearTimeout() and clearInterval() both use the same list of entries to clear from. Interestingly enough, this means that you can use either method to clear a setTimeout() or setInterval().

+ +

For consistency, you should use clearTimeout() to clear setTimeout() entries and clearInterval() to clear setInterval() entries. This will help to avoid confusion.

+ +

requestAnimationFrame()

+ +

requestAnimationFrame() is a specialized looping function created for running animations efficiently in the browser. It is basically the modern version of setInterval() — it executes a specified block of code before the browser next repaints the display, allowing an animation to be run at a suitable frame rate regardless of the environment it is being run in.

+ +

It was created in response to perceived problems with setInterval(), which for example doesn't run at a frame rate optimized for the device, sometimes drops frames, continues to run even if the tab is not the active tab or the animation is scrolled off the page, etc. Read more about this on CreativeJS.

+ +
+

Note: You can find examples of using requestAnimationFrame() elsewhere in the course — see for example Drawing graphics, and Object building practice.

+
+ +

The method takes as an argument a callback to be invoked before the repaint. This is the general pattern you'll see it used in:

+ +
function draw() {
+   // Drawing code goes here
+   requestAnimationFrame(draw);
+}
+
+draw();
+ +

The idea is that you define a function in which your animation is updated (e.g. your sprites are moved, score is updated, data is refreshed, or whatever), then you call it to start the process off. At the end of the function block you call requestAnimationFrame() with the function reference passed as the parameter, and this instructs the browser to call the function again on the next display repaint. This is then run continuously, as we are calling requestAnimationFrame() recursively.

+ +
+

Note: If you want to perform some kind of simple constant DOM animation, CSS Animations are probably faster as they are calculated directly by the browser's internal code rather than JavaScript. If however you are doing something more complex and involving objects that are not directly accessible inside the DOM (such as 2D Canvas API or WebGL objects), requestAnimationFrame() is the better option in most cases.

+
+ +

How fast does your animation run?

+ +

The smoothness of your animation is directly dependent on your animation's frame rate and it is measured in frames per second (fps). The higher this number is, the smoother your animation will look, to a point.

+ +

Since most screens have a refresh rate of 60Hz, the fastest frame rate you can aim for is 60 frames per second (FPS) when working with web browsers. However, more frames means more processing, which can often cause stuttering and skipping — also known as dropping frames, or jank.

+ +

If you have a monitor with a 60Hz refresh rate and you want to achieve 60 FPS you have about 16.7 milliseconds (1000 / 60) to execute your animation code to render each frame. This is a reminder that we need to be mindful of the amount of code that we try to run for each pass through the animation loop.

+ +

requestAnimationFrame() always tries to get as close to this magic 60 FPS value as possible, although sometimes it isn't possible — if you have a really complex animation and you are running it on a slow computer, your frame rate will be less. requestAnimationFrame() will always do the best it can with what it has available.

+ +

How does requestAnimationFrame() differ from setInterval() and setTimeout()?

+ +

Let's talk a little bit more about how the requestAnimationFrame() method differs from the other methods we looked at earlier. Looking at our code from above:

+ +
function draw() {
+   // Drawing code goes here
+   requestAnimationFrame(draw);
+}
+
+draw();
+ +

Let's now see how we'd do the same thing using setInterval():

+ +
function draw() {
+   // Drawing code goes here
+}
+
+setInterval(draw, 17);
+ +

As we said before, we don't specify a time interval for requestAnimationFrame(); it just runs it as quickly and smoothly as possible in the current conditions. The browser also doesn't waste time running it if the animation is offscreen for some reason, etc.

+ +

setInterval() on the other hand requires an interval to be specified. We arrived at our final value of 17 via the formula 1000 milliseconds / 60Hz, and then rounded it up. Rounding up is a good idea, as if you rounded down the browser might try to run the animation faster than 60fps, and it wouldn't make any difference to the smoothness of the animation anyway. As we said before, 60Hz is the standard refresh rate.

+ +

Including a timestamp

+ +

The actual callback passed to the requestAnimationFrame() function can be given a parameter too — a timestamp value that represents the time since the requestAnimationFrame() started running. This is useful as it allows you to run things at specific times and at a constant pace, regardless of how fast or slow your device might be. The general pattern you'd use looks something like this:

+ +
let startTime = null;
+
+function draw(timestamp) {
+    if(!startTime) {
+      startTime = timestamp;
+    }
+
+   currentTime = timestamp - startTime;
+
+   // Do something based on current time
+
+   requestAnimationFrame(draw);
+}
+
+draw();
+ +

Browser support

+ +

requestAnimationFrame() is supported in slightly more recent browsers than setInterval()/setTimeout() — most interestingly it is available in Internet Explorer 10 and above. So unless you need to support older versions of IE with your code, there is little reason to not use requestAnimationFrame().

+ +

A simple example

+ +

Enough with the theory; let's go through and build our own requestAnimationFrame() example. We're going to create a simple "spinner animation", the kind you might see displayed in an app when it is busy connecting to the server, etc.

+ +
+

Note: In a real world example, you should probably use CSS animations to run this kind of simple animation. However, this kind of example is very useful to demonstrate requestAnimationFrame() usage, and you'd be more like to use this kind of technique when doing something more complex such as updating the display of a game on each frame.

+
+ +
    +
  1. +

    First of all, grab a basic HTML template such as this one.

    +
  2. +
  3. +

    Put an empty {{htmlelement("div")}} element inside the {{htmlelement("body")}}, then add a ↻ character inside it. This is a circular arrow character that will act as our spinner for this simple example.

    +
  4. +
  5. +

    Apply the following CSS to the HTML template in whatever way you prefer. This sets a red background on the page, sets the <body> height to 100% of the {{htmlelement("html")}} height, and centers the <div> inside the <body>, horizontally and vertically.

    + +
    html {
    +  background-color: white;
    +  height: 100%;
    +}
    +
    +body {
    +  height: inherit;
    +  background-color: red;
    +  margin: 0;
    +  display: flex;
    +  justify-content: center;
    +  align-items: center;
    +}
    +
    +div {
    +  display: inline-block;
    +  font-size: 10rem;
    +}
    +
  6. +
  7. +

    Insert a {{htmlelement("script")}} element just above the </body> tag.

    +
  8. +
  9. +

    Insert the following JavaScript inside your <script> element. Here we're storing a reference to the <div> inside a constant, setting a rotateCount variable to 0, setting an uninitialized variable that will later be used to contain a reference to the requestAnimationFrame() call, and setting a startTime variable to null, which will later be used to store the start time of the requestAnimationFrame().

    + +
    const spinner = document.querySelector('div');
    +let rotateCount = 0;
    +let startTime = null;
    +let rAF;
    +
    +
  10. +
  11. +

    Below the previous code, insert a draw() function that will be used to contain our animation code, which includes the timestamp parameter:

    + +
    function draw(timestamp) {
    +
    +}
    +
  12. +
  13. +

    Inside draw(), add the following lines. Here we define the start time if it is not defined already (this will only happen on the first loop iteration), and set the rotateCount to a value to rotate the spinner by (the current timestamp, take the starting timestamp, divided by three so it doesn't go too fast):

    + +
      if (!startTime) {
    +   startTime = timestamp;
    +  }
    +
    +  rotateCount = (timestamp - startTime) / 3;
    +
    +
  14. +
  15. +

    Below the previous line inside draw(), add the following block — this checks to see if the value of rotateCount is above 359 (e.g. 360, a full circle). If so, it sets the value to its modulo of 360 (i.e. the remainder left over when the value is divided by 360) so the circle animation can continue uninterrupted, at a sensible, low value. Note that this isn't strictly necessary, but it is easier to work with values of 0-359 degrees than values like "128000 degrees".

    + +
    if (rotateCount > 359) {
    +  rotateCount %= 360;
    +}
    +
  16. +
  17. Next, below the previous block add the following line to actually rotate the spinner: +
    spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
    +
  18. +
  19. +

    At the very bottom inside the draw() function, insert the following line. This is the key to the whole operation — we are setting the variable we defined earlier to an active requestAnimation() call that takes the draw() function as its parameter. This starts the animation off, constantly running the draw() function at a rate of as close to 60 FPS as possible.

    + +
    rAF = requestAnimationFrame(draw);
    +
  20. +
+ +
+

Note: You can find this example live on GitHub (see the source code also).

+
+ +

Clearing a requestAnimationFrame() call

+ +

Clearing a requestAnimationFrame() call can be done by calling the corresponding cancelAnimationFrame() method (note, "cancel" not "clear" as with the "set..." methods), passing it the value returned by the requestAnimationFrame() call to cancel, which we stored in a variable called rAF:

+ +
cancelAnimationFrame(rAF);
+ +

Active learning: Starting and stopping our spinner

+ +

In this exercise, we'd like you to test out the cancelAnimationFrame() method by taking our previous example and updating it, adding an event listener to start and stop the spinner when the mouse is clicked anywhere on the page.

+ +

Some hints:

+ + + +
+

Note: Try this yourself first; if you get really stuck, check out of our live example and source code.

+
+ +

Throttling a requestAnimationFrame() animation

+ +

One limitation of requestAnimationFrame() is that you can't choose your frame rate. This isn't a problem most of the time, as generally you want your animation to run as smoothly as possible, but what about when you want to create an old school, 8-bit-style animation?

+ +

This was a problem for example in the Monkey Island-inspired walking animation from our Drawing Graphics article:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}

+ +

In this example we have to animate both the position of the character on the screen, and the sprite being shown. There are only 6 frames in the sprite's animation; if we showed a different sprite frame for every frame displayed on the screen by requestAnimationFrame(), Guybrush would move his limbs too fast and the animation would look ridiculous. We therefore throttled the rate at which the sprite cycles its frames using the following code:

+ +
if (posX % 13 === 0) {
+  if (sprite === 5) {
+    sprite = 0;
+  } else {
+    sprite++;
+  }
+}
+ +

So we are only cycling a sprite once every 13 animation frames. OK, so it's actually about every 6.5 frames, as we update posX (character's position on the screen) by two each frame:

+ +
if(posX > width/2) {
+  newStartPos = -((width/2) + 102);
+  posX = Math.ceil(newStartPos / 13) * 13;
+  console.log(posX);
+} else {
+  posX += 2;
+}
+ +

This is the code that works out how to update the position in each animation frame.

+ +

The method you use to throttle your animation will depend on your particular code. For example, in our spinner example we could make it appear to move slower by only increasing our rotateCount by one on each frame instead of two.

+ +

Active learning: a reaction game

+ +

For our final section of this article, we'll create a 2-player reaction game. Here we have two players, one of whom controls the game using the A key, and the other with the L key.

+ +

When the Start button is pressed, a spinner like the one we saw earlier is displayed for a random amount of time between 5 and 10 seconds. After that time, a message will appear saying "PLAYERS GO!!" — once this happens, the first player to press their control button will win the game.

+ +

{{EmbedGHLiveSample("learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html", '100%', 500)}}

+ +

Let's work through this.

+ +
    +
  1. +

    First of all, download the starter file for the app — this contains the finished HTML structure and CSS styling, giving us a game board that shows the two players' information (as seen above), but with the spinner and results paragraph displayed on top of one another. We just have to write the JavaScript code.

    +
  2. +
  3. +

    Inside the empty {{htmlelement("script")}} element on your page, start by adding the following lines of code that define some constants and variables we'll need in the rest of the code:

    + +
    const spinner = document.querySelector('.spinner p');
    +const spinnerContainer = document.querySelector('.spinner');
    +let rotateCount = 0;
    +let startTime = null;
    +let rAF;
    +const btn = document.querySelector('button');
    +const result = document.querySelector('.result');
    + +

    In order, these are:

    + +
      +
    1. A reference to our spinner, so we can animate it.
    2. +
    3. A reference to the {{htmlelement("div")}} element that contains the spinner, used for showing and hiding it.
    4. +
    5. A rotate count — how much we want to show the spinner rotated on each frame of the animation.
    6. +
    7. A null start time — will be populated with a start time when the spinner starts spinning.
    8. +
    9. An uninitialized variable to later store the {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}} call that animates the spinner.
    10. +
    11. A reference to the Start button.
    12. +
    13. A reference to the results paragraph.
    14. +
    +
  4. +
  5. +

    Next, below the previous lines of code, add the following function. This simply takes two numerical inputs and returns a random number between the two. We'll need this to generate a random timeout interval later on.

    + +
    function random(min,max) {
    +  var num = Math.floor(Math.random()*(max-min)) + min;
    +  return num;
    +}
    +
  6. +
  7. +

    Next add in the draw() function, which animates the spinner. This is exactly the same as the version seen in the simple spinner example we looked at earlier:

    + +
      function draw(timestamp) {
    +    if(!startTime) {
    +     startTime = timestamp;
    +    }
    +
    +    let rotateCount = (timestamp - startTime) / 3;
    +    spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
    +
    +    if(rotateCount > 359) {
    +      rotateCount -= 360;
    +    }
    +
    +    rAF = requestAnimationFrame(draw);
    +  }
    +
  8. +
  9. +

    Now it is time to set up the initial state of the app when the page first loads. Add the following two lines, which simply hide the results paragraph and spinner container using display: none;.

    + +
    result.style.display = 'none';
    +spinnerContainer.style.display = 'none';
    +
  10. +
  11. +

    We'll also define a reset() function, which sets the app back to the original state required to start the game again after it has been played. Add the following at the bottom of your code:

    + +
    function reset() {
    +  btn.style.display = 'block';
    +  result.textContent = '';
    +  result.style.display = 'none';
    +}
    +
  12. +
  13. +

    OK, enough preparation.  Let's make the game playable! Add the following block to your code. The start() function calls draw() to start the spinner spinning and display it in the UI, hides the Start button so we can't mess up the game by starting it multiple times concurrently, and runs a setTimeout() call that runs a setEndgame() function after a random interval between 5 and 10 seconds has passed. We also add an event listener to our button to run the start() function when it is clicked.

    + +
    btn.addEventListener('click', start);
    +
    +function start() {
    +  draw();
    +  spinnerContainer.style.display = 'block';
    +  btn.style.display = 'none';
    +  setTimeout(setEndgame, random(5000,10000));
    +}
    + +
    +

    Note: You'll see that in this example we are calling setTimeout() without storing the return value (so not let myTimeout = setTimeout(functionName, interval)). This works and is fine, as long as you don't need to clear your interval/timeout at any point. If you do, you'll need to save the returned identifier.

    +
    + +

    The net result of the previous code is that when the Start button is pressed, the spinner is shown and the players are made to wait a random amount of time before they are then asked to press their button. This last part is handled by the setEndgame() function, which we should define next.

    +
  14. +
  15. +

    So add the following function to your code next:

    + +
    function setEndgame() {
    +  cancelAnimationFrame(rAF);
    +  spinnerContainer.style.display = 'none';
    +  result.style.display = 'block';
    +  result.textContent = 'PLAYERS GO!!';
    +
    +  document.addEventListener('keydown', keyHandler);
    +
    +  function keyHandler(e) {
    +    console.log(e.key);
    +    if(e.key === 'a') {
    +      result.textContent = 'Player 1 won!!';
    +    } else if(e.key === 'l') {
    +      result.textContent = 'Player 2 won!!';
    +    }
    +
    +    document.removeEventListener('keydown', keyHandler);
    +    setTimeout(reset, 5000);
    +  };
    +}
    + +

    Stepping through this:

    + +
      +
    1. First we cancel the spinner animation with {{domxref("window.cancelAnimationFrame", "cancelAnimationFrame()")}} (it is always good to clean up unneeded processes), and hide the spinner container.
    2. +
    3. Next we display the results paragraph and set its text content to "PLAYERS GO!!" to signal to the players that they can now press their button to win.
    4. +
    5. We then attach a keydown event listener to our document — when any button is pressed down, the keyHandler() function is run.
    6. +
    7. Inside keyHandler(), we include the event object as a parameter (represented by e) — its {{domxref("KeyboardEvent.key", "key")}} property contains the key that was just pressed, and we can use this to respond to specific key presses with specific actions.
    8. +
    9. We first log e.key to the console, which is a useful way of finding out the key value of different keys you are pressing.
    10. +
    11. When e.key is "a", we display a message to say that Player 1 won, and when e.key is "l", we display a message to say Player 2 won. Note that this will only work with lowercase a and l — if an uppercase A or L is submitted (the key plus Shift), it is counted as a different key.
    12. +
    13. Regardless of which one of the player control keys was pressed, we remove the keydown event listener using {{domxref("EventTarget.removeEventListener", "removeEventListener()")}} so that once the winning press has happened, no more keyboard input is possible to mess up the final game result. We also use setTimeout() to call reset() after 5 seconds — as we explained earlier, this function resets the game back to its original state so that a new game can be started.
    14. +
    +
  16. +
+ +

That's it, you're all done.

+ +
+

Note: If you get stuck, check out our version of the reaction game (see the source code also).

+
+ +

Conclusion

+ +

So that's it — all the essentials of async loops and intervals covered in one article. You'll find these methods useful in a lot of situations, but take care not to overuse them — since these still run on the main thread, heavy and intensive callbacks (especially those that manipulate the DOM) can really slow down a page if you're not careful.

+ +

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}

+ + +

במודול זה

+ + diff --git a/files/he/learn/javascript/building_blocks/build_your_own_function/index.html b/files/he/learn/javascript/building_blocks/build_your_own_function/index.html new file mode 100644 index 0000000000..47df4e6b3a --- /dev/null +++ b/files/he/learn/javascript/building_blocks/build_your_own_function/index.html @@ -0,0 +1,247 @@ +--- +title: בניית פונקציה משלנו +slug: Learn/JavaScript/Building_blocks/Build_your_own_function +translation_of: Learn/JavaScript/Building_blocks/Build_your_own_function +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}
+ +

לאחר שסיימנו את רוב התיאוריה במאמר הקודם, מאמר זה נועד להעניק לכם ניסיון מעשי. אנחנו ננסה להעניק לכם כלים לבנות את הפונקציה שלכם. במהלך הדרך, אנחנו נסביר כמה פרטים שימושיים בקשר לשימוש בפונקציות.

+ + + + + + + + + + + + +
ידע מוקדם:הכרות בסיסית עם המחשב ועם הבסיס של HTML ו- CSS, וכן סיום במלאו של מודול צעדים ראשונים ב-JavaScript., Functions — פונקציות - חלקי קוד לשימוש חוזר.
מטרה:לספק לכם כלים לבניית פונקציות ולהסביר פרטים שימושיים בהקשר של בניית פונקציות.
+ +

למידה עצמאית: בניית פונקציה

+ +

הפונקציה שנראה לבנות תיקרא displayMessage(). היא תציג הודעה על גבי דף אינטרנט, אשר תחליף את הפונקציה המובנית של הדפדפן alert(). ראינו משהו דומה לפני כן,על מנת לרענן את הזכרון, הדפיסו את הקוד הבא בקונסולה

+ +
alert('This is a message');
+ +

הפונקציה alert מקבל פרמטר אחד כארגומנט - המחרוזת ששתוצג על גבי תיבת ההודעה. נא לשנות המחרוזת על מנת לשנות את ההודעהf

+ +

הפונקציה alert היא מוגבלת: אנחנו יכולים לשנות את הטקסט, אבל אנחנו לא ממש יכולים לשנות בקלות את כל היתר כמו צבעים, אייקון, או כל דבר אחר. אנו נבנבה פונקציה שתאפשר לנו את הדברים הללו.

+ +
+

לתשומת לב דוגמא זו אמורה לעבוד בכל הדפדפדנים המודרניים, אבל העיצוב עלול להיראות קצת מוזר בדפדפנים ישנים. אנחנו ממליצים ללכם להתשמשמ בדפדפן מודרני כגון פיירפוקס, כרום או אופרה לשם תרגול זה.

+
+ +

הפונקציה הבסיסית

+ +

על מנת להתחיל, בואו נבנה פונקציה בסיסית

+ +
+

לתשומת לב: הכללים בנושא מתן שמות לפונקציה הם דומים לכללים בנושא מתן שמות למשתנים. יחד עם זאת, בפונקציות, לאחר השם יבואו סוגריים רגילות () ואילו במשתנים לא.

+
+ +
    +
  1. התחילו בכך שתיצרו עותק מקומי של הקובץ function-start.html. בקובץ אתם תיראו שה-body כולל כפתור אחד. בנוסף הוספנו גם כמה כללי css פשוטים על מנת לעצב את תיבת ההודעה שלנו וכן אלממנט {{htmlelement("script")}} ריקה שבה נוסיף את קוד ה-JavaScriipt שלנו.
  2. +
  3. לאחר מכן, הוסיפו הקוד הבא בתוך האלמנט <script>: +
    function displayMessage() {
    +
    +}
    + אנחנו מתחילים עם המילה השמורה function, אשר משמעותה היא שאנחנו מגדירים פונקציה. לאחר מכן, אנחנו רושמים את שם הפונקציה שנרצה לתת לה, ולאחר מכן סוגריים רגילות () ולאחריהן סוגריים מסולסלות {....}. הפרמטרים שנרצה לתת לפונקציה שלנו - הארגומנטים, ייכנסו בתוך הסוגריים הרגילות (ובמידה שיש לנו כמה ארגומנטים, נפריד ביניהם באמצעות פסיק , ורווח.) את הקוד שנרצה שהפונקציה תריץ בכל פעם שנקרא לה אנחנו נשים בתוך הסוגריים המסולסות {....}.
  4. +
  5. לבסוף, הוסיפו את התקוד הבא בתוך הסוגריים המסולסלות +
    var html = document.querySelector('html');
    +
    +var panel = document.createElement('div');
    +panel.setAttribute('class', 'msgBox');
    +html.appendChild(panel);
    +
    +var msg = document.createElement('p');
    +msg.textContent = 'This is a message box';
    +panel.appendChild(msg);
    +
    +var closeBtn = document.createElement('button');
    +closeBtn.textContent = 'x';
    +panel.appendChild(closeBtn);
    +
    +closeBtn.onclick = function() {
    +  panel.parentNode.removeChild(panel);
    +}
    +
  6. +
+ +

נעבור על הקוד שהוספנו שורה אחר שורה

+ +

השורה הראשונה משתמשת בפונקציה של DOM API הנקראת {{domxref("document.querySelector()")}}, על מנת לבחור אלמנט מסוג {{htmlelement("html")}} ולאחסן הפנייה אליו בתוך משתנה שנקרא html, כדי שנוכל לבצע איתו דברים מאוחר יותר:

+ +
var html = document.querySelector('html');
+ +

החלק הבא בקוד שלנו משתמש בפונקציה נוספת של DOM API הנקראת {{domxref("document.createElement()")}} על מנת ליצור אלמנט מסוג {{htmlelement("div")}} ואנו מאחסנים הפניה אליו בתוך המשתנה שנקרא panel. האלמנט הזה יהיה הקונטיינר החיצוני של תיבת ההודעה שלנו.

+ +

לאחר מכן אנחנו משתמשים בפונקציה נוספת של DOM API שנקראת class על מנת לקבוע {{domxref("Element.setAttribute()")}} למשתנה msgBox שלנו עם הערך msgBox. זה אמור להקל עלינו לעצב את האלמנט - אם תסתכלו ב-CSS שבדךף, אתם תראו שאנחנו משתשמים בסלקטור של CSS .msgBox על מנת לעצב את תיבת ההודעה והתוכן שלה.

+ +

לבסוף - אנחנו קוראים לפונקציית DOM נוספת שנקראת {{domxref("Node.appendChild()")}} על גבי המשתנה html , אשר משרשרת את האלמנט שהיא מקבלת כארגומנט לאלמנט שהיא הופעלה עליו. כאן אנחנו מציינים שאנחנו רוצים שהאלמנט <div> יהיה אלמנט ילד של האלמנט <html>. אחנו צריכים להוסיף פונקציה זו בכל פעם שאנחנו משתמשים בפונקציה {{domxref("document.createElement()")}}, מכיוון שהאלמנט שיצרנו לא יופיע בעצמו ולא יתחבר בעצמו לאלמנט אחר - אנחנו צריכים לציין איפה אנחנו רוצים לשים אותו ולמי הוא יהיה קשור.

+ +
var panel = document.createElement('div');
+panel.setAttribute('class', 'msgBox');
+html.appendChild(panel);
+ +

ב-2 החלקים הבאים אנחנו עושים שימוש באותן פונקציות createElement() ן- appendChild() שהשתמשנו בהן על מנת ליצור שני אלמנטים חדשים: {{htmlelement("p")}} ו- {{htmlelement("button")}} — ומכניסים אותם בתוך הדף כאלמנטים ילדים של <div> panel. לאחר מכן אנחנו משתמשים בפרופ׳ {{domxref("Node.textContent")}} - אשר מייצג בעצם את תוכן הטקסט של אלמנט - על מנת להכניס הודעה בתוך הפסקה וכן אנחנו מוסיפים את התו 'x' בתוך הכפתור. הכפתור הזה צריך להיות לחוץ/מופעל כשהמשתמש רוצה לסגור את תיבת ההודעה.

+ +
var msg = document.createElement('p');
+msg.textContent = 'This is a message box';
+panel.appendChild(msg);
+
+var closeBtn = document.createElement('button');
+closeBtn.textContent = 'x';
+panel.appendChild(closeBtn);
+ +

לבסוף, אנחנו משתמשים במאזין אירוע ומטפל אירוע - {{domxref("GlobalEventHandlers.onclick")}} - על מנת להוסיף האזנה להתרחשות אירוע של לחיצה (במקרה הזה לחיצה על ) closeBtn וטיפול באותו אירוע על ידי הפונקציה האנונימית המכילה קוד שמוחק את כל ה-panel מהדף - כלומר מוחק את תיבת ההודעה.

+ +

בקצרה, מאזין האירוע onclick הוא פרופ׳ שזמין עבור כפתור (ובעצם זמין עבור כל אלמנט בדף) שיכול להיות מוגדר לפונקציה שתבצע קוד מסויים כאשר הכפתור/האלמנט נלחץ. אנחנו נלמד על כך לעומק במאמר שלנו בנושא אירועים. שימו לב שוב שמטפל האירוע של מאזין האירוע onclick שווה לפונקציה אנונימית, אשר מכילה קוד שירוץ כאשר הכפתור נלחץ. שורת הקוד שבתוך הפונקציה האנונימית משתמש בפונקציה של ה- DOM API בשם {{domxref("Node.removeChild()")}} על מנת להסיר אלמנט בן של אלמנט - במקרה זה של אלמנט <div>.

+ +
closeBtn.onclick = function() {
+  panel.parentNode.removeChild(panel);
+}
+ +

בעיקרון - כל הקוד הזה פשוט יוצר בלוק של HTML שנראה כך ומכניס את זה לתוך הדף:

+ +
<div class="msgBox">
+  <p>This is a message box</p>
+  <button>x</button>
+</div>
+ +

זה היה הרבה קוד לעבור עליו - אל תדאגו אם אתם לא זוכרים במדויק איך הוא עובד כרגע. הרעיון המרכזי שאנחנו רוצים לשים עליו דגש כאן הוא הפונקציה, המבנה שלה והשימוש שלה.

+ +

קריאה לפונקציה/הפעלת הפונקציה

+ +

כעת יש לנו את הגדרת הפונקציה בתוך האלמנט 555555, אבל היא לא תבצע כלום אם לא נקרא לה.

+ +
    +
  1. נסו לכלול את הקוד הבא מחת לפונקציה על מנת לקרוא לה: +
    displayMessage();
    + שורה זו בעצם קוראת פונקציה (Invoking the function) וגורמת לה לרוץ מיד. כאשר תשמרו את הקוד ותעלו מחדש את הדף בדפדפן, אתם תראו שתיבת ההודעה הופיע מיד, פעם אחת. אחרי הכל, קראנו לפונקציה רק פעם אחת.
  2. +
  3. +

    כעת, פתחו את הקונסולה והדפיסו את השורה למעלה שוב, ואתם תראו את ההודעה שוב. כלומר בנינו פונקציה שאנחנו יכולים להשתמש בה שוב ושוב.

    + +

    רוב הסיכויים שנרצה שתיבת ההודעה תופיע כתגובה לאירועים מסויימים של המערכת או של המשתמש. ביישומים אמיתיים, תיבת הודעה שכזו תיקרא בתגובה למידע חדש שזמין או לשגיאה שנוצרה, או כאשר המשתמש מוחק את הפרופיל שלו (״האם אתה בטוח רוצה למחוק את הפרופיל? וכד׳).

    + +

    בתרגול זה, אנחנו נגרום לתיבת ההודעה להופיע כאשר המשתמש לוחץ על כפתור.

    +
  4. +
  5. מחקו את השורה שהוספתם.
  6. +
  7. כעת, אנחנו נבחר את הכפתור ונאחסן הפניה אליו בתוך משתנה. הוסיפו את הקוד הבא לקוד שלהם, מעל הגדרת הפונקציה : +
    var btn = document.querySelector('button');
    +
  8. +
  9. לבסו, הוסיפו את הקוד הבא, מתחת לקוד שהוספתם בסעיף הקודם: +
    btn.onclick = displayMessage;
    + בדרך דומה ל ...closeBtn.onclick בתוך הפונקציה, כאן אנחנו קוראים קוד מסויים בתגובה כך שהכפתור נלחץ. אבל במקרה הזה במקום לקרוא לפונקציה אנונימית המכילה את אותו קוד, אנחנו קוראים לפונקציה שלנו בשמה באופן מפורש.
  10. +
  11. נסו לשמור ולרענן את הדף - אתם אמורים לראות את תיבת ההודעה כאשר הכפתור נלחץ.
  12. +
+ +

אתם בטח תוהים מדוע לא כללנו סוגריים () אחרי שם הפונקציה. הסיבה היא שאנחנו לא רוצים לקרוא לפונקציה ישר - אלא רק אחרי שהכפתור נלחץ. אם תשנו את השורה הבא כך:

+ +
btn.onclick = displayMessage();
+ +

ותרעננו את הדף, אתם תיראו שהתיבת ההודעה הופיע מבלי שלחצנו על הכפתור. הסוגריים () בהקשר זה לפעמים נקראים "function invocation operator". אנחנו משתמשים בהם כאשר אנחנו רוצים להריץ את הפונקציה באופן מיידי בתוך הסקופ הנוכחי. באותו דרך, הקוד שבתוך הפונקציה האנונימית לא ירוץ ישר, שכן הוא נמצא בתוך הסקופ של הפונקציה בלבד - שכאמור תופעל רק כאשר כפתור יילחץ.

+ +

אם ניסיתם את התרגול הקודם של הוספת (), אנא וודאו שמחקתם אותם לפני שאתם ממשיכים

+ +

שדרוג הפונקציה שלנו באמצעות פרמטרים/ארגומנטים

+ +

כרגע, הפונקציה שלנו לא ממש שימושית - אנחנו לא רוצים סתם להציג הודעה זהה בכל פעם. ננסה לשדרג אוהת על ידי כך שנוסיף מספר פרמטרים, אשר יאפשרו לנו לקרוא לה, אך עם אפשרויות שונות בכל קריאה.

+ +
    +
  1. תחילה, עדכנו את השורה הראשונה של הפונקציה: +
    function displayMessage() {
    + +
    לקוד הבא:
    + +
    function displayMessage(msgText, msgType) {
    + כעת, כאשר אנחנו קוראים לפונקציה, אנחנו מספקים לה שני ערכים של משתנים בתוך הסוגריים () על מנת לציין את תוכן ההודעה שתוצג ואת סוג ההודעה.
  2. +
  3. על מנת לעשות שימוש בפרמטר הראשון, עדכנו את הקוד שבתוך הפונקציה : +
    msg.textContent = 'This is a message box';
    + +
    לקוד הבא
    + +
    msg.textContent = msgText;
    +
  4. +
  5. לבסוף, אנחנו נרצה לעדכן כעת את הפונקציה כך שתכלול הודעה מעודכנת . שנה את השורה הבאה: +
    btn.onclick = displayMessage;
    + +
    לקוד הבא :
    + +
    btn.onclick = function() {
    +  displayMessage('Woo, this is a different message!');
    +};
    + If we want to specify parameters inside parentheses for the function we are calling, then we can't call it directly — we need to put it inside an anonymous function so that it isn't in the immediate scope and therefore isn't called immediately. Now it will not be called until the button is clicked.
  6. +
  7. עכשיו טען מחדש ונסה שוב את הקוד ותראה שהוא עדיין עובד כמו מקודם ,רק שעכשיו גם התווסף אפשרות לשנות את ההודעה שבתוך הפרמטר כדי להציג הודעות שונות בתיבה! 
  8. +
+ +

פרמטר מורכב יותר

+ +

הלאה לפרמטר הבא. זו תכלול עבודה מעט יותר - אנו מתכוונים להגדיר אותה כך שלפי הפרמטר msgType, הפונקציה תציג אייקון אחר וצבע רקע שונה.

+ +
    +
  1. First of all, download the icons needed for this exercise (warning and chat) from GitHub. Save them in a new folder called icons in the same location as your HTML file. + +
    Note: The warning and chat icons were originally found on iconfinder.com, and designed by Nazarrudin Ansyari — Thanks! (The actual icon pages were since moved or removed.)
    +
  2. +
  3. Next, find the CSS inside your HTML file. We'll make a few changes to make way for the icons. First, update the .msgBox width from: +
    width: 200px;
    + +
    ל
    + +
    width: 242px;
    +
  4. +
  5. Next, add the following lines inside the .msgBox p { ... } rule: +
    padding-left: 82px;
    +background-position: 25px center;
    +background-repeat: no-repeat;
    +
  6. +
  7. Now we need to add code to our displayMessage() function to handle displaying the icons. Add the following block just above the closing curly brace (}) of your function: +
    if (msgType === 'warning') {
    +  msg.style.backgroundImage = 'url(icons/warning.png)';
    +  panel.style.backgroundColor = 'red';
    +} else if (msgType === 'chat') {
    +  msg.style.backgroundImage = 'url(icons/chat.png)';
    +  panel.style.backgroundColor = 'aqua';
    +} else {
    +  msg.style.paddingLeft = '20px';
    +}
    + Here, if the msgType parameter is set as 'warning', the warning icon is displayed and the panel's background color is set to red. If it is set to 'chat', the chat icon is displayed and the panel's background color is set to aqua blue. If the msgType parameter is not set at all (or to something different), then the else { ... } part of the code comes into play, and the paragraph is simply given default padding and no icon, with no background panel color set either. This provides a default state if no msgType parameter is provided, meaning that it is an optional parameter!
  8. +
  9. Let's test out our updated function, try updating the displayMessage() call from this: +
    displayMessage('Woo, this is a different message!');
    + +
    to one of these:
    + +
    displayMessage('Your inbox is almost full — delete some mails', 'warning');
    +displayMessage('Brian: Hi there, how are you today?','chat');
    + You can see how useful our (now not so) little function is becoming.
  10. +
+ +
+

לתשומת לב: אם אתם נתקלים בבעיות או הדוגמא אינה עובדת לכם, אנא בדקו את הקוד שלכם אל מול הקוד הסופי שלנו ב- GitHub (או בדף האינטרנט או שאלו אותנו

+
+ +

לסיכום

+ +

במאמר זה ניסינו להסביר לכם את התהליך של בניית פונקציה בעצמנו, כאשר עם עט עבודה נוספת, יכול להפוך לפרוייקט אמיתי. במאמר הבא אנחנו נסיים לעבור על נושא פונקציות ונסביר עקרון חשוב נוסף בנושא זה - ערכי החזרה — return values.

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/building_blocks/events/index.html b/files/he/learn/javascript/building_blocks/events/index.html new file mode 100644 index 0000000000..9fc4363939 --- /dev/null +++ b/files/he/learn/javascript/building_blocks/events/index.html @@ -0,0 +1,588 @@ +--- +title: היכרות ראשונית עם אירועים - Events +slug: Learn/JavaScript/Building_blocks/Events +translation_of: Learn/JavaScript/Building_blocks/Events +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}
+ +

אירועים (Evants) אלו פעולות או התרחשויות אשר קורות במערכת שאנחנו מתכנתים בה, והמערכת אומרת לנו  בדרך מסויימת על ההתרחשות שלהם, כך שאנחנו יכולים להגיב להתרחשות של האירוע המסויים בדרך מסויימת שנרצה. לדוגמא אם משתמש לחץ על כפתור באתר, אולי נרצה בתגובה לטפל באירוע הלחיצה הזה על ידי הצגת הודעה כלשהי. במאמר זה אנחנו נדון בעקרונות החשובים של events ונסתכל כיצד הם עובדים בדפדפדנים. אנחנו לא נרחיב יותר מדי, אלא רק מה שתצטרכו לדעת בשלב זה.

+ + + + + + + + + + + + +
ידע מוקדם:הכרות בסיסית עם המחשב ועם הבסיס של HTML ו- CSS, וכן סיום במלאו של מודול צעדים ראשונים ב-JavaScript.
מטרה:להבין את התיאוריה הבסיסית של אירועים וכיצד הם עובדים בדפדפנים וכיצד אירועים יכולים להיות שונים בסביבות פיתוח שונות.
+ +

מה הם אירועים? 

+ +

כפי שרשמנו למעלה, אירועים, events, הם פעולות או התרחשויות שקורות במערכת שאנחחנו מתכנתים עליה - המערכת שולחת סימן מסויים שהאירוע מתרחש וגם מספקת לנו מכניזם מסויים שעל פיו כלומר, קוד מסויים ירוץ בתגובה להתרחשות אירוע מסויים. כמו בדוגמא עם לחיצת הכפתור שפירטנו למעלה. כאשר האירוע של ״לחיצת כפתור״ התרחש, הדפדפן שלח לנו סימן מסויים שהתרחש אירוע וטיפלנו בו באמצעות הקפצת חלונית.

+ +

במקרה שלנו, אירועים מתרחשים בתוך חלון הדפדפן, ונוטים להיות מחוברים לפריט מסויים שבו הם התרחשו או עליו הם קרו - זה יכול להית אלמנט אחד, מספר אלמנטים, אירוע של טעינה דף HTML בלשונית מסויימת או טעינתו של חלון הדפדפן כולו.

+ +

יש הרבה סוגים של אירועים שיכולים להתרחש. הנה דוגמא קטנה שלהם:

+ + + +

תוכלו לראות בדף MDN בנושא Event reference שיש לא מעט סוגי אירועים שניתן לטפל בהם - כלומר להגיב להתרחשותם.

+ +

לכל אירוע זמין יש ״מטפל אירוע״ - event handler, שזה בעצם בלוק של קוד שבדרך כלל זו פונקציה שאנחנו נגדיר בעצמנו ושירוצו כאשר האירוע החל להתרחש. שימו לב שלפעמים event handlers נקראים event listeners - ״מאזיני אירוע״ בעיקרון, הם כמעט אותו דבר מבחינתנו כרגע, אבל יש הבדלים ביניהם, הם עובדים יחד: מאזין אירוע - event listeners - מאזין להתרחשות האירוע, ואילו מטפל האירוע - event handlers, מטפל באותו אירוע שהתרחש - (הקוד שמורץ בתגובה להתרחשות של אותו אירוע).

+ +
+

לתשומת לב: חשוב לדעת כי אירועי web אינם חלק מהבסיס של JavaScript - הם מוגדרים כחלק מה-API של JavaScript המובנים בתוך הדפדפן.

+
+ +

דוגמא

+ +

נסתכל על דוגמא פשוטה על מנת להסביר את הנושא. ראינו כבר אירועים ו-event handlers בהרבה מהדוגמאות במהלך הקורס אבל בואו נרענן את הזיכרון.

+ +

הסתכלו בדוגמא הבאה, יש לנו אלמנט {{htmlelement("button")}} אחד, אשר כאשר הוא נלחץ, הוא שנה את הצבע של הרקע לצבע אקראי:

+ +
<button>Change color</button>
+ + + +

קוד ה-JavaScript שלנו נראה כך:

+ +
var btn = document.querySelector('button');
+
+function random(number) {
+  return Math.floor(Math.random()*(number+1));
+}
+
+btn.onclick = function() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+ +

בדוגמא למעלה, אחסנו הפניה לאלמנט <button> בתוך משתנה שנקרא btn, באמצעות פונקציית {{domxref("Document.querySelector()")}}. בנוסף ,הגדרנו פונקציה שמחזירה לנו מספר אקראי. החלק השלישי של הקוד הוא מטפל האירוע - event handler. המשתנה btn מצביע על אלמנט  <button> ולסוג הזה של האובייקט יש מספר מסוים של אירועים שיכולים להתרחש עליו, ולכן יש מספר מטפלי אירוע זמינים עבורו.

+ +

בדוגמא זו אנחנו מקשיבים בעצם לאירוע/התרחשות של לחיצת עכבר באמצעות השמת ערך לתכונה (property) של event handler השווה לפונקציה אנונימית. פונקציה אנונימית זו מכילה קוד שמייצר צבע רנדומלי בפורמט rgb וקובעת את ה-background-color לצבע זה.

+ +

הקוד שבפונקציה האנונימית ירוץ בכל פעם שאירוע הלחיצה על האלמנט <button> יתרחש, כלומר בכל פעם שהמשתמש לחץ על האלמנט הזה.

+ +

התוצאה של הדוגמא תהא כך:

+ +

{{ EmbedLiveSample('A_simple_example', '100%', 200, "", "", "hide-codepen-jsfiddle") }}

+ +

זה לא רק בדפי האינטרנט

+ +

דבר נוסף שנרצה להזכיר בנקודה זו הוא שאירועים הם לא מיוחדים ל-JavaScript - רוב שפות התכנות מכילות מודל כלשהו של אירועים, והדרך שבה מודל זה עובד בהם, יהיה לרוב שונה מהדרך שבה מודל זה עובד ב-JavaScript. האמת, מודל האירועים ב-JavaScript עבור דפי אינטרנט, שונה ממודל האירועים עבור JavaScript כאשר היא בסביבות אחרות.

+ +

לדוגמא, Node.js היא סביבה המאפשרת לנו הרצה ושימוש ב-JavaScript בצד השרת. מודל האירועים של Node.js מתבסס על מאזינים (listeners) אשר מאזינים לאירועים ועל פולטים (emitters) אשר פולטים אירועים בתדירות מסויימת - זה לא נשמע שונה, אבל הקוד הוא שונה, ושם מתבצע שימוש בפונקציות כמו ()on על מנת לרשום מאזין אירוע ו-()once על מנת לרשום מאזין אירוע שיאזין באופן חד פעמי ויתבטל לאחר שהורץ פעם אחת. דוגמא לכך ניתן לראות ב-HTTP connect event docs.

+ +

דוגמא נוספת למודל אירועים שונים אנחנו יכולים לראות כאשר משתמשים ב-JavaScript על מנת לבנות תוספים לדפדפנים (מעין שיפורים לדפדפנים) - באמצעות טכנולוגיה שנקראת WebExtensions. מודל האירועים שם הוא דומה למודל האירועים של ה-Web, אבל שונה מעט - מאזיני האירועים הם camel-cased - כלומר, onMessage ולא onmessage, והם צריכים להיות ביחד עם פונקצית addListener. לדוגמאות ראו runtime.onMessage page.

+ +

אינכם צריכים להבין עכשיו שום דבר על הסביבות הללו, רק רצינו להסביר לכם שאירועים ומודל האירועים יכול להיות בסביבות פיתוח שונות.

+ +

דרכים לשימוש ב-web events

+ +

יש מספר דרכים שונות שבהם אנחנו יכולים להוסיף מאזין אירוע -event listener לדפי הרשת על מנת שהוא יריץ את ה-event handler כאשר יתרחש האירוע הרלוונטי (event registration). בחלק זה, אנחנו נסקור את הדרכים השונות ונדון באילו מהן אנו צריכים להשתמש.

+ +

דרך I: הוספת תכונה של מטפל אירוע - Event handler properties

+ +

דרך אחת היא בעצם לתת לאלמנט מסויים תכונה של מאזין אירוע והערך שלה יהיה מטפל אירוע. אנו נקרא לה ״תכונת מטפל אירוע״. נתקלנו בזה במהלך הקורס לא מעט. אם נחזור לדוגמא למעלה:

+ +
var btn = document.querySelector('button');
+
+btn.onclick = function() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+ +

התכונה onclick היא תכונת מטפל האירוע (מאזין האירוע) הרלוונטית כרגע לדוגמא זו. היא תכונה בדיוק כמו שיש ל-btn תכונות נוספות שזמינו תעבורו כמו btn.textContent, או btn.style, אבל היא תכונה מסוג מיוחד - כאשר אנחנו משימים בה ערך ששווה לקוד מסויים, הקוד הזה ירוץ בכל פעם שהאירוע יתרחש על הכפתור (או על האלמנט אשר נתנו לו את התכונה הזו).

+ +

אנחנו גם יכולים לתת למטפל אירוע שכזה שיהיה שווה לשם של פונקציה מסויימת, כמו שראינו במאמר בניית פונקציות משלנו. הקוד הבא יעבוד בצורה מצויינת:

+ +
var btn = document.querySelector('button');
+
+function bgChange() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+
+btn.onclick = bgChange;
+ +

יש הרבה סוגים של תכונות מטפלות אירוע שזמינות. ננסה כמה מהם:

+ +

ראשית, שמרו לכם עותק מקומי של random-color-eventhandlerproperty.html, ופיתחו אותו בדפדפן. זהו עותק של הדוגמא הפשוטה של בחירת הצבע שראינו בעבר. עכשיו שנו את ה- btn.onclick לערכים הבאים, כל אחד בתור, וראו מה קורה :

+ + + +

ישנם אירועים שהם מאד כלליים והם זמינים כמעט בכל מקום או עבור כל אלמנט - לדוגמא - אפשר להאזין לאירוע onclick כמעט על כל אלמנט, בעוד יש כאלו שהם יותר ספציפיים ושימושיים בסיטואציות מסויימת. לדוגמא, הגיוני לנו ש-onplay יהיה זמין רק לאלמנטים מסוג מסויים כמו {{htmlelement("video")}}.

+ +

דרך II: לידיעה בלבד - לא להשתמש בהם - Inline event handlers

+ +

יכול להיות שראיתם קוד שנראה כך:

+ +
<button onclick="bgChange()">Press me</button>
+
+ +
function bgChange() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+ +
+

לתשומת לב: אתם יכולים לראות את קוד המקור לדוגמא זו ב- GitHub או בדף אינטרנט.

+
+ +

בעבר, הדרך לרשום (register) מטפלי אירוע הייתה באמצעות event handler HTML attributes הידועים גם כ- inline event handlers, כמו שרשום בדוגמא למעלה. הערך של אותו attribute הוא ממש קוד JavaScript שאנחנו רוצים שירוץ כאשר האירוע מתרחש. הדוגמא למעלה מפעילה פונקציה שמוגדרת בתוך האלמנט {{htmlelement("script")}} אבל באותה הדרך אנחנו יכולים להכניס פונקציה שלמה לתוך אותו attribute:

+ +
<button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button>
+ +

אל תשתמשו בדרך שכזו על מנת לטפל באירוע, זה נחשב כ- bad practice (פרקטיקה שאינה מקובלת בתעשייה). זה אולי נשמע יותר פשוט, אבל מהר מאוד זה הופך למשהו שלא ניתן לעקוב אחריו ולא יעיל.

+ +

בנוסף, זה לא רעיון טוב לערבב בין HTML לבין JavaScript מאחר שזה נהיה קשה לעיבוד - היצמדו לכך שכל ה-JavaScript נמצא במקום אחד.

+ +

אפילו כאשר מדובר בקובץ אחד, inline event handlers הם לא רעיון טוב. כאשר יש כפתור אחד זה עוד סביר, אבל מה אם היו לנו 100 כפתורים? היינו צריכים להוסיף 100 attributes לתוך הקובץ. מהר מאוד זה הופך לסיוט מבחינת תחזוקה של הקוד. עם JavaScript, אנחנו יכולים בקלות להוסיף מטפל אירוע לכל הכפתורים, לא משנה כמה יש כאלו, באמצעות קוד כזה:

+ +
var buttons = document.querySelectorAll('button');
+
+for (var i = 0; i < buttons.length; i++) {
+  buttons[i].onclick = bgChange;
+}
+ +
+

לתשומת לב: הפרדה בין הקוד שלנו לבין התוכן גם הופך את האתר שלנו ליותר ידידותי למנועי החיפוש.

+
+ +

דרך addEventListener() : III  ו- ()removeEventListener

+ +

הסוג החדש ביותר של מנגנון אירועים הוגדר ב- Document Object Model (DOM) Level 2 Events של ארגון W3C. מסמך זה הגדיר דרך טובה יותר לנהל אירועים, בין היתר באמצעות אובייקט מיוחד בשם  EvantTarget אשר נוצר בדפדפן כאשר אירוע מתרחש. לאובייקט זה יש שלוש מתודות ואחת מהן היא ()addEventListener. מתודה זו דומה לדרך הראשונה של event handler properties, אבל הסינטקס שונה. אנחנו יכולים לכתוב את הדוגמא שלנו עם הצבעים האקראיים בצורה הזו:

+ +
var btn = document.querySelector('button');
+
+function bgChange() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+
+btn.addEventListener('click', bgChange);
+ +
+

לתשומת לב:תוכלו למצוא את קוד המקור של דוגמא זו ב- GitHub או ראו כ- דף אינטרנט).

+
+ +

בתוך מתודת ()addEventListener, אנחנו מציינים שני פרמטרים - אחד בעבור אותו האירוע, אותו ה-event אשר אנחנו רוצים שיאזינו והפרמטר השני זה פונקציה של הקוד שאנחנו רוצים שירוץ כמטפל באותו אירוע. שימו לב שזה בסדר גמור לשים את כל הקוד שיטפל באירוע כפונקציה אנונימית, בשונה מהדוגמא למעלה:

+ +
btn.addEventListener('click', function() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+});
+ +

לשיטה הזו יש יתרונות ברורים על השיטות הישנות שדנו בהם למעלה. ראשית, יש מתודה נוספת בשם ()removeEventListener אשר מאפשרת לנו להסיר מאזיני אירוע. לצורך העניין, הקוד הבא יסיר את מאזין האירוע שקבענו למעלה:

+ +
btn.removeEventListener('click', bgChange);
+ +

זה אמנם לא כזה משמעותי עבור תוכניות קטנות או פשוטות, אבל בעבור תוכניות גדולות או מורכבות, זה יכול להגביר את היעילות ומאפשר לנקות מטפלי אירוע שאינם בשימוש עוד. בנוסף, זה מאפשר לנו שלאותו אלמנט - לאותו הכפתור - יהיו מספר מטפלי אירועים אשר יפעלו בצורה אחרת באירועים שונים - כל מה שאנחנו צריכים לעשות זה להוסיף/להסיר מטפלי אירוע בהתאם.

+ +

בנוסף, אנחנו גם יכולים לרשום מספר מטפלי אירוע לאותו אירוע - הקוד הבא לדוגמא לא יכול להחיל שני מטפלי אירוע:

+ +
myElement.onclick = functionA;
+myElement.onclick = functionB;
+ +

זאת מכיוון שהשורה השנייה דורסת את הערך שנקבע ל-onclick של השורה הראשנה. על מנת להוסיף מטפל אירוע נוסף מבלי לדרוס את מטפל את האירוע הקיים, אנחנו יכולים לעשות משהו כזה:

+ +
myElement.addEventListener('click', functionA);
+myElement.addEventListener('click', functionB);
+ +

במקרה זה, שתי הפונקציות ירוצו כאשר ילחצו על האלמנט.

+ +

כמו כן, יש מספר אפשרויות משמעותיות ותכונות זמינות עבור מנגנון ירועים בדרך הזו. אנחנו לא נרחיב עליהם, אך אם תרצו להרחיב את ידיעתכם בנושא, היכנסו לדפי ההסבר של MDN בנושא ()addEventListener ובנושא ()removeEventListener.

+ +

באיזה מנגנון אירועים להשתמש?

+ +

מתוך שלושת המנגנונים הללו, בוודאות אל תשתמשו במנגנון HTML event handler attributes - זה מנגנון ישן, שלא נמצא כבר בשימוש ונחשב כ- bad practice להשתמש בו.

+ +

השניים האחרים הם יחסית די חלופיים כאשר מדובר בתוכנית פשוטה או כאשר מדובר בשימוש פשוט שנרצה לעשות באירועים:

+ + + +

כמו שאמרנו, היתרון העיקרי של המנגנון השלישי הוא שאתם יכולים להסיר קוד שמטפל באירוע, באמצעות ()removeEventListener, ואתם יכולים להוסיף מספר מאזיני אירוע לאותו אלמנט. לדוגמא, אנחנו יכולים לקרוא ל-({ ... }()addEventListener('click', function על אלמנט מסויים מספר רב של פעמים, כאשר בכל פעם תהיה פונקציה אחרת בארגומנט השני. אופציה זו לא אפשרית ב- event handler properties כי כל הגדרה נוספת של property תמחוק את ההגדרה הקודמת שלו:

+ +
element.onclick = function1;
+element.onclick = function2;
+etc.
+ +
+

לתשומת לב: אם אתם נדרשים לתמוך בדפדפנים ישנים מאינטרנט אקפלורר 8, אתם עלולים להיתקל בקשיים שכן דפדפדנים ישנים אלו משתמשים במודל אירועים שונה מדפדפנים חדשים, אך אל פחד. מרבית ספריות JavaScript (כמו לדוגמא jQuery) מכילות פונקציות מובנות שמבטלות כביכול את ההבדלים בין הדפדנים השונים. אין לכם צורך לדאוג לנושא זה בתחילת הלמידה שלכם.

+
+ +

עקרונות נוספים בנושא אירועים

+ +

בחלק זה, אנחנו נגע בקצרה בנושאים מתקדמים הקשורים לאירועים. אין זה הכרחי להבין נושאים אלו לעומק בשלב זה, אך זה עלול לסייע לכם להבין תבניות קוד שתיתקלו בהם מדי פעם.

+ +

Event objects

+ +

לפעמים בתוך פונקצייה המטפלת באירוע (event handler) - אתם תראו פרמטר המצויין בתך הסוגריים, הנושא שם כמו event, evt, או סתם e. זה נקרא event object - האובייקט של האירוע עצמו, והוא מועבר בצורה אוטומטית למטפלי האירוע על מנת לספק אפשרויות נוספות ומידע נוסף. לדוגמא, נרשום את דוגמת הצבעים שלנו מחדש בצורה קצת שונה:

+ +
function bgChange(e) {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  e.target.style.backgroundColor = rndCol;
+  console.log(e);
+}
+
+btn.addEventListener('click', bgChange);
+ +
+

לתשומת לב: תוכלו למצוא את קוד המקור ב-GitHub וגם לראות את הדוגמא כדף אינטרנט.

+
+ +

כאן אנחנו יכולים לראות שאנחנו כוללים את האובייקט של האירוע - e, בתוך הפונקציה, ובתוך הפונקציה אנחנו קובעים צבע רקע ל- e.target — שזה בעצם הכפתור עצמו. המאפין target של event object הוא תמיד הפנייה לאלמנט שהאירוע התרחש עליו. כך, בדוגמא שלנו, אנחנו קובעים בעצם צבע רקע אקראי לכפתור עצמו שהאירוע הלחיצה התרחש עליו ולא לדף.

+ +
+

לתשומת לב: אתם יכולים להשתמש באיזה שם שנרצה עבור event object — רק השתמשו בשם שיהיה ברור שזה הפניה ל- event object. השמות המקובלים הם e/evt/event מכיוון שהם קצרים וקל לזכור את ההקשר שלהם.

+
+ +

e.target הוא שימושי להפליא כשאנחנו רוצים לקבוע את אותו event handler על מספר אלמנטים ולעשות משהו לכולם כאשר אותו אירוע מתרחש. לדוגמא, נניח ויש לנו סט של 16 אריחים שנעלמים כאשר הם נלחצים. זה נוח יותר כאשר מתאפשר לנו לקבוע שאלמנט יעלם כשאנחנו נלחץ עליו באמצעות שימוש ב- e.target , מאשר לבחור אותו בצורה מורכבת אחרת. בדוגמאות הבאות למשל ב-useful-eventtarget.html - קוד המקור (ניתן לראות גם ב-דף האינטרנט) יצרנו 16 אלמנטים מסוג {{htmlelement("div")}} באמצעות JavaScript, ואנחנו בוחרים את כולם באמצעות {{domxref("document.querySelectorAll()")}} ואז עוברים על כל אחד מהם באמצעות לולאה ומוסיפים מאזין אירוע ומטפל אירוע של onclick לכל אחד מהם כך שכאשר כל אחד מהאלמנטים הללו נלחץ על ידי המשתמש, צבע אקראי אחר ניתן לאותו אלמנט:

+ +
var divs = document.querySelectorAll('div');
+
+for (var i = 0; i < divs.length; i++) {
+  divs[i].onclick = function(e) {
+    e.target.style.backgroundColor = bgChange();
+  }
+}
+ +

הפלט נראה כך. נסו לשחק איתו:

+ + + +

{{ EmbedLiveSample('Hidden_example', '100%', 400, "", "", "hide-codepen-jsfiddle") }}

+ +

הרבה ממאזיני האירועים/מטפלי האירועים שניתקל בהם הם בעלי סט סטנדרטי של properties ופונקציות - או יותר נכון מתודות, אשר זמינים עבור ה-event object - ראו את הדף בנושא אובייקט {{domxref("Event")}} לרשימה המלאה.

+ +

יש מטפלי אירועים יותר מורכבים, המכילים properties מיוחדים בעלי מידע ספציפי שהם צריכים על מנת לתפקד כמטפל אירוע. לדוגמא ל-Media Recorder API, יש סוג אירוע - dataavailable - שמתרחש כאשר אודיו או וידאו הוקלטו והם זמינים כך שניתן לבצע עליהם פעולות מסויימות - לדוגמא לשמור אותם או לנגן אותם שוב. למאזין האירוע/מטפל אירוע התואם ondataavailable יש property שזמין בשם data עבורו המכיל את האודיו/וידאו שהוקלט ומאפשר לנו לגשת אליו ולעשות איתו דברים.

+ +

מניעת התנהגות ברירת מחדל

+ +

לפעמים אנחנו ניתקל בסיטואצית שאנחנו נרצה למנוע מאירוע לבצע את מה שהוא אמור לעשות כברירת מחדל. הדוגמא הכי נפוצה היא טופס באינטרנט. כאשר אנחנו נמלא את הפרטים ונלחץ על כפתור ה-Submit , ההתנהגות ברירת המחדל תהיה לשלוח את המידע שהוכנס בטופס לדף מסויים בשרת לשם עיבוד, והדפדפן יועבר למעין דף של ״הפרטים נשלחו בהצלחה״ או משהו דומה.

+ +

הבעיה מתעוררת כאשר המשתמש לא הזין את המידע כראוי - כמפתחים, אנחנו נרצה למנוע שליחה של המידע השגוי לשרת ולתת למשתמש הודעת שגיאה שאומרת להם מה הייתה הטעות ומה הם צריכים לעשות. חלק מהדפדנים תומכים בולידציה של מידע בטפסים, אך מכיון שהרבה לא ומסיבות נוספות, אנחנו ממליצים לא להסתמך על ולידציה של דפדפנים אלא לבנות את הולידציה בעצמכם. לדוגמא:

+ +

נתחיל עם טופס HTML פשוט דורש מאיתנו להכניס שם פרטי ושם משפחה:

+ +
<form>
+  <div>
+    <label for="fname">First name: </label>
+    <input id="fname" type="text">
+  </div>
+  <div>
+    <label for="lname">Last name: </label>
+    <input id="lname" type="text">
+  </div>
+  <div>
+     <input id="submit" type="submit">
+  </div>
+</form>
+<p></p>
+ + + +

כעת קצת JavaScript - כאן החלנו בדיקה פשוטה מאוד בתוך מטפל האירוע onsubmit (האירוע של submit מתרחש כאשר הטופס נשלח).

+ +

הבדיקה שלנו בודקת הם שדות הטקסט ריקים. אם כן, אנחנו קוראים לפונקציית ()preventDefault על ה- event object - כלומר על האלמנט שעליו התרחש האירוע. פונקצייה זו מונעת מהטופס להישלח - ומציגה הודעת שגיאה בפסקה מתחת לטופס שלנו, על מנת להציג למשתמש מה השתבש:

+ +
var form = document.querySelector('form');
+var fname = document.getElementById('fname');
+var lname = document.getElementById('lname');
+var submit = document.getElementById('submit');
+var para = document.querySelector('p');
+
+form.onsubmit = function(e) {
+  if (fname.value === '' || lname.value === '') {
+    e.preventDefault();
+    para.textContent = 'You need to fill in both names!';
+  }
+}
+ +

ברור שזו ולידציה פשוטה וחלשה מאוד - זה לא ימנע מהמשתמש להזין רווחים או מספרים לתוך שדות הטקסט, אך זה מספיק לשם הדוגמא. הפלט ייראה כך:

+ +

{{ EmbedLiveSample('Preventing_default_behavior', '100%', 140, "", "", "hide-codepen-jsfiddle") }}

+ +
+

לתשומת לב: ראו את קוד המקור פה preventdefault-validation.html או דף אינטרנט.

+
+ +

event bubbling ו- capture

+ +

נושא נוסף שאנחנו נרצה לדון הוא משהו שאמנם לא ניתקל בו הרבה, אך אם לא נכיר אותו ונבין אותו, יכול לעשות לנו כאב ראש לא קטן.

+ +

event bubbling ו- capture אלו שני מנגנונים אשר מסבירים מה קורה כאשר שנים או יותר מטפלי אירוע של אותו אירוע מופעלים על אלמנט אחד. לצורך ההסבר, פתחו את show-video-box.html בלשונית חדשה בדפדפן ואת קוד המקור בלשונית אחרת. ניתן גם לראות את הדוגמא כאן למטה:

+ + + +

{{ EmbedLiveSample('Hidden_video_example', '100%', 500, "", "", "hide-codepen-jsfiddle") }}

+ +

זו דוגמא פשוטה שמראה ומסתירה אלמנט {{htmlelement("div")}} שמכיל אלמנט {{htmlelement("video")}} בתוכו:

+ +
<button>Display video</button>
+
+<div class="hidden">
+  <video>
+    <source src="rabbit320.mp4" type="video/mp4">
+    <source src="rabbit320.webm" type="video/webm">
+    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
+  </video>
+</div>
+ +

כאשר אנחנו לוחצים על {{htmlelement("button")}} הוידאו מוצג, באמצעות שינוי ה-class של <div> מ- hidden ל- showing (ה-css בקובץ מכיל בין היתר שני classes אשר מציגים את הקופסא במסך או מסתירים אותה, בהתאמה) :

+ +
btn.onclick = function() {
+  videoBox.setAttribute('class', 'showing');
+}
+ +

לאחר מכן אנחנו מוסיפים עוד שני מטפלי אירוע של onclick - הראשון ל- <div> והשני ל - <video>. הרעיון הוא שכאשר האזור של ה- <div> מחוץ לוידאו מקבל לחיצה, הקופסא אמורה להיעלם שוב; כאשר הוידאו עצמו נלחץ, הוידאו אמור לפעול שוב:

+ +
videoBox.onclick = function() {
+  videoBox.setAttribute('class', 'hidden');
+};
+
+video.onclick = function() {
+  video.play();
+};
+ +

אבל ישנה בעי כרגע, כאשר אנחנו לוחצים על הוידאו הוא מתחיל להתנגן, אבל גורם גם ל- <div> להיות מוסתר באותו הזמן. זה מכיוון שהוידאו שבתוך ה-<div> - הוא חלק ממנו - אז כאשר אנחנו לוחצים על הוידאו - אנחנו בעצם גם מפעילים את האירוע על ה-<div> עצמו, ושני מטפלי האירוע נכנסים לפעולה.

+ +

bubbling ו- capturing

+ +

כאשר אירוע מתרחש על אלמנט שיש לו אלמנט אב - לדוגמא האלמנט {{htmlelement("video")}} שלנו, לדפדפנים המודרניים יש שני תהליכים שונים שהם מבצעים: שלב ה- capturing ושלב ה- bubbling.

+ +

בשלב - capturing:

+ + + +

בשלב - bubbling קורה בדיוק ההפך:

+ + + +

+ +

לחצו על התמונה על מנת להגדיל אותה.

+ +

בדפדפנים מודרניים, ברירת המחדל היא שכל ה-event handlers נרשמים בשלב ה-bubbling. בדוגמא שלנו למשל, כאשר לחצנו על הוידאו, האירוע מסוג לחיצה ״בועבע״ מאלמנט <video> הלאה לאלמנט <html>, כאשר במהלך הדרך :

+ + + +

שימו לב שמה שבועבע הוא התרחשות האירוע עצמו, ולא מטפל האירוע (אותו דבר קורה גם בשלב ה-capturing). 

+ +

תיקון הבעיה עם ()stopPropagation

+ +

על מנת לתקן את ההתנהגות הזו, לאובייקט האירוע עצמו - event object - יש מתודה זמינה עבורו שנקראת ()stopPropagation, כאשר היא מופעלת על מטפל האירוע של אובייקט האירוע, היא תריץ את מטפל האירוע, אבל תמנע מהאירוע עצמו להמשיך לבעבע במעלה השרשרת לאלמנטי האב שלו (וכנ״ל גם בשלב ה- capturing) וכך מטפלי אותו אירוע של אלמנטי האב לא ירוצו.

+ +

כך, על מנת לתקן את הבעיה בדוגמא שלנו, אנחנו נשנה את הפונקציה המטפלת באירוע כך:

+ +
video.onclick = function(e) {
+  e.stopPropagation();
+  video.play();
+};
+ +

אתם יכולים ליצור עותק מקומי של קוד המקור של show-video-box.html ולתקן זאת בעצמכם או לראות הקובץ המתוקן כאן ואת קוד המקור כאן.

+ +
+

הערה חשובה: אתם בטח שואלים את עצמכם מדוע לטרוח וליצור שני שלבים  - גם capturing וגם bubbling? התשובה נעוצה בהיסטוריה ובימי העבר כאשר התאימות בין דפדפנים הייתה שונה מאוד ולא כפי שהיא היום.

+ +

בימים ההם Netscape השתמש ב-event capturing בלבד ואילו Internet Explorer השתמש ב - event bubbling בלבד. כאשר ארגון W3C החליט לנסות וליצור סטדרט אחיד, הם החליטו על מנגנון שיכלול את שני השלבים - וכל הדפדפנים אימצו את שני השלבים.

+
+ +
+

לתשומת לב: יחד עם זאת, כברירת מחדל, כל מטפלי האירועים נרשמים בשלב bubbling ולא בשלב capturing וברוב המוחלט של הפעמים זה עושה שכל. אם אנחנו ממש רוצים לרשום אירוע בשלב ה-capturing, אנחנו יכולים לעשות זאת באמצעות הוספת true כפרמטר שלישי בתוך ()addEventListener. היכנסו לקישור זה אם תרצו להרחיב בנושא. 

+
+ +

Event delegation

+ +

bubbling מאפשר לנו להשתמש ביתרונות של event delegation — רעיון זה מתבסס על העובדה שאם אנחנו רוצים להריץ קוד מסויים כאשר אנחנו לוחצים על אלמנט ילד אחד שהוא חלק ממספר אלמנטים ילדים של אלמנט אב, אנחנו יכולים לקבוע את מאזין האירוע על אלמנט האב, ואז כאשר האירוע מתרחש על אחד מאלמנטים הילדים, האירוע ״יבועבע״ לאלמנט ההורה שלהם.

+ +

כתיבה שכזו עדיפה על רישום מאזין אירוע לכל אחד מהילדים בנפרד. להזכירכם - בשלב ה-bubbling נבדק האם האלמנט שהאירוע התרחש עליו מכיל מטפל אירוע לאירוע שהתרחש ומתקדם הלאה לאלמנט ההורה ומבעבע אליו את האירוע ובודק האם הוא מכיל מטפל אירוע לאירוע שהתרחש וכך הלאה.

+ +

דוגמא טובה לכך היא רשימה של פריטים - אם אנחנו רוצים שכל פריט מהרשימה יקפיץ הודעה כאשר לוחצים עליו, אנחנו יכולים לקבוע מאזין אירוע על האלמנט ההורה - במקרה שלנו <ul>, וכל לחיצה על פריט מהרשימה (<li>) -  כלומר כל התרחשות אירוע שכזה על פריט מהרשימה - אירוע הלחיצה יבעבע מאלמנט <li> לאלמנט האב - <ul>.

+ +

רעיון זה מוסבר בפירוט רב בבלוג של David Walsh עם דוגמאות נוספות. ראו את המאמר שלו בנושא How JavaScript Event Delegation Works.

+ +

לסיכום

+ +

במאמר זה ניסינו להסביר כל מה שאתם צריכים לדעת על אירועי Web בשלב זה. כפי שנאמר למעלה, אירועים הם לא חלק מה-core של JavaScript - הם מוגדרים ב-Web APIs של הדפדפן.

+ +

חשוב להבין בנוסף שיש הבדלים בין מודלי האירועים של JavaScript בסביסות השונות שהיא מורצת - החל מ- Web APIs ועד לסביבות אחרות כמו Browser WebExtensions ו- Node.js. לא מצפים מכם שכרגע תבינו את כל הסביבות הללו, אבל זה עוזר לכם להבין את הבסיס ככל ותתקדמו בלמידה.

+ +

אם משהו לא ברור, הרגישו חופשי לקרוא שוב את המאמר או ליצור עמנו קשר.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/building_blocks/functions/index.html b/files/he/learn/javascript/building_blocks/functions/index.html new file mode 100644 index 0000000000..99255d0591 --- /dev/null +++ b/files/he/learn/javascript/building_blocks/functions/index.html @@ -0,0 +1,386 @@ +--- +title: Functions — פונקציות - חלקי קוד לשימוש חוזר +slug: Learn/JavaScript/Building_blocks/Functions +translation_of: Learn/JavaScript/Building_blocks/Functions +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}
+ +

עקרון חשוב והכרחי נוסף בתכנות הוא פונקציות - functions. פונקציות מאפשרות לנו לאחסן קוד בתוך בלוק מוגדר ומבצע פעולה מסויימת, ואז לקרוא לאותו קוד מתי שנצטרך אותו באמצעות פקודה קצרה. זאת במקום לרשום את אותו קוד שוב ושוב. במאמר זה אנחנו נחקור את העקרונות הבסיסיים והחשובים שמאחורי פונקציות כמו הסינטקס הבסיסי שלהן, כיצד להגדיר אותן, כיצד להפעיל אותן, נלמד מהו סקופ ופרמטרים ועוד.

+ + + + + + + + + + + + +
ידע מוקדם:הכרות בסיסית עם המחשב ועם הבסיס של HTML ו- CSS, וכן סיום במלאו של מודול צעדים ראשונים ב-JavaScript.
מטרה:הבנה של עקרונות היסוד בנושא פונקציות ב-JavaScript.
+ +

איפה נמצא פונקציות?

+ +

ב-JavaScript אנחנו נמצא פונקציות בכל מקום. בעיקרון, אנחנו השתמשנו בפונקציות לאורך כל הדרך של הקורס, פשוט לא פירטנו עליהם לעומק. כעת אנחנו ניכנס לעומק של אלו ונבין כיצד להשתמש בהם.

+ +

כמעט כל פעם שאנחנו עושים שימוש בביטוי מסויים של JavaScript שעושה שימוש בסוגריים רגילות () וזה לא במסגרת לולאת for או לולאות while ו- do...while loop או משפטי תנאי if...else אנחנו בעצם עושים שימוש בפונקציות.

+ +

פונקציות מובנות (Built-in) של הדפדפן

+ +

אנחנו ביצענו לא מעט שימוש בפונקציות המובנות בדפדפן. בכל פעם שעשינו מניפולציה על מחרוזת לדוגמא:

+ +
let myText = 'I am a string';
+let newString = myText.replace('string', 'sausage');
+console.log(newString);
+// the replace() string function takes a source string,
+// and a target string and replaces the source string,
+// with the target string, and returns the newly formed string
+ +

או בכל פעם שעשינו מניפולציה על מערך:

+ +
let myArray = ['I', 'love', 'chocolate', 'frogs'];
+let madeAString = myArray.join(' ');
+console.log(madeAString);
+// the join() function takes an array, joins
+// all the array items together into a single
+// string, and returns this new string
+ +

או בכל פעם שרצינו ליצור מספר רנדומלי:

+ +
let myNumber = Math.random();
+// the random() function generates a random number between
+// 0 and up to but not including 1, and returns that number
+ +

אנחנו בעצם השתמשנו בפונקציות.

+ +
+

תשומת לבכם: נסו להכניס את השורות קוד הרשומות למעלה לקונסולה אם אתם לא זוכרים איך לעשות בהם שימוש.

+
+ +

ל- JavaScript יש לא מעט פונקציות מובנות אשר מאפשרות לנו לעשות דברים שימושיים מבלי לכתוב את כל הקוד בעצמנו. בפועל, חלק מהקוד שאנחנו מריצים כאשר אנחנו אנחנו קוראים לפונקציה מובנית של הדפדפן, לא יכל היה להירשם ב-JavaScript - הרבה מהפונקציות הללו קוראות לחלקים בקוד הדפדפן עצמו, אשר נבנה בשפה אחרת שאינה JavaScript כלל.

+ +

אנא זכרו שחלק מהפונקציות המובנות של הדפדפם אינן חלק מהבסיס של שפת JavaScript - חלקן מהן מוגדרות כחלק מה-API של הדפדפן, אשר בנוי מעל שפת הכתיבה של הדפדפן על מנת להעניק יותר פונקציונליות. אנחנו נסתכל על ה-API של הדפדפן בהמשך המודול. לרענון זכרונכם, ראו מאמר זה מתחילת הקורס.

+ +

functions vs. methods  - פונקציות מול מתודות

+ +

אנו קוראים לפונקציות שהן חלק מאובייקטים כ-מתודות (methods). אנחנו לא צריכים כרגע לדעת על המבנה הפנימי של אובייקטים ב-JavaScript - אנו נלמד את זה לעומק במודול הבא. לבינתיים, רק רצינו למנוע בלבול בין המושגים השונים. רוב הסיכויים שתשמעו את שניהם.

+ +

הקוד המובנה שעשינו בו שימוש לבינתיים מגיע ב-2 תצורות: פונקציות ו-מתודות. אתם יכולים לבדוק את הרשימה המלאה של פונקציות מובנות וכן את הרשימה המלאה של אובייקטים מובנים והמתודות שלהם פה.

+ +

כמו כן, ראינו הרבה פונקציות מותאמות -custom functions - פונקציות שאנחנו הגדרנו בעצמנו ולא מוגדרות בתוך הדפדפן עצמו. בכל פעם שאנחנו רואים שם שאינו שם שמור של השפה ולאחריו סוגריים רגילות (), זוהי פונקציה שהמפתח הגדיר בעצמו. בתרגול שלנו random-canvas-circles.html וגם בקוד המקור שלו במאמר בנושא לולאות, כללנו פונקציה ()draw שאנחנו בנינו שנראית כך :

+ +
function draw() {
+  ctx.clearRect(0,0,WIDTH,HEIGHT);
+  for (let i = 0; i < 100; i++) {
+    ctx.beginPath();
+    ctx.fillStyle = 'rgba(255,0,0,0.5)';
+    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+    ctx.fill();
+  }
+}
+ +

פונקציה זו מציירת 100 עיגולים אקראיים בתוך אלמנט {{htmlelement("canvas")}} . בכל פעם שאנחנו רוצים לעשות זאת, אנחנו יכולים להפעיל את הפונקציה עם הקוד הבא:

+ +
draw();
+
+ +

במקום לכתוב את כל הקוד שבתוך הפונקציה בכל פעם שאנחנו רוצים שיצוייר עיגול. 

+ +

פונקציות יכולות להכיל כל קוד שנרצה - אנחנו אפילו יכולים לקרוא לפונקציות אחרות מתוך פונקציות. הפונקציה למעלה לדוגמא, קוראת לפונקציה ()random שלוש פעמים, כפי שאנו רואים בקוד הבא:

+ +
function random(number) {
+  return Math.floor(Math.random()*number);
+}
+ +

אנחנו צריכים לבנות את הפונקציה ()random כי הפונקציה המובנית של הדפדפן ()Math.random מייצרת לנו מספר רנדומלי, אבל מספר רנדומלי עשרוני בין 0 ל-1. אנחנו לעומת זאת רצינו מספר רנדומלי שלם ולא עשרוני ושיהיה בין 0 למספר מסויים שאנחנו נגדיר לו.

+ +

Invoking functions - קריאה לפונקציה

+ +

ככל הנראה אתם כבר מבינים את הנושא, אבל בכל מקרה אנו מזכירים שכדי להשתמש בפועל בפונקציה אחרי שהיא הוגדרה, אנחנו צריכים להריץ אותה, לקרוא לה. בשפה האנגלית זה נקרא To invoke. זה נעשה באמצעות כתיבת שם הפונקציה איפה שנרצה בתוך הקוד שלנו, ולאחריו סוגריים מסולסלות ().

+ +
function myFunction() {
+  alert('hello');
+}
+
+myFunction()
+// calls the function once
+ +

Anonymous functions - פונקציות אנונימיות

+ +

אולי נתקלתם בפונקציות אשר מוגדרות ומופעלות בצורות אחרות. עד עכשיו, יצרנו פונקציה בצורה כזו:

+ +
function myFunction() {
+  alert('hello');
+}
+ +

אבל אנחנו גם יכולים ליצור פונקציה שאין לה שם:

+ +
function() {
+  alert('hello');
+}
+ +

פונקציה שכזו נקראת פונקציה אנונימית - anonymous function - אין לה שם. היא גם לא עושה כלום בעצמה. בדרך כלל אנחנו נשתמש בפונקציה אנונימית ביחד עם ״מטפל אירוע״ - event handler. לדוגמא, הקוד הבא יריץ את הקוד בנמצא בתוך הפונקציה, כאשר הכפתור myButton נלחץ:

+ +
var myButton = document.querySelector('button');
+
+myButton.onclick = function() {
+  alert('hello');
+}
+ +

הדוגמא למעלה דורשת שיהיה אלמנט {{htmlelement("button")}} זמין על גבי הדף כדי שנוכל לבחור אותו וללחוץ עליו. ראיתם מבנה שכזה מספר פעמים במהלך הקורס ואתם תלמדו עליו עוד בהמשך הקורס.

+ +

אנחנו יכולים להשים פונקציה אנונימית לתוך משתנה כך שהיא תהיה הערך של אותו משתנה. לדוגמא:

+ +
let myGreeting = function() {
+  alert('hello');
+}
+ +

ניתן להפעיל את הפונקציה הזו (invoke) באמצעות:

+ +
myGreeting();
+ +

בפועל, זה מעניק לשם פונקציה, למרות שהיא הייתה פונקציה אנונימית. אנחנו גם יכולים להשים את הפונקציה כך שהיא תהיה הערך של מספר משתנים. לדוגמא:

+ +
let anotherGreeting = myGreeting;
+ +

הפונקציה הזו יכולה להיות מופעלת בשתי הדרכים:

+ +
myGreeting();
+anotherGreeting();
+ +

אבל זה עלול להיות מבלבל, אז אל תעשו זאת. כשאנחנו יוצרים פונקציות, עדיף יהיה להיצמד למבנה הזה:

+ +
function myGreeting() {
+  alert('hello');
+}
+ +

אנחנו נשתמש בפונקציות אנונימיות בעיקר על מנת להריץ קוד בתגובה לאירוע שהתרחש - כמו לחיצה על כפתור - וזאת באמצעות ״מטפל אירוע״ - event handler. לדוגמא:

+ +
myButton.onclick = function() {
+  alert('hello');
+  // I can put as much code
+  // inside here as I want
+}
+ +

הפרמטרים של הפונקציה

+ +

חלק מהפונקציות דורשות שיינתנו להם פרמטרים מסויימים כשאנחנו מפעילים אותן - אלו בעצם ערכים שאנחנו צריכים לכלול בתוך הסוגריים הרגילות (), וזאת על מנת שהפונקציה תוכל לבצע את מה שהיא צריכה לבצע.

+ +
+

לתשומת לב: פרמטרים אלו נקראים ארגומנטים

+
+ +

כדוגמא, הפונקציה המובנית של הדפדפן לא דורשת שום פרמטרים. כשאנחנו קוראים לה, היא מחזירה מספר עשרוני בין 0 ל-1. ()Math.random:

+ +
let myNumber = Math.random();
+ +

הפונקציה המובנית של הדפדפן ()replace לעומת זאת, צריכה שני ארגומנטים - מחרוזת משנה שנרצה לחפש בתוך מחרוזת ראשית ואת המחרוזת המשנה שתחליף את זו שמצאנו בתוך המחרוזת הראשית:

+ +
let myText = 'I am a string';
+let newString = myText.replace('string', 'sausage');
+ +
+

שימו לב: כשאנחנו צריכים לציין מספר של ארגומנטים, אנחנו נפריד ביניהם באמצעות פסיק - ,.

+
+ +

חשוב לדעת כי לפעמים ארגומנטים יהיו אופציונליים - כלומר אנחנו לא נהיה חייבים לציין אותם. אם לא נציין אותם כאשר הם אופציונליים, הפונקציה בדרך כלל תאמץ סוג של התנהגות ברירת מחדל. לדוגמא, הפרמטר של פונקציה ()join של מערכים הוא אופציונלי:

+ +
let myArray = ['I', 'love', 'chocolate', 'frogs'];
+let madeAString = myArray.join(' ');
+// returns 'I love chocolate frogs'
+let madeAString = myArray.join();
+// returns 'I,love,chocolate,frogs'
+ +

אם לא נציין אותו, ערך ברירת המחדל הוא שיהיה התו המחבר - במקרה הזה זה יהיה פסיק ,.

+ +

סקופ של הפונקציה והתנגשויות

+ +

בוא נדבר מעט על {{glossary("scope")}} - עקרון חשוב מאוד להבנה כאשר אנחנו עוסקים בפונקציות. כאשר אנחנו יוצרים פונקציה, המשתנים וכל הדברים האחרים המוגדרים בתוך הפונקציה נמצאים בתוך scope נפרד, כלומר, הם נעולים בתוך מתחם מסויים, שלא ניתן לגשת אליו מחוץ לפונקציה.

+ +

הסקופ או התחום שנמצא מחוץ לכל הפונקציות שלנו נקרא global scope. ערכים שמוגדרים ב-global scope נגישים מכל מקום בקוד.

+ +

JavaScript נבנתה כך בצורה מסיבות מסויימות - אבל בעיקר מסיבות של אבטחה וארגון. לפעמים אנחנו לא נראה שמשתנים יהיו נגישים מכל מקום בקוד - סקריפטים חיצוניים שאנחנו מפעילים ממקומות אחרים יכולים לעשות לנו בלאגן ולגרום לשגיאות במקרה והם ישתמשו בשמות משתנים זהים ועלולים ליצור התנגשויות בין אם בטעות ובין אם מכוון.

+ +

לדוגמא, נניח ויש לנו קובץ HTML אשר קורא לשני קבצי JavaScript חיצוניים, ובשניהם יש משתנה ופונקציה מוגדרים שמשתמשים באותו שם:

+ +
<!-- Excerpt from my HTML -->
+<script src="first.js"></script>
+<script src="second.js"></script>
+<script>
+  greeting();
+</script>
+ +
// first.js
+var name = 'Chris';
+function greeting() {
+  alert('Hello ' + name + ': welcome to our company.');
+}
+ +
// second.js
+var name = 'Zaptec';
+function greeting() {
+  alert('Our company is called ' + name + '.');
+}
+ +

שתי הפונקציות שאנחנו רוצים לקרוא להם נקראות ()greeting, אבל אנחנו יכולים לגשת רק לפונקציה ()greeting שבקובץ second.js - שכן הקישור לקובץ מבוצע ב -HTML מאוחר יותר בקוד שלנו, ולכן המשתנים והפונקציה שלו דורסים את אלו שב- first.js.

+ +
+

לתשומת לב: ניתן לראות את הדוגמא ב-GitHub וכן את קוד המקור).

+
+ +

שמירה על הקוד שלנו נעול בתוך סקופ מונע בעיות שכאלו ונחשב כ- best practice.

+ +

ניתן לחשוב על זה כמו גן חיות. האריות, זברות, נמרים ופינגווינים נמצאים כל במתחמים נפרדים כל אחד, ויש להם גישה רק לדברים שנמצאים במתחם שלהם - בדיוק כמו בסקופים של פונקציות. אם הם היו יכולים להיכנס למתחמים אחרים, היו נוצרות לא מעט בעיות. במקרה הטוב, החיות האחרות היו מרגישות לא בנוח, במקרה הרע, הן היו נאכלות על ידי חיה אחרת.

+ +

+ +

למידה אקטיבית: לשחק עם ה-scope

+ +

נסתכל על דוגמא על מנת להבין מהו scope.

+ +
    +
  1. ראשית, צרו לעצמכם עותק של הדוגמא שלנו function-scope.html. הקובץ מכיל 2 פונקציות שנקראות ()a ו- ()b וכן שלושה משתנים - x, y, ו- z - שניים מתוכם מוגדרים בתוך פונקציות ואחד מתוכם מוגדר ב-global scope. בנוסף, הדוגמא גם מכילה פונקציה שלישית שנקראת ()output, אשר מקבלת ארגומנט אחד ומציגה אותו בתוך פסקה על גבי הדף.
  2. +
  3. פתחו את הדוגמא בדפדפן ובעורך הקוד שלכם.
  4. +
  5. הקלידו בקונסולה את הקוד הבא: +
    output(x);
    + אתם אמורים לראות שהערך של המשתנה x הוצג למסך.
  6. +
  7. כעת נסו להזין את הקוד הבא לקונסולה: +
    output(y);
    +output(z);
    + שתי הפקודות הללו יחזירו לנו שגיאה ביחד עם המשפט: "ReferenceError: y is not defined". מדוע? הסיבה נעוצה ב-scope של הפונקציות: - y ו- z נעולים בתוך הפונקציות ()a ו- ()b ולכן הפונקציה ()output לא יכולה לגשת אליהם כשהיא נקראת מה-global scope.
  8. +
  9. עם זאת, מה לדעתכם יקרה כשנקרא לפונקציה ()output מתוך הפונקציות? נסו לשנות את ()a ו- ()b שייראו כך: +
    function a() {
    +  let y = 2;
    +  output(y);
    +}
    +
    +function b() {
    +  let z = 3;
    +  output(z);
    +}
    + שמרו את הקוד ואז העלו מחדש את הדף בדפדפן ונסו לקרוא לפונקציות ()a ו- ()b מהקונסולה: + +
    a();
    +b();
    + אתם אמורים לראות את הערכים של y ו- z על גבי הדף. זה עובד מכיוון שהפונקציה ()output מופעלת מתוך פונקציה אחרת - כלומר מתוך אותו סקופ שבו מוגדרים המשתנים שהיא מדפיסה. הפונקציה ()output עצמה זמיני מכל מקום, שכן היא מוגדרת ב-global scope.
  10. +
  11. נסו עכשיו לעדכן את הקוד שלכם כך: +
    function a() {
    +  var y = 2;
    +  output(x);
    +}
    +
    +function b() {
    +  var z = 3;
    +  output(x);
    +}
    + רענון והעלו את הדף שוב והזינו את הקוד הבא בקונסולה:
  12. +
  13. +
    a();
    +b();
    + גם a() וגם b() מחזירים את הערך של x — 1. זה עובד מכיוון שלמרות ש-()output לא מוגדרת באותו סקופ ש- x מוגדר בו, אבל x הוא משתנה גלובלי אז הוא זמין בכל מקום בקוד.
  14. +
  15. לבסוף, עדכנו את הקוד כך: +
    function a() {
    +  var y = 2;
    +  output(z);
    +}
    +
    +function b() {
    +  var z = 3;
    +  output(y);
    +}
    + שמור ורענן את הדף. לאחר מכן הזן את הקוד הבא בקונסולה:
  16. +
  17. +
    a();
    +b();
    + הפעם כשקראנו ל- ()a ו- ()b אנחנו נקבל שגיאה מסוג "ReferenceError: z is not defined" זה מכיוון שביצוע הקוד ()output והמשתנים שהם מנסים להשיג לא מוגדרים בתוך אותו סקופ של פונקציה - המשתנים בעיקרון בלתי נראים לקריאות הפונציה הזו.
  18. +
+ +
+

לתשומת לבכם: אותו מנגנון סקופינג לא חל על לולאות { ... }(...) for ובלוקים של תנאים - { ... }(...) if הם אמנם נראים דומים, אבל הם לא אותו דבר. אל תתבלבלו.

+
+ +
+

לתשומת לבכם: השגיאה ReferenceError: "x" is not defined היא אחת מהשגיאות השכיחות שתיתקלו בה. אם קיבלתם שגיאה שכזו, וודאו שהגדרת את המשתנה הרלוונטי ובסקופ הרלוונטי.

+
+ + + +

פונקציות בתוך פונקציות

+ +

זכרו שאנחנו יכולים לקרוא לפונקציה מכל מקום, גם מתוך פונקציה אחרת. זה שימושי על מנת על מנת להשאיר את הקוד שלנו מסודר - אם יש לנו פונקציה אחת מורכבת וגדולה יהיה יותר ברור להבין אותה אם נפרק אותה לכמה פונקציות:

+ +
function myBigFunction() {
+  var myValue;
+
+  subFunction1();
+  subFunction2();
+  subFunction3();
+}
+
+function subFunction1() {
+  console.log(myValue);
+}
+
+function subFunction2() {
+  console.log(myValue);
+}
+
+function subFunction3() {
+  console.log(myValue);
+}
+
+ +

חשוב לוודא שהערכים שמבוצע בהם שימוש בתוך הפונקציה, מוגדרים בסקופ הנכון. הדוגמא למעלה תחזיר לנו שגיאה ReferenceError: myValue is not defined מכיוון שאמנם המשתנה myValue מוגדר באותו סקופ שהפונקציה נקראת, אך הוא לא מוגדר בתוך הפונקציות עצמן - בתוך הקוד שירוץ בפועל כשאנחנו קוראים לתתי פונקציות. על מנת לגרום לכך לעבוד, היינו צריכים להעביר אליהם את הפונקציה כפרמטר כמו שרשום בקוד מטה:

+ +
function myBigFunction() {
+  var myValue = 1;
+
+  subFunction1(myValue);
+  subFunction2(myValue);
+  subFunction3(myValue);
+}
+
+function subFunction1(value) {
+  console.log(value);
+}
+
+function subFunction2(value) {
+  console.log(value);
+}
+
+function subFunction3(value) {
+  console.log(value);
+}
+ +

לסיכום

+ +

מאמר זה סקר את העקרונות יסוד של פונקציות, על מנת לסלול את הדרך שלכם להבנה של כלים שימושיים נוספות ולהבנה כיצד לבנות פונקציה משלכם.

+ +

ראו גם

+ + + + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/building_blocks/index.html b/files/he/learn/javascript/building_blocks/index.html new file mode 100644 index 0000000000..e60d618b4e --- /dev/null +++ b/files/he/learn/javascript/building_blocks/index.html @@ -0,0 +1,51 @@ +--- +title: אבני הבנין של JavaScript +slug: Learn/JavaScript/Building_blocks +tags: + - JavaScript + - events + - אירועים + - לולאות + - מדריך + - ערכים מוחזרים מפונקציה + - פונקציות + - תנאים - Conditionals +translation_of: Learn/JavaScript/Building_blocks +--- +
{{LearnSidebar}}
+ +

במודול זה, אנחנו נמשיך להעמיק ביסודות ובמאפיינים והחשובים של JavaScript ונתמקד בסוגים שונים של קוד כגון משפטי תנאי (conditional statements),לולאות (loops), פונקציות (functions), ואירועים (events). חלק מהם כבר ראינו בשלבים קודמים של הקורס, אך לא העמקנו בהם. במודול זה ניכנס לעומק של אותן אבני בניין ונבין כיצד הן עובדות ומה מטרתן.

+ +

ידע מוקדם

+ +

לפני שתתחילו את המודול הזה, אנא וודאו כי הנכם בקיאים ביסודות HTML ושל CSS, וכן סיימתם לעבור על המודול הקודם שלנו צעדים ראשונים ב-JavaScript.

+ +
+

הערה: אם אתם עובדים על מחשב\טבלט\מכשיר אחר שאין לכם אפשרות ליצור עליו קבצים אישיים, אתם יכולים לנסות את (רוב) דוגמאות הקוד על תוכנות קוד אינטרנטיות כמו JSBin או Thimble.

+
+ +

מדריכים

+ +
+
קבלת החלטות בקוד - משפטי תנאי - Conditionals
+
כמו בכל שפת תכנות, קוד צריך לקבל החלטות ולבצע פעולות מסויימות בהתאם למקרים שונים. לדוגמא - במשחק, אם מספר הנסיונות המותרים של השחקן עבר את מספר הנסיונות המקסימלי שהוגדר, המשמעות היא שהמשחק הסתיים. או יישום אינטרנט של מזג אוויר אשר יציג למשתמש עיצוב מסויים של מזג האוויר בהתאם לשעה הנוכחית ביום, כך שבשעות היום יוצג עיצוב בהיר ובשעות הערב יוצג עיצוב כהה. במאמר זה אנחנו נחקור את המבנה של משפטי תנאי ב-JavaScript וכיצד הם עובדים.
+
לולאות - Loops
+
לפעמים אנחנו נצטרך לבצע את אותה פעולה או משימה ברצף, יותר מפעם אחת. לדוגמא -  כשנרצה לעבור רשימת שמות. בתכנות, לולאות יכולות לעשות את העבודה הזו בצורה מצויינת. במאמר זה אנחנו נחקור את המבנה של לולאות ב- JavaScript וכיצד הן עובדות.
+
פונקציות - בלוקי קוד לשימוש חוזר - Functions
+
קונספט הכרחי חשוב בתכנות הוא פונקציות. פונקציות מאפשרות לנו לאחסן חלקי קוד שמבצעים פעולה מסויימת בתוך בלוק מוגדר, ואז לקרוא לאותו קוד כשנצטרך להשתמש בו, באמצעות פקודה קצרה - וזאת במקום לרשום את אותו קוד פעם אחר פעם, כל פעם מחדש. במאמר זה נחקור את המבנה של פונקציה והרעיון העומד מאחוריה, ונבין מה הוא הסינטקס הבסיסי על מנת לרשום פונקציה, כיצד אנו קוראים לפונקציה ועוד. כמו כן, נלמד מהם ארגומנטים או פרמטרים אשר הפונקציות יכולות לקבל וכן מהו Scope.
+
בניית פונקציות משלנו
+
לאחר שנסיים לעבור על התיאוריה ונבין מהן פונקציות, במאמר זה נתרגל ונבין בצורה מעשית כיצד לבנות פונקציות משלנו, בהתאם לפעולת שנרצה לבצע. בהמשך הדרך, אנחנו נסביר גם פרטים שימושיים כיצד לטפל בפונקציות ובערכים שהן יכולות להחזיר לנו. 
+
ערכים מוחזרים מהפונקציה - Function return values
+
עקרון הכרחי וחשוב שאנחנו צריכים להכיר לגבי פונקציות הוא אילו ערכים פונקציות מחזירות לנו, אם בכלל. ישנן פונקציות שלא מחזירות ערכים לאחר שסיימו להריץ את הקוד שבתוכן, וישנן פונקציות שיחזירו לנו ערכים מסויימים. במאמר זה אנחנו נבין מהם אותם ערכים מוחזרים, כיצד אנחנו יכולים להשתמש בהם וכיצד אנחנו יכולים לגרום לפונקציות שלנו להחזיר ערכים. 
+
היכרות ראשונית עם אירועים -Introduction to events
+
אירועים אלו בעצם פעולות או התרחשויות אשר קורים במערכת, אשר אנחנו יכולים ״להאזין״ להם כך שנוכל להגיב בדרך מסויימת, ברגע שהם יתרחשו. לדוגמא, כאשר משתמש לוחץ על כפתור, אולי נרצה להגיב לאותה התרחשות, לאותו אירוע של לחיצת הכפתור על ידי הקפצת הודעה מסויימת. במאמר אחרון זה אנחנו נדון בכמה מהעקרונות החשובים בהקשר של אירועים, ונסתכל כיצד הם עובדים בדפדפנים.
+
+ +

תרגול

+ +

התרגול הבא יבדוק את ההבנה שלכם של החומר שנלמד במודול זה

+ +
+
בניית גלריית תמונות
+
אנו נתרגל שימוש בלולאות ופונקציות וכן בתנאים ואירועים על ידי בנייה של גלריית תמונות.
+
diff --git a/files/he/learn/javascript/building_blocks/looping_code/index.html b/files/he/learn/javascript/building_blocks/looping_code/index.html new file mode 100644 index 0000000000..b9067199e7 --- /dev/null +++ b/files/he/learn/javascript/building_blocks/looping_code/index.html @@ -0,0 +1,931 @@ +--- +title: לולאות +slug: Learn/JavaScript/Building_blocks/Looping_code +translation_of: Learn/JavaScript/Building_blocks/Looping_code +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}
+ +

שפות תכנות הן שימושיות מאוד כשזה קשור לביצוע משימות מסויימות שוב ושוב. מביצוע חישובים מתמטיים עד לכל דבר שאנחנו יכולים לחשוב עליו. במאמר זה נסתכל על מבנים שלו לולאות שזמינים עבורנו ב-JavaScript.

+ + + + + + + + + + + + +
ידע מוקדם:הכרות בסיסית עם המחשב ועם הבסיס של HTML ו- CSS, וכן סיום במלאו של מודול צעדים ראשונים ב-JavaScript.
מטרה:להבין כיצד להשתמש בלולאות ב-JavaScript ומתי.
+ +

מה הן לולאות?

+ +

לולאו הן עקרון חשוב מאוד בתכנות. לולאות קוד מכילות פעולות שנרצה לבצע שוב ושוב - ובשפה מקצועית - איטראציה - Iteration.

+ +

יש הרבה סוגי לולאות - אבל כולן עושות בעיקרון את אותו דבר: הן חוזרות על פעולה מסויימת מספר פעמים (ואף יכול להיות גם 0 פעמים).

+ +

ננסה להבין זאת באמצעות דוגמא פשוטה. נניח ויש לנו חקלאי אשר רוצה לוודא שיש לו מספיק אוכל למשפחה להמשך השבוע. הוא עשוי להשתמש בלולאה הבאה על מנת לעשות זאת:

+ +


+

+ +

ללולאה יהיו בדרך כלל אחד או יותר (לא בהכרח את כולם) מהמאפיינים הבאים:

+ + + +

למה צריך לולאות?

+ +

בשלב זה כנראה הבנו את הרעיון העומד מאחורי לולאות. כעת ננסה להבין כיצד זה עוזר לנו לבצע פעולות מסויימות שוב ושוב. 

+ +

בדרך כלל, הקוד שלנו יהיה מעט שונה בכל ריצה של הלולאה (כלומר - בכל איטראציה מוצלחת של הלולאה). דבר זה מאפשר לנו להשלים את ביצוען של כמות רבה של משימות דומות, אך שונות מעט. כך לדוגמא, אם יש לנו כמות רבה של חישובים שונים אשר נרצה לבצע קצת אחרת בכל ריצה/איטראציה.

+ +

נסתכל על דוגמא נוספת על מנת להסביר את הנושא. נניח שאנחנו רוצים לצייר 100 עיגולים אקראיים על אלמנט מסוג {{htmlelement("canvas")}}, ובאמצעות לחיצה על כפתור Update אנחנו נרצה לקבל סט חדש ואחר של 100 עיגולים אקראיים:

+ + + +

{{ EmbedLiveSample('Hidden_code', '100%', 400, "", "", "hide-codepen-jsfiddle") }}

+ +

אתם לא צריכים להבין את כל הקוד הרשום למעלה, אבל הסתכלו לעומק על החלק שמצייר בפועל 100 כדורים:

+ +
for (let i = 0; i < 100; i++) {
+  ctx.beginPath();
+  ctx.fillStyle = 'rgba(255,0,0,0.5)';
+  ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+  ctx.fill();
+}
+ + + +

אתם מבינים את הרעיון - אנחנו משתמשים בלולאה על מנת לרוץ 100 איטראציות של הקוד הזה, כאשר בכל ריצה כזו נקבל עיגול במיקום אקראי אחר. אם נרצה יותר מ-100 עיגולים פשוט נשנה מספר אחד!.

+ +

אם לא היינו משתמשים בלולאה, היינו צריכים לרשום את הקוד הבא שוב ושוב בהתאם לכמות הפעמים שהיינו רוצים שיצוייר עיגול:

+ +
ctx.beginPath();
+ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ctx.fill();
+ +

לולאת for

+ +

נחקור כיצד לולאות מסויימות בנויות. סוגי הלולאות השונים מאפשרים לנו דרכים שונות על מנת לקבוע את נקודת ההתחלה והעצירה של הלולאה.

+ +

הראשונה, שאנחנו נשתמש בה הרבה מאוד פעמים, היא לולאה מסוג for. לולאה זו היא בעלת הסינטקס הבא:

+ +
for (initializer; exit-condition; final-expression) {
+  // code to run
+}
+ +

הסבר:

+ +
    +
  1. המילה השמורה for, ולאחריה סוגריים רגילות ().
  2. +
  3. בתוך הסוגריים הרגילות () יש לנו 3 ביטויים, מופרדים על ידי ;: +
      +
    1. initializer  - מונה-מאתחל — פה אנו נראה בדרך כלל הצהרה על משתנה, בדרך כלל משתנה let אשר אנחנו נותנים לו ערך (מספר) התחלתי לפני שהלולאה רצה. הוא ישמש את הלולאה כמונה של מספר הריצה הרלוונטית. 
    2. +
    3. exit-condition - תנאי יציאה — זהו התנאי שנבדק בטרם כל איטרציה. אם תוצאת המבחן היא true, הלולאה תמשיך לרוץ והביטוי שבתוך הסוגריים המסולסלות יבוצע. אם תוצאת המבחן היא false הלולאה תפסיק לרוץ. תנאי היציאה הוא בדרך כלל ביטוי הכולל אופטורים להשוואה - מבחנים לבדיקה האם התקיים תנאי מסויים.
    4. +
    5. final-expression - iterator - ביטוי לקידום הלולאה — ביטוי זה יבוצע או ירוץ בכל פעם שהלולאה ביצעה ריצה/איטראציה במלואה. ביטוי זה משמש בדרך כלל להעלות (או להוריד) את המונה-מאתחל על מנת לקרב אותו לקיום תנאי היציאה.
    6. +
    7. שימו לב כי כל הביטויים האלו הם אופציונליים - אך לא ניכנס לאפשרויות השונות לעומק. אתם מוזמנים להרחיב בנושא בדף בנושא לולאת for
    8. +
    +
  4. +
  5. לאחר מכן יש לנו סוגריים מסולסלות {...} שכוללות בתוכן קוד אשר ירוץ בכל פעם שהלולאה מבצעת איטרציה.
  6. +
+ +

נסתכל על הדוגמא הבאה:

+ +
const cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
+let info = 'My cats are called ';
+const para = document.querySelector('p');
+
+for (let i = 0; i < cats.length; i++) {
+  info += cats[i] + ', ';
+}
+
+para.textContent = info;
+ +

הקוד למעלה יציג לנו את הפלט הבא:

+ + + +

{{ EmbedLiveSample('Hidden_code_2', '100%', 60, "", "", "hide-codepen-jsfiddle") }}

+ +
+

תשומת לב: ניתן למצוא את הקוד ב- GitHub או לראות אותו פה.

+
+ +

קוד זה מראה לולאת for שמבצעת איטרציה על הפריטים במערך ועושה משהו עם כל אחד מהם - זוהי תבנית שנראה שוב ושוב ב-JavaScript. בדוגמא זו למשל:

+ +
    +
  1. המונה: לפעמים גם נקרא כמאתחל, הינו משתנה i, והוא מתחיל ב-0.   (let i = 0).
  2. +
  3. בדיקת תנאי יציאה/עצירת הלולאה: הלולאה תרוץ כל עוד i קטן יותר ממספר האיברים שנמצאים במערך cats. (מציאת כמות האיברים שנמצאת במערך זה מתבצעת באמצעות cats.length. תנאי היציאה הוא חשוב ביצירת לולאה - הוא קובע מהו התנאי אשר רק כאשר תוצאות תהיה true, הלולאה תמשיך ותבצע איטרציה נוספת. במקרה הזה, כל עוד i < cats.length עדיין נכון, הלולאה תמשיך לרוץ.
  4. +
  5. הרצת הקוד שבתוך הסוגריים המסולסלות: בתוך הלולאה, אנחנו מחברים בין הפריט הנוכחי שהלולאה מבצעת עליו איטרציה [הערך של i באותה ריצה] cats,  ביחד עם פסיק ורווח בסוף המשתנה info: +
      +
    1. במהלך הריצה הראשונה - האיטרציה הראשונה, , i = 0, ולכן cats[0] + ', ' (שהוא שווה ל-Bill, ) יצורף לתוך info.
    2. +
    3. במהלך הריצה השנייה - האיטרציה השנייה , i = 1, ולכן cats[1] + ', ' (אשר שווה ל- Jeff, ) יצורך גם הוא לתוך info.
    4. +
    +
  6. +
  7. קירוב המונה לתנאי היציאה: אחרי כל ריצה של הלולאה, נוסיף 1 ל-i באמצעות ++iשימו לב - רק אחרי שהקוד שבתוך הסוגריים המסולסלות מבוצע, מתבצעת הוספה של 1 למשתנה i וכך הלאה.
  8. +
  9. ביצוע בדיקה חוזרת לתנאי העצירה ועמידה בו:  לאחר קירוב המונה לתנאי העצירה, תבוצע בדיקה חוזרת האם התנאי מתקיים -  כאשר תוצאת התנאי תהיה שוב true, הלולאה תמשיך ותבצע איטרציה נוספת.
  10. +
  11. רק כאשר i יהיה שווה ל- cats.length (במקרה זה, 5), הלולאה תפסיק שכן הערך המתקבל בתוצאה הוא false והדפדפן יעבור לקוד שמתחת לולאה. 
  12. +
+ +
+

לתשומת לבכם: רשמנו את תנאי היציאה כ- i < cats.length, ולא i <= cats.length, מכיוון שמחשבים מתחילים לספור מ-0 ולא מ-1 — אנחנו מתחילים כאשר i שווה ל0, וממשיכים עד אשר i = 4 (האינדקס של האיבר האחרון במערך).

+ +

 cats.length יחזיר 5, ומכיוון שאכן ישנם 5 פריטים, אבל הפריט החמישי של cats.length, נמצא באינדקס מס׳ 4. ולכן אנחנו לא רוצים את cats.length אלא את cats.length פחות אחד. אם נשים רק את cats.length, כאשר   i = 5  המבחן יחזיר לנו ערך של  undefined בעבור הפריט האחרון - שכן אין איבר באינדקס 5. ולכן, אנחנו נרצה להריץ את הלולאה מספר 1 פחות.

+
+ +
+

לתשומת לב: טעות נפוצה עם תנאי יציאה היא להשתמש עם (״שווה ל-״)  (===)  במקום עם (״קטן מ- או שווה ל-״) (<=) .

+ +

אם אנחנו נרצה להריץ את הלולאה שלנו עד אשר i = 5, תנאי היציאה יצטרך להיות i <= cats.length.אם נקבע אותו כ i === cats.length  אזי הלולאה לא תרוץ בכלל מכיוון ש-i לא שווה ל-5 באיטרציה הראשונה של הלולאה, ולכן הלולאה תעצור במיידי.

+
+ +

בעיה אחת קטנה שנותרה לנו היא שהמשפט בסופה של הלולאה לא מסודר כראוי במלואו שכן בריצה האחרונה גם הוספנו פסיק ולכן יש לנו פסיק בסוף המשפט.

+ +
+

My cats are called Bill, Jeff, Pete, Biggles, Jasmin,

+
+ +

באופן הגיוני, אנחנו נרצה לשנות את צורת חיבור המחרוזת כך שבאיטרציה האחרונה של הלולאה, לא נקבל פסיק בסוף המשפט. לשם כך אנחנו יכולים להכניס משפט if בתוך לולאת for שלנו על מנת לטפל במקרה זה:

+ +
for (let i = 0; i < cats.length; i++) {
+  if (i === cats.length - 1) {
+    info += 'and ' + cats[i] + '.';
+  } else {
+    info += cats[i] + ', ';
+  }
+}
+ +
+

לתשומת לב: ניתן למצוא את קוד הדוגמא הזו ב- GitHub או כ- דף אינטרנט).

+
+ +
+

זהירות: בלולאת for — כמו ביתר הלולאות, אנחנו צריכים לוודא שהמונה שלנו עולה או יורד, בהתאם למקרה הרלוונטי, כך שבשלב מסויים הוא יגיע לתנאי היציאה. אם לא - הלולאה תמשיך בלי סוף, ותגרום לכך שהדפדפן יעצור אותה או יקרוס. דבר כזה נקרא לולאה אינסופית.

+
+ +

יציאה מלולאות באמצעות break

+ +

אם אנחנו רוצים לצאת מלולאה לפני שכל האיטרציות הושלמו, אנחנו יכולים להשתמש בביטוי break. ראינו אותו בעבר כשלמדנו על משפטי תנאי מסוג switch (כאשר ביטוי מסויים עונה למקרה מסויים - break עוצר באופן מיידי את המשך הבדיקה וממשיך לקוד שלאחר משפט switch).

+ +

בדיוק אותו הדבר כמו עם לולאות - הביטוי break יגרום ליציאה מיידית מהלולאה והדפדפן ימשיך לקוד שנמצא לאחר מכן.

+ +

נניח ואנחנו רוצים לחפש בתוך מערך של אנשי קשר וטלפונים, ואז להחזיר רק את המספר שאנחנו רוצים למצוא? נתחיל ב-HTML פשוט - תיבת טקסט + {{htmlelement("input")}}, המאפשרת לנו להכניס את השם שנרצה לחפש ואלמנט מסוג כפתור {{htmlelement("button")}} על מנת לשלוח את החיפוש וכן אלמנט של פסקה  {{htmlelement("p")}} על מנת להציג בו את התוצאות.

+ +
<label for="search">Search by contact name: </label>
+<input id="search" type="text">
+<button>Search</button>
+
+<p></p>
+ +

כעת נכניס - JavaScript:

+ +
const contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
+const para = document.querySelector('p');
+const input = document.querySelector('input');
+const btn = document.querySelector('button');
+
+btn.addEventListener('click', function() {
+  let searchName = input.value.toLowerCase();
+  input.value = '';
+  input.focus();
+  for (let i = 0; i < contacts.length; i++) {
+    let splitContact = contacts[i].split(':');
+    if (splitContact[0].toLowerCase() === searchName) {
+      para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
+      break;
+    } else {
+      para.textContent = 'Contact not found.';
+    }
+  }
+});
+ + + +

{{ EmbedLiveSample('Hidden_code_3', '100%', 100, "", "", "hide-codepen-jsfiddle") }}

+ +
    +
  1. ראשית - הגדרנו קבועים - יש לנו מערך עם פרטי קשר - כאשר כל איבר במערך הוא מחרוזת המכילה שם ומספר טלפון המופרדים על ידי :. הגדרנו קבוע בשם para שמקבל הפנייה לאלמנט <p>. הגדרנו קבוע בשם input שמקבל הפניה לאלמנט <input> וקבוע בשם btn שמקבל הפניה ל-<button>
  2. +
  3. לאחר מכן חיברנו ״מאזין אירוע״ לכפתור - (btn), כך שבכל פעם שהוא יילחץ ירוץ קוד מסויים לביצוע החיפוש ויחזיר את התוצאה (event handler). במקרה הזה, זו הפונקציה האנונימית שנמצאת מיד לאחר הביטוי 'click'.
  4. +
  5. אחסנו את הערך שהוכנס לתוך תיבת הטקסט (input)  בתוך משתנה שנקרא searchName, לאחר מכן ריקנו את תיבת הטקסט מתוכן, ועשינו עליה פוקוס באמצעות מתודת ()focus , על מנת שתהיה מוכנה לחיפוש הבא. שימו לב שאנחנו גם הרצנו את מתודת ()toLowerCase על הערך שהתקבל ב-input כדי לנרמל את המחרוזת. 
  6. +
  7. ועכשיו לחלק המעניין של לולאת ה-for: +
      +
    1. אנחנו מתחילים את המונה ב-0, מתבצעת בדיקת עמידה בתנאי, ככל והערך המוחזר הוא true, מתבצעת איטרציה - הרצה של הקוד שבתוך הסוגריים המסולסלות של הלולאה: +
        +
      1. בתוך הלולאה אנחנו תחילה מחלצים כל חלק מהמחרוזת הראשית באמצעות מתודה שאנחנו מכירים ()split, אשר מקבלת את התו : כתו שיחצה את המחרוזת contacts בכל פעם שהמתודה תמצא את התו הזה. להזכירכם מתודה זו מחזירה מערך של מחרוזות שהופרדו על ידי התו שהיא קיבלה. במקרה הזה אנחנו מאחסנים במשתנה בשם splitContact את המערך החדש שהוחזר לנו בכל איטרציה שזה בעצם מערך עם שני איברים: שם ומספר. 
      2. +
      3. לאחר מכן אנחנו משתמשים במשפט תנאי לבדוק האם [splitContact[0 שזה בעצם שם האדם מנורמל באמצעות()toLowerCase, שווה לטקסט שנמצא ב-searchName. אם כן, אנחנו נכניס מחרוזת לתוך הפסקה עם הטלפון שלו שזה בעצם [splitContact[1, ונשתמש ב-break על מנת לעצור את הלולאה. 
      4. +
      +
    2. +
    3. לאחר מכן מתבצעת הגדלת של ה-i ב-1 בכל איטרציה מוצלחת של הלולאה באמצעות ++i.
    4. +
    +
  8. +
  9. אחרי כל האיטרציות, כאשר i יהיה שווה לאיבר האחרון במערך של המחרוזת הראשית - כלומר אחרי (contacts.length-1) אם searchName לא זהה לאף  [splitContact[i, אז טקטסט של הפסקה הופך ל- "Contact not found."  
  10. +
+ +
+

לתשומת לב: ניתן למצוא את קוד המקור - GitHub code on GitHub או כדף אינטרנט.

+
+ +

דילוג על איטרציה עם continue

+ +

ביטוי ה-continue עובד בדרך דומה לביטוי break, רק שבמקום לעצור את הלולאה ולצאת ממנה, הוא פשוט ממשיך לאיטרציה הבאה של הלולאה. נעבור כעת על דוגמא נוספת שבה אנחנו נזין מספר מקבל מספר כערך, ומחזיקה רק המספרים שהם מספרים שלמים.

+ +

ה-HTML  בעיקרון דומה לדוגמא הקודמת - יש לנו תיבת טקסט פשוטה ופסקה להציג את הפלט. ה-JavaScript גם דומה, למרות שהלולאה שלנו קצת אחרת:

+ +
let num = input.value;
+
+for (let i = 1; i <= num; i++) {
+  let sqRoot = Math.sqrt(i);
+  if (Math.floor(sqRoot) !== sqRoot) {
+    continue;
+  }
+
+  para.textContent += i + ' ';
+}
+ +

זה הפלט שלנו:

+ + + +

{{ EmbedLiveSample('Hidden_code_4', '100%', 100, "", "", "hide-codepen-jsfiddle") }}

+ +
    +
  1. במקרה הזה, הקלט צריך להיות מספר - num. אנו נותנים ללולאת ה-for מונה שמתחיל ב-1 (אנחנו לא מעוניינים ב- 0 במקרה הנוכחי), תנאי יציאה שאומר שהלולאה תפסיק כאשר המונה יהיה גדול מהמספר שהכנסנו - מ-num, ואז ביטוי העלאה שמוסיף למונה 1 בכל איטרציה. 
  2. +
  3. בתוך הלולאה, אנחנו מוצאים את השורש הריבועי של כל מספר (של כל num) באמצעות שימוש במתודה ()Math.sqrt, ואז בודקים האם השורש הריבועי הוא שלם על ידי בדיקה האם הוא זהה לעצמו כשהוא מעוגל כלפי מטה לשלם הקרוב - זה מה שמתודת ()Math.floor עושה למספר שמועבר אליה - היא מחזירה את המספר השלם הגדול ביותר אשר נמוך מהמספר שהבאנו לה או השווה לו.
  4. +
  5. אם השורש הריבועי והמספר שעוגל כלפי מטה אינהם זהים אחד לשני - (!==), המשמעות היא שהשורש הריבועי הוא לא מספר שלם, ולכן אנחנו לא מעוניינים בו. במקרה כזה, אנחנו נשתמש בביטוי continue על מנת לעבור לאיטרציה הבאה, אבל מבלי להמשיך להריץ את קוד אשר נמצא בהמשך האיטרציה הנוכחית (וביתר האיטרציות) ומבלי לצאת מהלולאה.
  6. +
  7. אם השורש הריבוע הוא מספר שלם, אנחנו לא עומדים בתנאי שרשום במשפט ה-if ולכן המשפט continue לא מורץ. במקום, אנחנו מצרפים את הערך שבתוך i בצירוף רווח, לסוף של הטקסט שבתוך הפסקה. 
  8. +
+ +
+

לתשומת לב: ניתן לראות את  קוד המקור ב-GitHub או לראות את הדף אינטרנט.

+
+ +

while ו-do ... while

+ +

לולאות for הן לא הלולאות היחידות שיש לנו ב-JavaScript. האמת שיש עוד הרבה אחרות. אנחנו ללא צריכים לדעת את כולן כעת, אבל שווה יהיה להעיף מבט בכמה ונבין שאפשרויות שונות עובדים בצורה שונה.

+ +

לולאת while מורכבת מהסינטקס הבא:

+ +
initializer
+while (exit-condition) {
+  // code to run
+
+  final-expression
+}
+ +

לולאה זו עובדת בצורה דומה ללולאת for, למעט העובדה שהערך המאתחל נקבע לפני הלולאה, והביטוי שיביא למימוש תנאי היציאה יהיה כלול בתוך הסוגריים המסולסלות {}. תנאי היציאה נכלל בתוך המרכאות העגולים, כאשר לפני המרכאות יש את המילה השמורה while ולא for.

+ +

משפטי while ממשיכים לפעול עד שהתנאי המופיע בראש המשפט אינו נכון עוד. שימו לב שניתן להכניס בלולאה זו את את כל שלושת הביטויים המוכרים לנו מלולאת for - ערך מאתחל (לא חובה), תנאי יציאה וביטוי  סופי שיבוצע בסוף האיטרציה (לא חובה). כלומר, הערך היחיד שחובה לכלול בלולאת while הוא התנאי ליציאה - אך יחד עם זאת, ראו הערה חשובה בסוף פסקה זו בדבר סיום ריצת הלולאה. 

+ +

בוא נסתכל שוב על רשימת החתולים, אבל נכתוב אותה באמצעות לולאת while:

+ +
let i = 0;
+
+while (i < cats.length) {
+  if (i === cats.length - 1) {
+    info += 'and ' + cats[i] + '.';
+  } else {
+    info += cats[i] + ', ';
+  }
+
+  i++;
+}
+ +
+

לתשומת לב: זה עובד כמו שציפינו — ראו את הדף עובד ב-GitHub או את קוד המקור).

+
+ +

לולאת do...while דומה מאוד ללולאת while אבל שונה מעט:

+ +
initializer
+do {
+  // code to run
+
+  final-expression
+} while (exit-condition)
+ +

במקרה הזה, המאתחל מגיע שוב ראשון, לפני שהלולאה מתחילה. המילה שמורה do ולאחר מכן סוגריים מסולסלות {} שבותכן ייכנס הקוד שנרה שירוץ בלולאה. ולסוף , המילה השמורה while ולאחרי קוד היציאה.

+ +

השוני כאן זה שתנאי היציאה מגיע בסוף, עטוף בתוך סוגריים רגילות (). בלולאת do...while, הקוד בתוך הסוגריים המסולסלות {...} תמיד ירוץ פעם אחת לפחות לפני בדיקת התנאי ואז יבדוק את התנאי על מנת לבדוק האם לרוץ שוב. להזכירכם - בלולאות while ובלולאות for, בדיקת התקיימות התנאי מתבצעת לפני הרצת הקוד שבתוך {...}  כך שיכול להיות שהקוד בלולאות אלו לא יבוצע לעולם. בלולאת do...while לעומת זאת, הקוד תמיד ירוץ פעם אחת לפחות.

+ +

בוא נכתוב את הדוגמא שלנו באמצעות הלולאה do...while:

+ +
let i = 0;
+
+do {
+  if (i === cats.length - 1) {
+    info += 'and ' + cats[i] + '.';
+  } else {
+    info += cats[i] + ', ';
+  }
+
+  i++;
+} while (i < cats.length);
+ +
+

לתשומת לב: הלולאה עובדת בדיוק כפי שרצינו - ראו ב- GitHub וכן את קוד המקור.

+
+ +
+

זהירות: בלולאות while  וב-do...while - אנחנו חייבם לוודא שהמאתחל מועלה או, בהתאם למקרה, יורד, כך שבסופו של דבר הקוד יגיע לתנאי היציאה והלולאה תסתיים. אחרת, הלולאה תמשיך עד אינסוף. במקרה כזה הדפדפן יכריח אותה להפסיק או שהוא יקרוס. זה נקרא לולאה אינסופית.

+
+ +

למידה עצמאית: בניית שעון ספירה לאחור

+ +

בתרגיל זה, אנחנו נרצה שתבנה תוכנית שתדפיס שעון עצר מ-10 ל-0 וכן:

+ + + + +

אם עשיתם טעות אתם תמיד יכולים לאתחל את הקוד באמצעות הכפתור "Reset" ואם ממש נתקתעם לחצו על כפתור "Show solution" לפתרון.

+ + + +

{{ EmbedLiveSample('Active_learning', '100%', 880, "", "", "hide-codepen-jsfiddle") }}

+ +

למידה עצמאית - מילוי של רשימת אורחים

+ +

+ בתרגיל זה, אנחנו רוצים שתיקחו רשימה של שמות שמאוחסנים במערך ותכניסו אותם לתוך רשימת אורחים. זה לא כזה קל - אנחנו רוצים רשימת אורחים שמורשים להיכנס ורשימת אורחים שלא מורשים להיכנס.

+ +

אתם מתבקשים לבצע את הדברים הבאים:

+ + + +

לבינתיים סיפקנו לכם:

+ + + +

+ שאלת בונוס - אחרי השלמת המשימה, אתם תישארו עם 2 רשימת שמות, מופרדות על ידי פסיקים, אבל לא מסודרות, שכן בסוף כל רשימה יש לנו פסיק. + מה ניתן לעשות כדי לחתוך את אותו פסיק או לבטל אותו? הסכתלו על +מתודות מחרוזות שימושיות לעזרה.

+ +

אם עשיתם טעות אתם תמיד יכולים לאתחל את הקוד באמצעות הכפתור "Reset" ואם ממש נתקתעם לחצו על כפתור "Show solution" לפתרון

+ + + +

{{ EmbedLiveSample('Active_learning_2', '100%', 680, "", "", "hide-codepen-jsfiddle") }}

+ +

באיזו לולאה עלינו להשתמש??

+ +

+ בדרך כךך, לשימושים בסיסים, הלולאות for, while, ו-do...while, הן חלופיות. כולן לרוב יכולות לפתור את אותן בעיות ואתם תחליטו במה לבחור.

+ +

תחילה לולאת for:

+ +
for (initializer; exit-condition; final-expression) {
+  // code to run
+}
+ +

לולאת while:

+ +
initializer
+while (exit-condition) {
+  // code to run
+
+  final-expression
+}
+ +

לולאת do...while:

+ +
initializer
+do {
+  // code to run
+
+  final-expression
+} while (exit-condition)
+ +

+ אנחנו ממליצים על לולאת _____, לפחות בהתחלה, כיוון שהיא לרוב הקלה ביותר לזכור - המאתחל, תנאי היציאה ומקדם/מחסיר, אשר כולם נכנסים בתוך הסוגריים הרגילות וכך קל לבדוק שלא שכחנו כלום.

+ +
+

לתשומת לב: + יש סוגי לולאות נוספים ואפשרויות נוספות ללולאות, גם לאלו שסקרנו במאמר זה. אפשרויות ולולאות אלו הן מאוד שימושיות במקרים מתקדמים או פרטניים ולא נגע בהם במאמר זה. אם תרצו להעמיק, ראו את הדף בנושא + Loops and iteration guide.

+
+ +

לסיכום

+ +

+ מאמר זה נועד להעניק לכם את העקרונות הבסיסים והאפשרויות העומדות בפניהם כאשר תרצו לבצע לולאה של קוד. אתם אמורים בלשב זה להבין את הרעיון והטכניקה שבה לולאות עובדות על מנת לבצע את אותו קוד שוב ושוב.

+ +

ראו גם

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/building_blocks/return_values/index.html b/files/he/learn/javascript/building_blocks/return_values/index.html new file mode 100644 index 0000000000..52e224289b --- /dev/null +++ b/files/he/learn/javascript/building_blocks/return_values/index.html @@ -0,0 +1,180 @@ +--- +title: return values - ערכים המוחזרים מפונקציה +slug: Learn/JavaScript/Building_blocks/Return_values +tags: + - Beginner + - Functions + - JavaScript + - Return values + - מדריך + - ערכים מוחזרים + - פונקציות +translation_of: Learn/JavaScript/Building_blocks/Return_values +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}
+ +

עקרון חשוב שנותר לנו לדון בו בהקשר של פונקציות הוא הערכים שהפונקציות יכולות להחזיר - return values. יש פונקציות שלא מחזירות ערך משמעותי אחרי שהן סיימו, אבל אחרות כן וזה חשוב שנבין מהם אותם ערכים, כיצד אנחנו יכולים לעשות בהם שימוש ואיך לגרום לפונקציות שאנחנו בונים להחזיר ערכים שנוכל להשתמש בהם. במאמר זה נדון בהיבטים אלו.

+ + + + + + + + + + + + +
ידע מוקדם: +

הכרות בסיסית עם המחשב ועם הבסיס של HTML ו- CSS, סיום במלואו של מודול צעדים ראשונים ב-JavaScript. וכן, את המאמר בנושא Functions — פונקציות - חלקי קוד לשימוש חוזר.

+
מטרה:להבין מהם return values, וכיצד להתשמש בהם.
+ +

מה הם return values?

+ +

Return values הם בדיוק כמו שהם נשמעים - אלו ערכים שמוחזרים על ידי הפונקציה כאשר היא מסיימת. אנחנו כבר ראינו return values במספר פעמים, למרות שאולי לא חשבנו עליהם כך. נחזור לקוד שאנחנו מכירים:

+ +
var myText = 'I am a string';
+var newString = myText.replace('string', 'sausage');
+console.log(newString);
+// the replace() string function takes a string,
+// replaces one substring with another, and returns
+// a new string with the replacement made
+ +

ראינו את הקוד הזה בעבר במאמר הראשון בנושא פונקציות. אנחנו קוראים/מפעילים את הפונקציה ()replace על המחרוזת myText ומעבירים לה שני פרמטרים (ארגומנטים) - את המחרוזת משנה('string') שהיא צריכה לחפש במחרוזת הראשית (myText) ואת המחרוזת משנה החדשה שתחליף את המחרוזת משנה שנמצאה ('sausage'). כאשר פונקציה זו מסיימת - משלימה את הריצה שלה, היא מחזירה ערך, שהוא בעצם המחרוזת החדשה עם ההחלפה שבוצgה. בקוד למעלה, אנחנו שומרים את אותו ערך מוחזר כערך של המשתנה newString שלנו.

+ +

אם תעיפו מבט על הדף שלנו בנושא פונקציית ()replace, אתם תראו חלק שנקרא Return value. תמיד חשוב לדעת ולהבין אלו ערכים מוחזרים על ידי פונקציה, על מנת שנוכל להשתמש בהם אם נרצה או כשנצטרך.

+ +

חלק מהפונקציות לא מחזירות return value. במקרים כאלו, הערך המוחזר יהיה void או undefined. לדוגמא, בפונקציה ()displayMessage שבנינו במאמר הקודם, אין ערך מוחזר כתוצאה מהשלמת ריצת הפונקציה. היא רק גרמה לקופסא להיות מוצגת איפשהו על המסך.

+ +

באופן כללי, return value משמש כאשר הפונקציה היא שלב ביניים בחישוב כלשהו שאנחנו מבצעים לשם קבלת תוצאה סופית. ערכים מוחזרים אלו צריכים להיות מחושבים על ידי הפונקציה, והיא מחזירה את הערכים הללו בתור התוצאות של הקריאה לה (הריצה שלה), ובתוצאות הללו ניתן להשתמש בשלב הבא של החישוב.

+ +

שימוש ב- return values בפונקציות שלנו

+ +

על מנת להחזיר ערך מפונקציה שאנחנו בונים, אנחנו צריכים להשתמש במילה השמורה return. ראינו מילה זו בפעולה לאחרונה בתרגול שלנו בנושא random-canvas-circles.html. פונקציית ()draw שלנו מציירת 100 עיגולים אקראיים על האלמנט {{htmlelement("canvas")}}:

+ +
function draw() {
+  ctx.clearRect(0,0,WIDTH,HEIGHT);
+  for (var i = 0; i < 100; i++) {
+    ctx.beginPath();
+    ctx.fillStyle = 'rgba(255,0,0,0.5)';
+    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+    ctx.fill();
+  }
+}
+ +

בכל איטרציה של הלולאה, מבוצעות שלוש קריאות לפונקציה ()random, על מנת לייצר מספר אקראי לקוארדינטות x ו-y של העיגול הנוכחי וכן לרדיוס שלו. הפונקציה ()random מקבלת ארגומנט אחד - מספר שלם - ומחזירה מספר שלם בין 0 לאותו מספר שלם שהכנסנו לה כארגומנט. זה נראה כך:

+ +
function randomNumber(number) {
+  return Math.floor(Math.random()*number);
+}
+ +

הקוד למעלה היה יכול להירשם גם כך:

+ +
function randomNumber(number) {
+  var result = Math.floor(Math.random()*number);
+  return result;
+}
+ +

אבל הגרסה הראשונה של הקוד קלה יותר לכתיבה וגם קומפקטית יותר.

+ +

אנחנו מחזירים את התוצאה של החישוב (Math.floor(Math.random()*number בכל פעם שקוראים לפונקציה. הערכים המוחזרים הללו מופיעים ברגע שהפונקציה נקראת (מופעלת), והקוד ממשיך. לדוגמא, אם נריץ את השורות הבאות:

+ +
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ +

ושלושת הקריאות לפונקציה ()random החזירו נניח את הערכים 500, 200, ו- 35, בהתאמה, השורה תרוץ בפועל כאילו היא נכתבה כך :

+ +
ctx.arc(500, 200, 35, 0, 2 * Math.PI);
+ +

הקריאות לפונקציה באותה שורה רצות קודם והערכים המוחזרים של אותן קריאות נכנסים לתוך השורה עוד לפני ששורת הקוד עצמה מורצת במלואה.

+ +

למידה עצמאית: הגדרת - return value של הפונקציה שלנו

+ +

בתרגול זה אנחנו נכתוב פונקציות משלנו הכוללות return values.

+ +
    +
  1. ראשית, שמרו עותק מקומי של הקובץ function-library.html מ-GitHub. זהו דף HTML פשוט המיכל שדה טקסט {{htmlelement("input")}} ופסקה. בנוסף יש גם אלמנט {{htmlelement("script")}} כאשר בתוכו עשינו הפנייה לשני האלמנטים של ה-HTML בתוך שני משתנים. דף זה יאפשר לנו להכניס מספר כלשהו לתוך תיבת טקסט, ולהציג חישובים שונים על המספר הזה בתוך הפסקה שמתחת.
  2. +
  3. כעת, נוסיף פונקציות בתוך <script>. מתחת לשתי השורות הקיימות של JavaScript, הוסיפו את הגדרות הפונקציות הבאות: +
    function squared(num) {
    +  return num * num;
    +}
    +
    +function cubed(num) {
    +  return num * num * num;
    +}
    +
    +function factorial(num) {
    +  var x = num;
    +  while (x > 1) {
    +    num *= x-1;
    +    x--;
    +  }
    +  return num;
    +}
    + הפונקציות ()squared ו- ()cubed דיי ברורות - הן מחזירות את תוצאות ההכפלה או את השילוש של מספר שניתן להן כפרמטר, בעצמו. הפונקציה ()factorial מחזירה את תוצאת העצרת של מספר מסויים. אם אתם לא זוכרים מה זה עצרת, ראו הסבר על הערך בויקיפדיה.
  4. +
  5. כעת, אנחנו הולכים להוסיף דרך להדפיס את כל המידע הזה לגבי המספר שהכנסנו. הכניסו את הקוד הבא, מתחת לפונקציות הנוכחיות: +
    input.onchange = function() {
    +  var num = input.value;
    +  if (isNaN(num)) {
    +    para.textContent = 'You need to enter a number!';
    +  } else {
    +    para.textContent = num + ' squared is ' + squared(num) + '. ' +
    +                       num + ' cubed is ' + cubed(num) + '. ' +
    +                       num + ' factorial is ' + factorial(num) + '.';
    +  }
    +}
    + +

    הוספנו פונקציה בתור ״מטפל אירוע״ אשר תרוץ כל פעם שאירוע מסוג onchange החל להתרחש בשדה הטקסט שלנו (input), כלומר - כל פעם שמוכנס ערך חדש לתוך שדה הטקסט והערך הזה הוגש - כלומר לחצנו על אנטר או עברנו לאלמנט אחר בדף. כאשר פונקציה אנונימית זו רצה, הערך שהוכנס לתוך שדה הטקסט מאוחסן בתוך המשתנה num.

    +
  6. +
  7. +

    לאחר מכן, אנחו מבצעים בדיקה באמצעות משפט תנאי -

    + +
      +
    1. +

      אם הערך שהוכנס אינו מספר, אנחנו נציג הודעת שגיאה לתוך הפסקה. המבחן שלנו מבצע שימוש בפונקציית ()isNaN אשר בודקת האם הערך שהוכנס ב-num הוא לא מספר. אם הערך שהוכנס הוא מספר - היא מחזירה false ואם הוא לא מספר היא מחזירה true.

      +
    2. +
    3. +

      אם המבחן שלנו החזיר false, אז הערך של num הוא מספר ואנחנו יכולים להדפיס לתוך האלמנט p שלנו מהו תוצאת ההכפלה, השילוש והעצרת שלו. המשפט שלנו קורא לפונקציות ()squared(), cubed, ו- ()factorial על מנת לקבל את הערכים הללו.

      +
    4. +
    +
  8. +
  9. שמרו את הקוד ונסו אותו בדפדפן שלכם.
  10. +
+ +
+

לתשומת לב: אם נתקלתם בבעיות בתוכנית או אתם לא מצליחים לגרום לקוד שלכם לעבוד, נסו להשוו אותו לגרסה הסופית שלנו ב-GitHub או ראו אותו כדף אינטרנט.

+
+ +

תרגיל זה ניסה להציג לכם כמה מהנקודות החשובות של ביטוי return. בנוסף:

+ + + +

לסיכום

+ +

במאמר זה ובשני המאמרים הקודמים למדנו על פונקציות, כמה הן שימושיות וכיצד ניתן להשתמש בהם. יש כמובן הרבה מה להוסיף על פונקציות בנוגע לסינטקס שלהם ולפונקציונליות שלהם, שלושת המאמרים האחרונים בנושא פונקציות ניסו להסביר לכם את עקרונות היסוד והבסיס. הרגישו חופשי להרחיב את הידיעות שלכם בנושא בקישורים המופיעים מטה.

+ +

ראו גם

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}

+ +

במודול זה

+ + diff --git "a/files/he/learn/javascript/building_blocks/\327\252\327\240\327\220\327\231\327\235/index.html" "b/files/he/learn/javascript/building_blocks/\327\252\327\240\327\220\327\231\327\235/index.html" new file mode 100644 index 0000000000..4c5c5dcc54 --- /dev/null +++ "b/files/he/learn/javascript/building_blocks/\327\252\327\240\327\220\327\231\327\235/index.html" @@ -0,0 +1,789 @@ +--- +title: משפטי תנאי - קבלת החלטות בקוד שלנו +slug: Learn/JavaScript/Building_blocks/תנאים +tags: + - Conditionals + - Switch + - else if + - if ... else + - ternary + - אופרטור טרנארי + - משפטי אם...אחר + - משפטי תנאי + - תנאים +translation_of: Learn/JavaScript/Building_blocks/conditionals +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/JavaScript/Building_blocks/Looping_code", "Learn/JavaScript/Building_blocks")}}
+ +

כמו בכל שפת תכנות, הקוד שלנו צריך ״לקבל החלטות״ בעצמו ולבצע פעולות מסויימות בהתאם למקרים שונים. לדוגמא - במשחק, אם מספר הנסיונות המותרים של השחקן עבר את מספר הנסיונות המקסימלי שהוגדר, המשמעות היא שהמשחק הסתיים. דוגמא נוספת היא יישום אינטרנט של מזג אוויר אשר יציג למשתמש עיצוב מסויים של מזג האוויר בהתאם לשעה הנוכחית ביום, כך שבשעות היום יוצג עיצוב בהיר ובשעות הערב יוצג עיצוב כהה. במאמר זה אנחנו נחקור את המבנה של משפטי תנאי ב-JavaScript וכיצד הם עובדים.

+ + + + + + + + + + + + +
ידע מוקדם:הכרות בסיסית עם המחשב ועם הבסיס של HTML ו- CSS, וכן סיום במלאו של מודול צעדים ראשונים ב-JavaScript.
מטרה:להבין כיצד להשתמש במשפטי תנאי ב-JavaScript.
+ +

תנאים והתניות בחיים האמיתיים

+ +

בני האדם מקבלים החלטות שמשפיעות על החיים שלהם באופן קבוע, מהחלטה קטנה האם לאכול שתי עוגיות או אחת עד להחלטות מורכבות האם לעזוב את העבודה או האם ללכת ללמוד אסטרונומיה במקום סוציולוגיה.

+ +

משפטי תנאי ב-JavaScript אפשרים לנו לייצג בקוד שלנו את התהליך של קבלת ההחלטה, החל מהחלטות שנהיה חייבים לקבל ועד החלטות שנקבל רק אם ניתקל במקרה מסויים.

+ +

+ +

if ... else - משפטי אם ... אחרת

+ +

נסתכל על הסוג הנפוץ ביותר של משפטי תנאי ב-JavaScript - משפט התנאי if ... else.

+ +

הסינטקס הבסיסי של משפטי if ... else

+ +

אם ננסה להמחיש את הסינטקס הבסיסי של if...else בצורה מופשטת של {{glossary("pseudocode")}}, הוא ייראה כך:

+ +
if (condition - תנאי) {
+  code to run if condition is true - הקוד שירוץ אם התנאי נכון
+} else {
+  run some other code instead  - הקוד שירוץ אם התנאי לא נכון
+}
+ +

מה שיש לנו כאן זה:

+ +
    +
  1. המילה השמורה if ולאחרי סוגריים רגילות ().
  2. +
  3. לאחר מכן - התנאי שנבדוק האם הוא מתקיים נמצא בתוך ה- () (כמו: ״האם הערך הזה גדול מערך אחר״ או ״הערך הזה קיים״). תנאי זה יעשה שימוש באופרטורים שדבירנו עליהם במודול הקודם - comparison operators אופרטורים להשוואה ויחזיר לנו ערך של אמת - true או ערך של שקר - false.
  4. +
  5. לאחר מכן  - סוגריים מסולסלות - {} - שבתוכן נכניס כל קוד שנרצה, אשר יתבצע אך ורק אם התנאי התקיים, כלומר החזיר תוצאת אמת - true.
  6. +
  7. לאחר מכן - מילה שמורה נוספת שהיא else. אשר באה לבטא מה יקרה, אם בכלל, כאשר התנאי לא יחזיר תוצאת true
  8. +
  9. ולבסוף - סוגריים מסולסלות נוספות{}- שבתוכן נכניס כל קוד שנרצה, אשר יתבצע אך ורק אם התנאי עצמו לא התקיים, כלומר החזיר תוצאת שקר - false.
  10. +
+ +

קוד זה דיי קריא ומובן על ידי בני אדם - הוא אומר בעצם ש-"אם ה- condition (התנאי) מתקיים - כלומר מחזיר ערך של אמת - true, תריץ את קוד A, אחרת - תריץ את קוד B"

+ +

שימו לב שאנחנו לא חייבים את לכלול את ה- else ואת הסוגריים המסולסלות השניות. כך לדוגמא, התנאי הבא הוא חוקי לחלוטין:

+ +
if (condition) {
+  code to run if condition is true
+}
+
+run some other code
+ +

יחד עם זאת, אנחנו צריכים לזכור שבמקרה כזה, הקוד השני שאינו חלק ממשפט התנאי, לא נשלט על ידיו ולא כפוף למשפט התנאי - מה שאומר שהוא ירוץ תמיד לא משנה אם התנאי החזיר ערך true או false. זה לאו דווקא משהו רע, אבל חשוב לדעת זאת.

+ +

לפעמים אנחנו גם נראה משפטי- if...else ללא סוגריים מסולסלות כלל, בקיצור אשר נראה כך:

+ +
if (condition) code to run if condition is true
+else run some other code instead
+ +

זהו קוד תקין לחלוטין, אך אינו מומלץ לשימוש -  הרבה יותר קל לקרוא קוד ולהבין מה קורה, אם אנחנו משתמשים בסוגריים מסולסולת לתחום את הקוד, וכן משתמשים במס׳ שורות ורווחים על מנת להפוך את הקוד לקריא ומובן יותר.

+ +

דוגמא לתרגול

+ +

על מנת להבין את הסינטקס טוב יותר, בוא ננסה דוגמא קטנה לתרגול. דמיינו ילד אשר התבקש לעזור לאימו בביצוע הקניות. אם הוא ישלים את המשימה, הוא יקבל דמי כיס גבוהים יותר מהרגיל:

+ +
var shoppingDone = false;
+
+if (shoppingDone === true) {
+  var childsAllowance = 10;
+} else {
+  var childsAllowance = 5;
+}
+ +

עם זאת, קוד זה תמיד יגרום לכך שהילד יקבל דמי כיס מופחתים שכן בתחילת הקוד המשתנה shoppingDone קיבל את הערך של false. אנחנו צריכים למצוא דרך להפוך את הערך של המשתנה shoppingDone - true אם הילד השלים את הקניות.

+ +
+

תשומת לב: ניתן לראות את הפתרון לתרגול זה ב- GitHub (וכן ניתן לראות אותו כדף אינטרנט.)

+
+ +

else if - משפטי התניה משורשרים

+ +

הדוגמא הקודמת נתנה לנו שתי אפשרויות אך מה אם נצטרך יותר משתי אפשרויות?

+ +

ישנה דרך לשרשר אפשרויות נוספות לתוך משפט if...else שלנו באמצעות שימוש ב- else if. כל בחירה נוספות דורשת בלוק נוסף של קוד שייכנס בין{ ... }() if לבין { ... }else — ראו את הדוגמאות הבאות, שהן בעצם חלק מיישום פשוט לתחזית מזג אוויר:

+ +
<label for="weather">Select the weather type today: </label>
+<select id="weather">
+  <option value="">--Make a choice--</option>
+  <option value="sunny">Sunny</option>
+  <option value="rainy">Rainy</option>
+  <option value="snowing">Snowing</option>
+  <option value="overcast">Overcast</option>
+</select>
+
+<p></p>
+ +
var select = document.querySelector('select');
+var para = document.querySelector('p');
+
+select.addEventListener('change', setWeather);
+
+function setWeather() {
+  var choice = select.value;
+
+  if (choice === 'sunny') {
+    para.textContent = 'It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.';
+  } else if (choice === 'rainy') {
+    para.textContent = 'Rain is falling outside; take a rain coat and a brolly, and don\'t stay out for too long.';
+  } else if (choice === 'snowing') {
+    para.textContent = 'The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.';
+  } else if (choice === 'overcast') {
+    para.textContent = 'It isn\'t raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.';
+  } else {
+    para.textContent = '';
+  }
+}
+
+
+ +

{{ EmbedLiveSample('else_if', '100%', 100, "", "", "hide-codepen-jsfiddle") }}

+ +
    +
  1. כאן יש לנו אלמנט {{htmlelement("select")}} המאפשר לנו לבחור אפשרויות שונות, וכן פסקה פשוטה.
  2. +
  3. ב-JavaScript, אנחו מאחסנים הפניות לאלמנט {{htmlelement("select")}} ולאלמנט- {{htmlelement("p")}} על ידי שמירתם במשתנים ומוסיפים ל-{{htmlelement("select")}} ״מאזין אירוע״ - event listener לאלמנט {{htmlelement("select")}} כך שכאשר הערך שלו ישתנה, הפונקציה ()setWeatherתופעל.
  4. +
  5. כאשר הפונקציה הזו רצה, אנחנו מגדירים תחילה משתנה בשם choice ומשימים לו את הערך הרלוונטי שהמשתמש בחר באלמנט <select>. לאחר מכן, אנחנו משתמשים במשפטי תנאי על מנת לתת כיתוב שונה בטקסט של הפסקאות, בהתאם לערך שיקבל <select> בכל פעם. שימו לב שכל התנאים נבדקים בבלוקי קוד של משפט תנאי {...}()else if, למעט האפשרות הראשונה, אשר נבדק בבלוק של המשפט התנאי {...}()if.
  6. +
  7. האפשרות האחרונה הנמצאת בבלוק של {...}else היא בעצם ברירת המחדל, או האופציה האחרונה. הקוד בבלוק שלה ירוץ רק אם אף אחד מהתנאים לא החזירו true. במקרה הזה, זה ישמש לרוקן את הטקסט מהפסקה ומידה ושום אופציה לא נבחרה.
  8. +
+ +
+

לתשומת לב: אתם גם יכולים למצוא את הדוגמא הזו ב- GitHub או להריץ אותה.

+
+ +

תשומת לב לאופרטורים להשוואה

+ +

אופרטורים להשוואה משמשים לבדוק את התנאים בתוך משפטי התנאי שלנו. עברנו עליהם במאמר מתמטיקה בסיסית ב-JavaScript — מספרים ואופרטורים שלנו. האפשרויות שלנו הן:

+ + + +
+

לתשומת לבכם: חיזרו על החומר אם אינכם זוכרים אותם. 

+
+ +

כאשנו מדברים על תוצאות ההשוואה, אנחנו רוצים לשים תשומת לב לערכים הבוליאנים שנקבל - הלא הם (true/false) ומהו המקור שלהם וכן לשים לב להתנהגות מסויימת שאנחנו ניתקל בה שוב ושוב.

+ +

כל ערך שהוא לא false, undefined, null, 0, NaN, או מחרוזת ריקה - (''), יחזיר לנו אמת - true כאשר הוא נבדק כתנאי במשפט תנאי. ולכן, אנחנו יכולים להשתמש רק בשם של המשתנה בלבד על מנת לבדוק האם הוא אמת או אפילו לבדוק האם הוא קיים - כלומר הוא לא undefined לדוגמא:

+ +
var cheese = 'Cheddar';
+
+if (cheese) {
+  console.log('Yay! Cheese available for making cheese on toast.');
+} else {
+  console.log('No cheese on toast for you today.');
+}
+ +

ואם נחזור לדוגמא הקודמת שלנו עם הילד והמטלה, נוכל לרשום זאת כך:

+ +
var shoppingDone = false;
+
+if (shoppingDone) { // don't need to explicitly specify '=== true'
+  var childsAllowance = 10;
+} else {
+  var childsAllowance = 5;
+}
+ +

שרשור של if ... else

+ +

זה בסדר גמור לשים משפט if...else אחד בתוך השני - כלומר לשרשר אותם. לדוגמא, אנחנו יכולים לעדכן את היישום מזג אוויר שלנו להראות רשימה נוספת של אפשרויות בהתבסס על הטמפרטורה:

+ +
if (choice === 'sunny') {
+  if (temperature < 86) {
+    para.textContent = 'It is ' + temperature + ' degrees outside — nice and sunny. Let\'s go out to the beach, or the park, and get an ice cream.';
+  } else if (temperature >= 86) {
+    para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
+  }
+}
+ +

למרות שכל הקוד עובד יחד, כל משפט if...else עובד לחלוטין באופן עצמאי מהאחר.

+ +

AND, OR או NOT :אופרטוריים לוגיים - Logical operators

+ +

אם נרצה לבדוק מספר תנאים מבלי לשרשר משפטי if...else שונים, logical operators אופרטוריים לוגיים - יכולים לעזור לנו. כשנעשה בהם שימוש בתנאים, השניים ראשונים עושים את הדברים הבאים:

+ + + +

 ניתן היה לרשום את הקוד הקודם באמצעות שימוש באופרטור הלוגי AND בצורה הבאה:

+ +
if (choice === 'sunny' && temperature < 86) {
+  para.textContent = 'It is ' + temperature + ' degrees outside — nice and sunny. Let\'s go out to the beach, or the park, and get an ice cream.';
+} else if (choice === 'sunny' && temperature >= 86) {
+  para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
+}
+ +

בדוגמא זו, הבלוק קוד הראשון ירוץ רק אם 'choice === 'sunny וגם temperature < 86 יחזיר true.

+ +

דוגמא לשימוש באופרטור הלוגי OR:

+ +
if (iceCreamVanOutside || houseStatus === 'on fire') {
+  console.log('You should leave the house quickly.');
+} else {
+  console.log('Probably should just stay in then.');
+}
+ +

הסוג האחרון של אופרטור לוגי - NOT, מבוטא על ידי האופרטור ! וניתן לשימוש על מנת לשלול ביטוי מסויים. נראה אותו בדוגמא הבאה:

+ +
if (!(iceCreamVanOutside || houseStatus === 'on fire')) {
+  console.log('Probably should just stay in then.');
+} else {
+  console.log('You should leave the house quickly.');
+}
+ +

בקוד זה אם ביטוי של OR מסויים מחזיר true, אז האופרטור NOT יהפוך אותו לשלילי על מנת שכל הביטוי יחזור false.

+ +

אנחנו יכולים לאחד כמה משפטי התנייה ואופרטורים לוגיים כמה שנרצה, בכל מבנה שנרצה. הדוגמאות הבאות מריצות קוד בפנים רק אם שני משפטי ה-OR מחזירים ערך של אמת. 

+ +
if ((x === 5 || y > 3 || z <= 10) && (loggedIn || userName === 'Steve')) {
+  // run the code
+}
+ +

טעות נפוצה שעושים כאשר משתמשים באופרטור הלוגי OR במשפט תנאי היא לציין את המשתנה שאנחנו נרצה לבדוק את הערך שלו ואז לתת מס׳ ערכים לבדיקה מופרדים על ידי האופרטור|| (OR) , זה עלול להחזיר לנו ערך של אמת. לדוגמא:

+ +
if (x === 5 || 7 || 10 || 20) {
+  // run my code
+}
+ +

במקרה זה, התנאי בתוך הסוגריים () יחזיר לנו תמיד true, כי המספר 7 או כל מספר שאינו 0, תמיד יחזיר ערך של true כי כל ערך שהוא לא false, undefined, null, 0, NaN, או מחרוזת ריקה - (''), יחזיר לנו אמת - true כאשר הוא נבדק כתנאי במשפט תנאי. 

+ +

על מנת שקוד זה יעבוד לוגית כפי שרצינו, אנחנו צריכים להשתמש אופרטור OR על כל אחד מהם:

+ +
if (x === 5 || x === 7 || x === 10 ||x === 20) {
+  // run my code
+}
+ +

משפטי switch

+ +

משפטי if...else עוזרים לנו לממש קוד מותנה שירוץ בהתאם לתנאים שנגדיר לו, אבל לא בלי החסרונות שלהם. הם לרוב יהיו טובים כאשר יש לנו שתי אפשרויות וכל מהן דורשת כמויות הגיונית של קוד, או כאשר התנאים שלנו מורכבים יחסית. למקרים שבה אנחנו נרצה הרבה מקרים לבדיקה, הקוד עלול להיות קצת מעצבן וארוך. 

+ +

בשביל זה נועדו משפטי switch. משפטים אלו מקבלים ביטוי/ערך בתוך פרמטר ואז בודקים התקיימותו בין מספר אפשרויות שמוגדרות להם. אם הם מוצאים אחת מהאפשרויות שמתאימה לביטוי/ערך שהם קיבלו כפרמטר, הן יריצו את הקוד המתאים. כך זה קורה מבחינה רעיונית:

+ +
switch (expression) {
+  case choice1:
+    run this code
+    break;
+
+  case choice2:
+    run this code instead
+    break;
+
+  // include as many cases as you like
+
+  default:
+    actually, just run this code
+}
+ +

הסבר:

+ +
    +
  1. המילה השמורה switch, ולאחריה סט של סוגריים רגילות ().
  2. +
  3. ביטוי או ערך בתוך הסוגריים.
  4. +
  5. המילה השמורה case, ולאחריה מקרה אפשרי שהביטוי או הערך יכול להיות, לאחריו :.
  6. +
  7. קוד שירוץ אם המקרה מתאים לביטוי/ערך.
  8. +
  9. המילי השמורה break, ולאחריה ; אם האפשרות הקודמת תואמת לערך/ביטוי, הדפדפן יפסיק להריץ את הקוד במשפט ה-switch וימשיך לקוד שמתחת למשפט ה-switch.
  10. +
  11. ניתן להוסיף case כמה שרק נרצה. 
  12. +
  13. לבסוף, המילה השמורה default, ולאחריה : וקוד שירוץ. default תרוץ אם הערך/ביטוי שהוכנס לא תואם לאף אחד מאפשרויות ה-case שרשמנו. במקרה של default - אין צורך להתשמש במילה השמורה break, מכיוון שאין מה להפסיק/לעצור לאחר התקיימותו של מקרה זה. 
  14. +
+ +
+

לתשומת לב:  החלק של default הוא אופציונלי - אנחנו יכולים להשמיט אותו אם אין סיכוי כשהביטוי יחזיר לנו ערך לא ידוע או לא תואם לאף אחד מהמקרים. אם יש סיכוי  כזה - אנחנו צריכים לכלול default במשפט ה-switch על מנת להתמודד עם אותם מקרים לא ידועים. 

+
+ +

דוגמא למשפט switch

+ +

נסתכל על דוגמא אמיתית - נכתוב את יישום מזג האוויר שלנו מחדש באמצעות שימוש במשפט switch:

+ +
<label for="weather">Select the weather type today: </label>
+<select id="weather">
+  <option value="">--Make a choice--</option>
+  <option value="sunny">Sunny</option>
+  <option value="rainy">Rainy</option>
+  <option value="snowing">Snowing</option>
+  <option value="overcast">Overcast</option>
+</select>
+
+<p></p>
+ +
var select = document.querySelector('select');
+var para = document.querySelector('p');
+
+select.addEventListener('change', setWeather);
+
+
+function setWeather() {
+  var choice = select.value;
+
+  switch (choice) {
+    case 'sunny':
+      para.textContent = 'It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.';
+      break;
+    case 'rainy':
+      para.textContent = 'Rain is falling outside; take a rain coat and a brolly, and don\'t stay out for too long.';
+      break;
+    case 'snowing':
+      para.textContent = 'The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.';
+      break;
+    case 'overcast':
+      para.textContent = 'It isn\'t raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.';
+      break;
+    default:
+      para.textContent = '';
+  }
+}
+ +

{{ EmbedLiveSample('A_switch_example', '100%', 100, "", "", "hide-codepen-jsfiddle") }}

+ +
+

לתשומת לבכם: אפשר למצוא את הדוגמא ב-​​​​​GitHub או להריץ אותה פה.

+
+ +

 Ternary operator - אופרטור טרנארי

+ +

סינטקס נוסף שנרצה להציג בפניכם לפני שנמשיך הוא אופרטור טרנארי

+ +

אופרטור טרנארי הוא סינטקס קצר אשר בודק התקיימותו של תנאי מסויים ומבצע פעולה מסויימת אם התנאי מתקיים - true, ופעולה אחרת אם התנאי לא מתקיים - false. אופרטור זה שימושי בסיטואציות מסוימות ויכול לקחת הרבה פחות קוד מאשר if...else, במידה ויש שתי אפשרויות, אשר נבחרות לפי מבחן/תנאי true/false.

+ +

על מנת להפשיט את הנושא, הקוד בצורה רעיונית נראה כך:

+ +
( condition ) ? run this code : run this code instead
+ +

נסתכל על דוגמא פשוטה:

+ +
var greeting = ( isBirthday ) ? 'Happy birthday Mrs. Smith — we hope you have a great day!' : 'Good morning Mrs. Smith.';
+ +

יש לנו פה משתנה בשם isBirthday - אם הוא true, אנחנו נתן הודעת יומולדת שמח, ואם הוא false, אנחנו נתן ברכה רגילה.

+ +

דוגמא לאופרטור טרנארי

+ +

שימו לב כי אנחנו לא חייבים לקבוע ערכים בפרמטרים של האופרטור הטרנארי - אנחנו יכולים גם לתת לו כפרמטרים שורות קוד או פונקציות - כל דבר שנרצה. הדוגמא הבאה מראה לנו אפשרות לבחירת עיצוב לאתר על בסיס אופרטור טרנארי.

+ +
<label for="theme">Select theme: </label>
+<select id="theme">
+  <option value="white">White</option>
+  <option value="black">Black</option>
+</select>
+
+<h1>This is my website</h1>
+ +
var select = document.querySelector('select');
+var html = document.querySelector('html');
+document.body.style.padding = '10px';
+
+function update(bgColor, textColor) {
+  html.style.backgroundColor = bgColor;
+  html.style.color = textColor;
+}
+
+select.onchange = function() {
+  ( select.value === 'black' ) ? update('black','white') : update('white','black');
+}
+
+ +

{{ EmbedLiveSample('Ternary_operator_example', '100%', 300, "", "", "hide-codepen-jsfiddle") }}

+ +

כאן יש לנו אלמנט {{htmlelement('select')}} המאפשר לנו לבחור את הסגנון (שחור או לבן) ובנוסף כותרת פשוטה של {{htmlelement('h1')}} על מנת להציג את הכותרת של האתר. לאחר מכן יש לנו פונקציה שנקראת ()update שמקבלת שני צבעים כארגומנטים. הרקע של האתר נקבע על ידי הצבע הראשון שאנחנו בוחרים, והצבע של הטקסט ניתן כפרמטר השני.

+ +

לבסוף ישלנו ״מאזין אירוע״ - event listener בשם onchange. מאזין אירוע זה מכיל ״מטפל אירוע״ - event handler מסוג מסוג אופרטור טרנארי. הוא מתחיל עם מבחן התנאי - select.value === 'black'. אם התנאי מחזיר true, אנחנו נריץ את פונקציית true עם האפרמטרי ׳black׳ ו-׳white׳ - כלומר אנחנו נקבל רקע שחור עם כיתוב טקסט לבן. אם זה תוצאת התקיימות התנאי היא false - פונקציית ()update תרוץ עם פרמטרים לבן ושחור, כלומר הצבעים יהיו הפוכים.

+ +
+

לתשומת לבכם:  אירועים אלו בעצם פעולות או התרחשויות אשר קורים במערכת, אשר אנחנו יכולים ״להאזין״ להם באמצעות ״מאזין אירוע״ - event listener כך שנוכל להגיב בדרך מסויימת, ברגע שהם יתרחשו באמצעות ״מטפל אירוע״ - event handler. אנו נגע בהם בהמשך.

+
+ +
+

לתשומת לב: ראו גם דוגמא זו ב- GitHub או בדוגמא כדף אינטרנט

+
+ +

למידה עצמאית: לוח שנה פשוט

+ +

בדוגמא הבאה, אנחנו הולכים לכתוב דוגמא ללוח שנה. בקוד שלנו יש:

+ + + +

אנחנו צריכים משפט תנאי בתוך ה-event handler שלנו - כלומר בתוך פונקציית onchange שלנו, מתחת להערה ADD CONDITIONAL HERE //. משפט תנאי זה אמור:

+ +
    +
  1. להסתכל על החודש שנבחר ואוכסן במשתנה choice. זה יהיה הערך של האלמנט <select> לאחר שהערך שלו השתנה. בדוגמא שלנו זה יהיה January.
  2. +
  3. להגדיר משתנה בשם days שיהיה שווה למספר הימים בחודש הנבחר. על מנת לעשות זאת אנחנו נצטרך להסתכל על מספר הימים בכל חודש בשנה. אנחנו יכולים להתעלם משנים מעוברות לצרכי דוגמא זו.
  4. +
+ +

רמזים:

+ + + +

אם אתם עושים טעות - תמיד ניתן לאתחל את הקוד למצבו הראשוני באמצעות כפתור ה-״Reset״. אם ממש נתקעתם - הסתכלו על הפתרון.

+ + + +

{{ EmbedLiveSample('Playable_code', '100%', 1110, "", "", "hide-codepen-jsfiddle") }}

+ +

למידה עצמאית: יותר אפשרויות צבעים!

+ +

בתרגול זה, אנחנו הולכים לקחת את האופרטור הטרנארי שראינו קודם, ולהפוך אותו למשפט switch שיאפשר לנו לבחור מבין אפשרות אחת מבין אפשרויות רבות יותר עבור האתר הפשוט שלנו. הסתכלו על {{htmlelement("select")}} - הפעם אתם תראו שיש לו חמש אופציות ולא שתיים. אתם נדרשים להוסיף משפט switch ישר מתחת להערת ADD SWITCH STATEMENT //אשר יבצע את הפעולות הבאות:

+ + + +

אם אתם עושים טעות - תמיד ניתן לאתחל את הקוד למצבו הראשוני באמצעות כפתור ה-״Reset״. אם ממש נתקעתם - הסתכלו על הפתרון.

+ + + +

{{ EmbedLiveSample('Playable_code_2', '100%', 950, "", "", "hide-codepen-jsfiddle") }}

+ +

לסיכום

+ +

אלו הדברם העיקריים והמרכזיים שאנחנו צריכים לדעת על משפטי תנאי ומבניהם ב-JavaScript. אם לא הבנתם את הרעיונות שעברנו עליהם בתרגולים למעלה, חזרו על השיעור שוב. ואם משהו לא ברור, צרו עמנו קשר לשם קבלת עזרה.

+ +

ראו גם

+ + + +

{{NextMenu("Learn/JavaScript/Building_blocks/Looping_code", "Learn/JavaScript/Building_blocks")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/a_first_splash/index.html b/files/he/learn/javascript/first_steps/a_first_splash/index.html new file mode 100644 index 0000000000..76a5d40f9b --- /dev/null +++ b/files/he/learn/javascript/first_steps/a_first_splash/index.html @@ -0,0 +1,634 @@ +--- +title: מבט ראשון ל-JavaScript +slug: Learn/JavaScript/First_steps/A_first_splash +translation_of: Learn/JavaScript/First_steps/A_first_splash +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/What_is_JavaScript", "Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps")}}
+ +

עכשיו כשלמדתם קצת על התיאוריה של JavaScript, אנחנו יכולים להיכנס לעומק יותר ולצדדים הפרקטיים. אנחנו נבנה יחד משחק ״נחש את המספר״, צעד אחד צעד. 

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה:להתנסות בכתיבת קוד JavaScript ולהבין כיצד לנסות לעשות זאת.
+ +

אנו לא מצפים שתבינו את כל הקוד לפרטי פרטים ישר על ההתחלה. אנחנו רק רוצים להציג לכם בצורה עקרונית את הרעיון כיצד השפה עובדת. בהמשך המאמרים ניכנס יותר לעומק. 

+ +
+

הרבה מהמאפיינים והאפשרויות ב-JavaScript זהים לשפות תכנות אחרות - כמו לדוגמא לולאות, פונקציות וכד׳. הסינטקס אולי שונה, אבל הרעיונות בגדול דומים.

+
+ +

לחשוב כמו מתכנת או מתכנתת

+ +

אחד מהדברים הקשים ללמידה בפיתוח תוכנה אינו למידת השפה או הסינטקס, אלא איך אנחנו מחילים את השפה לפתרון בעיות אמיתיות. אנחנו צריכים להתחיל לחשוב כמו מתכנתים - זה בעצם כולל בתוכו התבוננות על התיאור של מה התוכנית שלנו צריכה לעשות, להבין מה מאפייני הקוד שאנחנו צריכים כדי להשיג את אותם דברים ואיך אנחנו גורמים להם לעבוד יחד. 

+ +

זה דורש מאיתנו שילוב של עבודה קשה, ניסיון עם סינטקס השפה, תרגול, וכן מעט יצירותיות. ככל שנכתוב קוד יותר, ככה נשתפר בכך. אנחנו לא יכולים להבטיח שנגרום לך לפתח ״מוח של מתכנת״ בחמש דקות, אבל אנחנו נתן לך את מירב ההזדמניות לתרגל חשיבה של מתכנת בקורס זה.

+ +

דוגמא — משחק ״נחש את המספר״

+ +

במאמר זה אנחנו נראה לך כיצד ליצור משחק פשוט:

+ + + +

{{ EmbedLiveSample('Top_hidden_code', '100%', 320, "", "", "hide-codepen-jsfiddle") }}

+ +

נסו לשחק עם הקוד קצת, הכירו את המשחק לפני שאתם ממשיכים הלאה.

+ +

תדמיינו שהבוס שלכם נתן את התיאור הקצר ליצירת המשחק הזה:

+ +
+

הבוס: ״אני רוצה שתצרו משחק פשוט של ״נחש את המספר״. 

+ +

הוא אמור ליצור מספר בין 1 ל-100, כאשר המשתתף צריך לנחש את המספר בכפולות של 10. 

+ +

אחרי כל פעם שהמשתתף ניחש את המספר, המשחק יגיד לו אם הוא טעה או צדק, אם הוא טעה, הוא יגיד לו האם הניחוש היה גבוה מדי או נמוך מדיץ בנוסף, המשחק צריך להגיד למשתתף כמה ניחושים הוא ביצע כבר. המשחק יסתיים ברגע שהמשתתף ינחש נכונה את המספר האקראי או ברגע שנגמרו לו הנסיונות. כשהמשחק הסתיים, המשתמש צריך לקבל אפשרות להתחיל את המשחק מחדש. 

+
+ +

עכשיו, ננסה להפוך את למשימות פשוטות, בצורה מתוכננת כמה שאפשר: 

+ +
    +
  1. צור מספר בין 1 ל-100.
  2. +
  3. שמור את הניסיון של המשתתף. תתחיל במספר 1 שכן זהו הניסיון הראשון.
  4. +
  5. ספק למשתמש דרך לנחש את המספר האקראי.
  6. +
  7. ברגע שהמתשמש ניחש, שמור את הניחוש שלו כדי שתוכל להציג לו את הניחושים הקודמים.
  8. +
  9. בדוק האם המשתמש ניחש את המספר האקראי.
  10. +
  11. אם ניחש נכונה: +
      +
    1. הצג הודעת זכייה. 
    2. +
    3. תמנע מהמשתמש להזין ניחושים נוספים.
    4. +
    5. תציג למשתמש אפשרות לאתחל את המשחק.
    6. +
    +
  12. +
  13. אם המשתמש לא ניחש נכונה ונותרו לו הנסיונות : +
      +
    1. תציג למשתמש שהוא טעה.
    2. +
    3. תאפשר לו לנחש שוב.
    4. +
    5. תעלה את מספר הנסיונות שלו ב-1.
    6. +
    +
  14. +
  15. אם המשתמש לא ניחש נכונה ונגמרו לו הנסיונות: +
      +
    1. תציג למשתמש שהמשחק נגמר.
    2. +
    3. תמנע מהמשתמש להזין ניחושים נוספים.
    4. +
    5. תציג למשתמש אפשרות לאתחל את המשחק.
    6. +
    +
  16. +
  17. ברגע שהמשחק אותחל מחדש, תוודא שהלוגיקה והנראות של המשחק מאותחלים לחלוטין וחזור לצעד מספר 1.
  18. +
+ +

כעת נראה כיצד ניתן להפוך את רשימת המשימות הזו לקוד של JavaScript.

+ +

הגדרות ראשונות

+ +

על מנת להתחיל את המדריך, צרו עותק מקומי של הקובץ number-guessing-game-start.html או ראו כדף אינטרנט.

+ +

פתחו אותו גם בעורך הקוד וגם בדפדפן. בנקודה זו אתם תראו כותרת, פסקת הוראות וטופס להכניס ניחוש, אבל הטופס לא עובד כראוי. 

+ +

אנחנו נכניס את הקוד שלנו בתוך האלמנט {{htmlelement("script")}} בתחתית קובץ ה-HTML:

+ +
<script>
+
+  // Your JavaScript goes here
+
+</script>
+
+ +

הוספת משתנים לשם אחסון המידע

+ +

ראשית, הכניסו את השורות הבאות תוך האלמנט {{htmlelement("script")}} :

+ +
let randomNumber = Math.floor(Math.random() * 100) + 1;
+
+const guesses = document.querySelector('.guesses');
+const lastResult = document.querySelector('.lastResult');
+const lowOrHi = document.querySelector('.lowOrHi');
+
+const guessSubmit = document.querySelector('.guessSubmit');
+const guessField = document.querySelector('.guessField');
+
+let guessCount = 1;
+let resetButton;
+ +

חלק זה של הקוד מגדיר משתנים וקבועים שאנחנו צריכים על מנת לאחסן את המידע שהתוכנית שלנו תצטרך. משתנים הם בעצם כמו קופסאות אחסון עבור ערכים מסוגים שונים אשר יכולים להיות מספרים, טקסט, אובייקטים, מערכים וכד׳. 

+ +

אנו יוצרים משתנה באמצעות שימוש במילה שמורה של JavaScript בשם letאו var ולאחר מכן בשם שאנחנו רוצים להעניק למשתנה שלנו. (אנחנו נלמד על ההבדלים ביניהם במאמר בהמשך).

+ +

קבועים לעומת זאת, אלו מעין קופסאות אחסון שנועדו לאחסן ערכים שאנחנו לא נרצה לשנות, ואותם אנחנו יוצרים באמצעות מילה שמורה בשם  const. במקרה שלנו אנחנו משתמשים בקבועים על מנת לאחסן את ההפניות לאלמנטים בדף ה-html, כלומר לממשק המשתמש. אמנם יכול להיות שהתוכן שלהם ישתנה, אבל ההפניה לאותו אלמנט תישאר קבועה לאורך התוכנית.

+ +

על מנת לשים ערך מסויים במשתנה או בקבוע מסויים, אנחנו משתמשים בסימן ״שווה״ (=), ולאחר מכן הערך שאנחנו רוצים להשים באותו משתנה או קבוע.

+ +

בדוגמא שלנו :

+ + + +
+

לתשומת לב: אתם תלמדו עוד על משתנים וקבועים בהמשך הקורס, החל מהמאמר הבא.

+
+ +

פונקציות

+ +

הוסיפו את הקוד הבא, מתחת לקוד הקיים שלכם:

+ +
function checkGuess() {
+  alert('I am a placeholder');
+}
+ +

פונקציות הם בעצם חלקי קוד הניתנים לשימוש חוזר, אותם ניתן לרשום פעם אחת, ולהריץ שוב ושוב. זה מאוד שימושי. יש כמה דרכים להגדיר פונקציה אבל כעת אנחנו נתמקד בצורה פשוטה אחת. 

+ +

כאן אנחנו הגדרנו את הפונקציה באמצעות שימוש במילה שמורה function ולאחר מכן שם הפונקציה וסוגריים (). לאחר מכן, הוספנו סוגריים מסולסלים {}. כל מה שנמצא בתוך הסוגריים המסולסלים זה הקוד שנרצה שירוץ בכל פעם שנקרא לפונקציה. 

+ +

כשנרצה להריץ את הקוד, אנחנו פשוט נקליד את שם הפונקציה ובצמוד לה נשים סוגריים (). שמרו את הקובץ ורענן את הדף בדפדפן. לאחר מכן, הזן בקונסולה, את השורה הבאה:  

+ +
checkGuess();
+ +

לאחר שנלחץ על  Return/Enter, אנחנו אמורים לראות שקפצה הודעה שרשמה "I am a placeholder".

+ +

הגדרנו פונקציה שמקפיצה הודעה למשתמש בכל פעם שנקרא לה.

+ +
+

לתשומת לב: אתם תלמדו עוד על פונקציות בהמשך הקורס.

+
+ +

אופרטורים (Operators)

+ +

אופרטורים של JavaScript מאפשרים לנו לבצע מבחנים מסויימים, חישובים מתמטיים, חיבור מחרוזות ודברים נוספים רבים. 

+ +

אם לא עשיתם זאת עד עכשיו, שמרו את הקוד, ורעננו את הדף. לאחר מכן, פתחו את הקונסולה. נסו להזין את הדוגמאות המפורטות למטה בעמודת ״דוגמא״, בדיוק בצורה שבה הן מופיעות ולאחר מכן לחצו Return/Enter  וראו את התוצאות. 

+ +

נתחיל עם אופרטורים מתמטיים:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
אופרטורשםדוגמא
+חיבור6 + 9
-חיסור20 - 15
*כפל3 * 7
/חילוק10 / 5
+ +

ניתן להשתמש בסימן החיבור + על מנת לחבר מחרוזות ביחד (בתכנות, בשפה האנגלית זה נקרא: concatenation).

+ +

נסו להזין את השורות הבאות בקונסולה, אחת בכל פעם:

+ +
let name = 'Bingo';
+name;
+let hello = ' says hello!';
+hello;
+let greeting = name + hello;
+greeting;
+ +

יש גם כמה קיצורים לאופרטורים מתמטיים, לדוגמא, אם אנחנו פשוט רוצים להוסיף מחרוזת טקסט חדשה למחרוזת טקסט קיימת ולהחזיר את המחרוזת המחוברת של שתיהן, נוכל לבצע את הדבר הבא: 

+ +
name += ' says hello!';
+ +

זה זהה בתוצאה לדבר הבא:

+ +
name = name + ' says hello!';
+ +

כשאנחנו מריצים מבחני אמת/שקר (לדוגמא, בתוך תנאים), אנחנו משתמשים באופרטורים השוואתיים

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
אופרטורשםדוגמא
===האם זהה לחלוטין? +
+5 === 2 + 4 // false
+'Chris' === 'Bob' // false
+5 === 2 + 3 // true
+2 === '2' // false; number versus string
+
+
!==האם לא זהה? +
+5 !== 2 + 4 // true
+'Chris' !== 'Bob' // true
+5 !== 2 + 3 // false
+2 !== '2' // true; number versus string
+
+
<קטן מ- +
+6 < 10 // true
+20 < 10 // false
+
>גדול מ- +
+6 > 10 // false
+20 > 10  // true
+
+ +

משפטי תנאי

+ +

נחזור לפונקציית  ()checkGuess שלנו. אנחנו נרצה שהיא תעשה יותר מאשר להוציא הודעה פשוטה למשתמש. אנחנו רוצים לבדוק האם הניחוש של המשתמש היה נכון או לא  ולהגיב ובהתאם. 

+ +

 בנקודה זו, החלף את הפונקציה ()checkGuess עם הקוד הבא: 

+ +
function checkGuess() {
+  let userGuess = Number(guessField.value);
+  if (guessCount === 1) {
+    guesses.textContent = 'Previous guesses: ';
+  }
+  guesses.textContent += userGuess + ' ';
+
+  if (userGuess === randomNumber) {
+    lastResult.textContent = 'Congratulations! You got it right!';
+    lastResult.style.backgroundColor = 'green';
+    lowOrHi.textContent = '';
+    setGameOver();
+  } else if (guessCount === 10) {
+    lastResult.textContent = '!!!GAME OVER!!!';
+    setGameOver();
+  } else {
+    lastResult.textContent = 'Wrong!';
+    lastResult.style.backgroundColor = 'red';
+    if(userGuess < randomNumber) {
+      lowOrHi.textContent = 'Last guess was too low!';
+    } else if(userGuess > randomNumber) {
+      lowOrHi.textContent = 'Last guess was too high!';
+    }
+  }
+
+  guessCount++;
+  guessField.value = '';
+  guessField.focus();
+}
+ +

נעבור על כל חלק בקוד על מנת להסביר אותו: 

+ + + +

אירועים

+ +

בשלב זה הגדרנו את פונקציית ()checkGuess אבל היא לא תעשה כלום כי לא קראנו לה עדיין. באופן אידיאלי, אנחנו רוצים ״לקרוא״ (Invoke) לפונקציה כאשר המשתמש ילחץ על לחצן ״Submit guess" ועל מנת לבצע זאת אנחנו צריכים לעשות שימוש באירוע (Event). אירועים אלו דברים המתרחשים בתוך הדפדפן - כפתור נלחץ, דף נטען, וידאו מתנגן וכד׳ - ובהתאם, אנחנו יכולים להריץ בלוקים של קוד. הקונסטרקטור (בעברית: הבנאי), שמאזין לאותו אירוע שמתרחש בדפדפן נקרא מאזין אירוע (event listeners), ואילו בלוק הקוד שרץ בתגובה לאותו אירוע נקרא מטפל אירוע (event handlers).

+ +

הוסף את הקוד הבא מתחת לפונקציית ()checkGuess :

+ +
guessSubmit.addEventListener('click', checkGuess);
+ +

כאן אנו מוסיפים event listener לכפתור guessSubmit. מתודה זו מקבלת 2 ערכים (שנקראים ״ארגומנטים״) - הראשון הוא סוג האירוע שאנחנו רוצים להאזין לו (במקרה, אירוע מסוג  click), ואותו נזין כמחרוזת, עטוף בגרשיים, לאחר מכן פסיק, ואז את event handlers, שהוא בעצם הקוד שנרצה שירוץ ברגע שהתרחש האירוע (במקרה זה, פונקציית ()checkGuess ). תשומת לב כי אנחנו לא צריכים להכניס סוגריים כאשר אנחנו רושמים את הפונקציה checkGuess בתוך המתודה {{domxref("EventTarget.addEventListener", "addEventListener()")}}.

+ +

נסו לשמור ולרען את הדף כעת - הדוגמא אמורה לעבוד, עד נקודה מסויימת. הבעיה היחידה היא שכאשר המשתתף ינחש נכון או המשחק הסתיים כי נגמרו לו הנסיונות, המשחק יסתיים בצורה לא רצויה, מכיוון שלא הגדרנו את הפונקציה ()setGameOver שאמורה לרוץ ברגע שהמשחק יסתיים (הן אם המשתתף ניחש נכון והן אם למשתתף נגמרו הנסיונות).

+ +

כעת נוסיף את הקוד החסר ונשלים את הפונקציונליות של המשחק.  

+ +

השלמת הפונקציונליות של המשחק

+ +

כעת נוסיף את הפונקציה ()setGameOver מתחת לקוד הנוכחי: 

+ +
function setGameOver() {
+  guessField.disabled = true;
+  guessSubmit.disabled = true;
+  resetButton = document.createElement('button');
+  resetButton.textContent = 'Start new game';
+  document.body.appendChild(resetButton);
+  resetButton.addEventListener('click', resetGame);
+}
+ + + +

עת אנחנו צריכים להגדיר את הפונקציה הזו. הוסף את הקוד הבא מתחת לקוד הנוכחי: 

+ +
function resetGame() {
+  guessCount = 1;
+
+  const resetParas = document.querySelectorAll('.resultParas p');
+  for (let i = 0 ; i < resetParas.length ; i++) {
+    resetParas[i].textContent = '';
+  }
+
+  resetButton.parentNode.removeChild(resetButton);
+
+  guessField.disabled = false;
+  guessSubmit.disabled = false;
+  guessField.value = '';
+  guessField.focus();
+
+  lastResult.style.backgroundColor = 'white';
+
+  randomNumber = Math.floor(Math.random() * 100) + 1;
+}
+ +

קוד זה מאתחל הכל לאיך שהוא היה בתחילת המשחק: 

+ + + +

בשלב זה - יש לנו משחק שעובד במלואו. פשוט יחסית, אך עובד.  

+ +

כעת נדבר על אפשרויות נוספות שראינו, ואולי לא שמנו לב. 

+ +

לולאות

+ +

חלק מתוך הקוד למעלה שנרצה להתעמק בו הוא לולאת מסוג for.

+ +

לולאות הן עקרון מאוד חשוב בתכנות, ומאפשרות לנו להריץ קוד שוב ושוב, עד שתנאי מסויים יתרחש. 

+ +

על מנת להתחיל, פתח את הקונסולה שוב, והכנס את הקוד הבא: 

+ +
for (let i = 1 ; i < 21 ; i++) { console.log(i) }
+ +

מה קרה?

+ +

המספרים 1 עד 20 הודפסו לקונסולה. זה מכיוון שהשתמשנו בלולאה מסוג for. לולאה מסוג for מקבלת 3 ערכים (ארגומנטים):

+ +
    +
  1. ערך ראשון (Starting Value) - במקרה זה אתחלנו את המונה ל-1, אך אנו יכולים להתחיל בכל מספר שנרצה. כמו כן, את שם המשתנה i ניתן להחליף בכל שם שנרצה. אנו מבצעים שימוש ב-i כי זה קצר ונוח לזכור. 
  2. +
  3. תנאי יציאה (An exit condition) - כאן הגדרנו שתנאי היציאה מהלולאה, כלומר מתי הלולאה תפסיק לרוץ יהיה כל עוד i < 21. כלומר הלולאה תרוץ כל זמן ש-i יהיה קטן מ-21. ברגע ש-i יהיה שווה ל-21, הלולאה תפסיק לרוץ.
  4. +
  5. המוסיף (An incrementor) - כאן הגדרנו i++ כלומר נעלה את הערך של i  ב-1 בכל פעם שהקוד שנמצא בתוך הסוגריים המסולסלות {} מסיים לרוץ (כל איטרציה-iteration), עד אשר i יגיע לערך 21. במקרה הזה אנחנו פשוט מדפיסים לקונסולה את הערך של i בכל איטרציה (iteration), באמצעות {{domxref("Console.log", "console.log()")}}.
  6. +
+ +

בוא נסתכל כעת על הלולאה במשחק הניחוש שבנינו בתוך פונקציית ()resetGame:

+ +
let resetParas = document.querySelectorAll('.resultParas p');
+for (let i = 0 ; i < resetParas.length ; i++) {
+  resetParas[i].textContent = '';
+}
+ +

קוד זה יוצר משתנה מסוג מערך (נדבר עליו בהמשך) בשם resetParas המכיל רשימה של כל האלמנטים מסוג p הנמצאים בתוך אלמנט עם class בשם resultParas (במקרה שלנו זה האלמנט הבא: <div class="resultParas">). 

+ +

resetParas נוצר באמצעות שימוש במתודה {{domxref("Document.querySelectorAll", "querySelectorAll()")}}. 

+ +

לאחר מכן, הוא עובר באמצעות לולאה על כל איבר במערך, ומסיר את התוכן טקסט של כל איבר. 

+ +

דיון קצר על אובייקטים

+ +

נוסיף שיפור נוסף לפני שנמשיך בדיון : הוסיפו את הקוד הבא, מתחת לקוד זה: let resetButton; הנמצא בראש הדף, ולאחר מכן שמרו את הקובץ: 

+ +
guessField.focus();
+ +

קוד זה עושה שימוש במתודה בשם {{domxref("HTMLElement.focus", "focus()")}} על מנת לשים את סמן העבר בתוך הטקסט של תיבת הקלט {{htmlelement("input")}} ברגע שהדף נטען, כך שהמשתמש יוכל הזין את הניחוש הראשון שלו ישר כשהדף נטען, ללא צורך ללחוץ על תיבת הקלט. זוהי אמנם תוספת קטנה, אך היא משפרת השימוש של המשתמש ומאפשרת לו להבין באופן ויזואלי איך לשחק את המשחק.

+ +

נכנס לזה קצת יותר לעומק. ב-JavaScript, כל דבר הוא אובייקט. אובייקט הוא אוסף של מס׳ פונקציונליות וערכים אשר קובצו יחד לקבוצה אחת. אנו יכולים ליצור אובייקט בעצמו, אך נגע בנושא זה בהמשך. לצורך העניין במאמר הנוכחי, אנחנו נדון בקצרה באובייקטים המובנים בדפדפן, אשר מאפשרים לנו לעשות הרבה דברים שימושיים. 

+ +

במקרה הנוכחי, יצרנו תחילה קבוע בשם guessField אשר אחסן בתוכן הפנייה לתיבת קלט הטקסט ב-html - השורה הבאה יכולה להימצא באזור שבו הצרנו על המשתנים/קבועים בתחילת הקוד שלנו: 

+ +
const guessField = document.querySelector('.guessField');
+ +

על מנת לקבל את ההפניה הזו, השתמשנו במתודה  {{domxref("document.querySelector", "querySelector()")}} של האובייקט {{domxref("document")}}. דוגמא נוספת היא למתודה של אובייקט זה היא  ()querySelector אשר מקבלת מידע אחד מסוג סלקטור של CSS - ואותו סלקטור בוחר את האלמנט שאנחנו רוצים להפנות אליו. 

+ +

מכיוון ש-guessField מכיל כעת הפנייה לאלמנט {{htmlelement("input")}}, הוא יכול כעת לגשת למספר מאפיינים (properties) ומתודות (methods). באופן עקרוני, properties אלו משתנים המאוחסנים בתוך אובייקט, וחלקם לא יכולים לשנות את ערכם, ואילו methods אלו פונקציות המאוחסנות בתוך אובייקט. מתודה אחת אשר זמינה לאלמנט {{htmlelement("input")}}, היא ()focus, כך שאנחנו יכולים להשתמש בקוד למטה, על מנת לבצע פוקוס לתיבת קלט הטקסט: 

+ +
guessField.focus();
+ +

משתנים שאינם כוללים הפנייה לאלמנטים של טפסים, אינם יכולים לגשת למתודה הזו והיא לא תהיה זמינה עבורם. לדוגמא, הקבוע guesses מכיל הפנייה לאלמנט  {{htmlelement("p")}} ו-המשתנה guessCount מכיל מספר. 

+ +

לשחק ולנסות את האובייקטים של הדפדפן

+ +

בואו נשחק טיפה עם האובייקטים של הדפדפן.

+ +
    +
  1. פתחו תוכנית בדפדפן.
  2. +
  3. פתחו את browser developer tools ווודאו שהקונסולה מופיעה. 
  4. +
  5. רשמו guessField והקונסולה תציג לכם את המשתנה המכיל את האלמנט {{htmlelement("input")}}. אתם תבחינו שהקונסולה גם משלימה את השמות של האובייקטים שקיימים בסביבת ההרצה, כולל משתנה. 
  6. +
  7. כעת הזינו את הקוד הבא: 
  8. +
  9. +
    guessField.value = 'Hello';
    + המאפיין (property) בשם value מייצג את הערך שהוזן לתוך שדה הטקסט. ניתן לראות שעם הזנת הקוד לעיל, אנחנו משנים את הטקסט בשדה הטקסט. 
  10. +
  11. נסו לרשום בקונסולה guesses. הקונסולה תראה לכם שהוא מכיל אלמנט מסוג{{htmlelement("p")}}.
  12. +
  13. כעת הזן את הקוד הבא:
  14. +
  15. +
    guesses.value
    + הקונסולה תחזיר את הערך undefined מכיוון שאלמנט מסוג {{htmlelement("p")}} לא מקבל מאפיין מסוג value.
  16. +
  17. על מנת לשנות את הטקסט בתוך {{htmlelement("p")}} , אנחנו צריכים להשתמש במאפיין בשם  {{domxref("Node.textContent", "textContent")}}
  18. +
  19. כעת הזינו את הקוד הבא:
  20. +
  21. +
    guesses.textContent = 'Where is my paragraph?';
    +
  22. +
  23. כעת ננסה לשנות עיצוב באמצעות גישה ל-properties של האובייקט: +
    guesses.style.backgroundColor = 'yellow';
    +guesses.style.fontSize = '200%';
    +guesses.style.padding = '10px';
    +guesses.style.boxShadow = '3px 3px 6px black';
    + כל אלמנט שנמצא בדף מכיל מאפיין בשם style אשר הוא בעצמו אובייקט המכיל את כללי ה-CSS ה-inline שהוחלו על אותו אובייקט (ואף ניתן להחיל עליו כללי CSS או למחוק את הכללים הנוכחיים או להחליפם באחרים) . זה מאפשר לנו לשנות באופן דינמי את העיצוב של האובייקט באמצעות JavaScript.
  24. +
+ +

לסיכום

+ +

סיימנו לבנות את הדוגמא. בדקו את הקוד הסופי שלכם וודאו שהוא עובד כראוי, או שתוכלו לשחק עם הגרסה הסופית פה אם אתם לא מצליחים לגרום לקוד שלכם לעבוד, נסו להשוות אותו אל מול קוד המקור

+ +

{{PreviousMenuNext("Learn/JavaScript/First_steps/What_is_JavaScript", "Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/arrays/index.html b/files/he/learn/javascript/first_steps/arrays/index.html new file mode 100644 index 0000000000..44675349fa --- /dev/null +++ b/files/he/learn/javascript/first_steps/arrays/index.html @@ -0,0 +1,564 @@ +--- +title: Arrays - מערכים +slug: Learn/JavaScript/First_steps/Arrays +translation_of: Learn/JavaScript/First_steps/Arrays +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps/Silly_story_generator", "Learn/JavaScript/First_steps")}}
+ +

במאמר האחרון במודול זה, אנחנו נדע במערכים - דרך מסודרת וקלה לאחסן רשימה פריטי מידע תחת משתנה אחד. אנחנו נבין מדוע שמוש במערך הוא שימושי כל כך ונבין כיצד ליצור מערך, לשנות את האיברים שלו, למחוק או להוסיף לו איברים ועוד.

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של HTML, CSS ו - JavaScript.
Objective:הבנה של מערכים וביצוע פעולות שונות עליהם
+ +

מה זה בעצם מערך - Array?

+ +

מערכים לרוב מתוארים כאובייקט של מעין רשימה (״list-like objects״). בעיקרון, אלו אובייקטים המכילים מספר ערכים שמאוחסנים יחד.

+ +

אובייקטים מסוג מערך יכולים להיות מאוחסנים בתוך משתנה ונוכל לטפל בהם בדיוק כמו שאנחנו מטפלים במשתנים אחרים.

+ +

אנו יכולים לטפל בכל ערך בודד באותו במערך או במערך בכללותו. אנחנו יכולים לעבור על כל איברי המערך אחד אחרי השני ולבצע בהם פעולות שונות. כך לדוגמא, אם יש לנו סדרה של מוצרים והמחירים שלהם מאוחסנים במערך, אנחנו רוצים לעבור על כל המחירים ולהדפיס אותם בחשבונית, ובמקביל לחשב את הסכום הכולל של המוצרים יחד עם המחירים שלהם.

+ +

אם לא היו לנו מערכים, היינו צריכים לשמור כל פריט במשתנה אחר, ואז לכתוב קוד שידפיס את המחיר בנפרד עבור כל משתנה. זה היה הופך את הקוד שלנו להרבה יותר מורכב וארוך ופחות יעיל. אם היו לנו 10 פריטי להדפיס לחשבונית זה עוד נשמע הגיוני, אך מה נעשה במצב שיש לנו 100 פריטים או 1,000 פריטים? נחזור לדוגמא זו בהמשך המאמר על מנת להבין כיצד ניתן לעשות זאת.

+ +

כמו במאמרים קודמים, אנחנו נלמד על הבסיס של מערכים באמצעות שימוש בקונסולה.

+ +

יצירת מערכים

+ +

מערכים כוללים סוגריים מרובעות [] וכל איבר (אלמנט) במערך מופרד מאיבר אחר על ידי פסיק ,.

+ +
    +
  1. נניח ואנחנו רוצים לאחסן רשימת פריטים בתוך מערך. הכניסו את הקוד הבא לקונסולה: +
    let shopping = ['bread', 'milk', 'cheese', 'hummus', 'noodles'];
    +shopping;
    +
  2. +
  3. בדוגמא למעלה, כל איבר הוא מחרוזת, אך ניתן לאחסן כל מיני סוגי ערכים בתוך מערכים - מחרוזות, מספרים, אובייקטים ואפילו מערכים נוספים. לדוגמא: +
    let sequence = [1, 1, 2, 3, 5, 8, 13];
    +let random = ['tree', 795, [0, 1, 2]];
    +
  4. +
  5. לפני שתמשיכו, נסו ליצור מספר מערכים וחקרו את האפשרויות השונות.
  6. +
+ +

גישה לאיברי המערך ושינוי שלהם

+ +

אנחנו יכולים לגשת לכל איבר במערך על ידי שימוש בסוגריים המרובעות [] בדיוק כפי שאנו ניגשים לתווים במחרוזת.

+ +
    +
  1. הכניסו את הקוד הבא לקונסולה: +
    shopping[0];
    +// returns "bread"
    +
  2. +
  3. אנחנו גם יכולים לשנות איבר במערך באמצעות מתן ערך חדש לאותו איבר במערך. ראו לדוגמא: +
    shopping[0] = 'tahini';
    +shopping;
    +// shopping will now return [ "tahini", "milk", "cheese", "hummus", "noodles" ]
    + +
    להזכירכם: מחשבים מתחילים לספור מ-0 ולא מ-1. בדיוק כמו במחרוזות, כך גם במערכים.
    +
  4. +
  5. שימו לב שמערך בתוך מערך נקרא מערך רב מימדי. אנחנו יכולים לגשת לאיבר שנמצא בתוך מערך מסויים, כאשר המערך הזה נמצא בתוך מערך באמצעות שרשור של שתי סוגריים מרובעות ביחד. לדוגמא, על מנת לגשת לאיבר במערך שהוא האיבר השלישי במערך המשורשר והמערך המשורשר הוא האיבר השלישי במערך הראשי בשם random, אנו נצטרך לרשום זאת בצורה הבאה: +
    random[2][2];
    +
  6. +
  7. נסו לעשות כמה פעולות ושינויים בעצמכם על מנת להבין את הדרך שבה אנו ניגשים לאיברים במערך.
  8. +
+ +

מציאת האורך של מערך

+ +

מציאת האורך של מערך - כלומר כמה איברים יש לו - מתבצעת בדיוק באותה דרך שבה אנחנו מוצאים את מספר התווים במחרוזת - באמצעות המאפיין {{jsxref("Array.prototype.length","length")}}.

+ +

נסו להזין את הקוד הבא:

+ +
sequence.length;
+// should return 7
+ +

מאפיין length  גם ישמש אותנו לצורך מעבר על איברי המערך באמצעות לולאת for וביצוע פעולות כלשהן שנבחר על כל איבר ואיבר, כך שהלולאה תעבור על כל איברי המערך. לדוגמא:

+ +
let sequence = [1, 1, 2, 3, 5, 8, 13];
+for (let i = 0; i < sequence.length; i++) {
+  console.log(sequence[i]);
+}
+ +

אנחנו נלמד יותר לעומק על לולאות במאמרים הבאים, אך בהסבר פשוט, הקוד הנוכחי אומר את הדברים הבאים:

+ +
    +
  1. הרצת הקוד שנמצא בין הסוגריים המסולסלות {} על כל איבר במערך החל מהאיבר הראשון במערך (אשר נמצא במיקום 0 כמובן). את מספר המיקום של האיבר במערך אותו אנו מייצגים באמצעות המשתנה i.
  2. +
  3. הרצת הלולאה מסוג for כל עוד מיקומו של האיבר במערך (שכאמור מיוצג בכל ריצה על ידי המשתנה i) קטן מסך כל האיברים במערך. כלומר, במקרה זה הקוד בתוך הסוגריים המסולסלות {} לא ירוץ כאשר המשתנה i יהיה שווה ל-7. (להזכירכם - אמנם יש שבעה איברים במערך, אך הספירה שלהם מתחילה ב-0, ולכן האיבר השביעי במערך, יהיה במיקום/אינדקס השישי וזו הסיבה שהשתמשנו בסימן קטן מאורך המערך (>) ולא שווה לאורך המערך (=) שכן אז היינו מקבלים במיקום השביעי את הערך undefiened.
  4. +
  5. בתוך הסוגריים המסולסלות, רשמנו את הקוד שאנחנו רוצים שיבוצע במסגרת כל ריצה של הלולאה. במקרה הזה, הדפסנו לקונסולה את הערך הנוכחי של האיבר שעליו רצה הלולאה באותה ריצה. לצורך הדפסה לקונסולה עשינו שימוש ב-()console.log.
  6. +
+ +

מס׳ מתודות שימושיות של מערכים

+ +

בחלק זה אנחנו נסתכל על מתודות שימושיות של מערכים או מתודות שימושיות הקשורות למערכים, המאפשרות לנו להפוך מחרוזת למערך ומערך למחרוזת ונלמד כיצד להכניס איברים נוספים לתוך מערכים או להסיר איברים ממערך. 

+ +

המרה בין מחרוזות ומערכים

+ +

הרבה פעמים אנחנו נקבל מידע גולמי אשר נמצא במחרוזת ארוכה ונרצה להפריד אותו לפריטי מידע אשר עמם נוכל לעשות שימוש באמצעות המרתה של המחרוזת למבנה אחר.

+ +

על מנת לעשות זאת, אנחנו נעשה שימוש במתודה {{jsxref("String.prototype.split()","split()")}}. מתודה זו ממירה מחרוזת למערך ומקבלת פרמטר אחד - התו שהמתודה תחפש בתוך המחרוזת שנרצה להמיר למערך וכאשר היא תמצא את אותו תו בכל פעם, היא תפריד בין החלק במחרוזת שנמצא לפני תו זה לבין החלק במחרוזת שנמצא אחריו, וכך היא בעצם מחלקת את המחרוזת לאיברים במערך. התוצאה של מתודה זו מערך שהאיברים שלו הם המחרוזות משנה שהופרדו.

+ +
+

לתשומת לב: מבחינה טכנית זוהי מתודה של מחרוזות ולא מתודה של מערכים.

+
+ +
    +
  1. בואו ננסה להבין כיצד מתודה הזו עובדת: צרו מחרוזת בקונסולה כגון זו: +
    let myData = 'Manchester,London,Liverpool,Birmingham,Leeds,Carlisle';
    +
  2. +
  3. עכשיו הפרידו בין התווים במחרוזת באמצעות המתודה {{jsxref("String.prototype.split()","split()")}} כך שכל פעם שיופיע התו , תבוצע העברה של התווים שלפניו לאיבר חדש במערך שנקרא myArray: +
    let myArray = myData.split(',');
    +myArray;
    +
  4. +
  5. נסו למצוא את האורך של המערך החדש וכן לקבל את הערך של חלק מהאיברים שלו: +
    myArray.length;
    +myArray[0]; // the first item in the array
    +myArray[1]; // the second item in the array
    +myArray[myArray.length-1]; // the last item in the array
    +
  6. +
  7. ניתן כמובן גם להמיר מערך למחרוזת באמצעות שימוש במתודה {{jsxref("Array.prototype.join()","join()")}}. נסו את הקוד הבא: +
    let myNewString = myArray.join(',');
    +myNewString;
    +
  8. +
  9. דרך נוספת של המרת מערך למחרוזת נוכל לעשות באמצעות שימוש במתודה {{jsxref("Array.prototype.toString()","toString()")}}.
  10. +
  11. המתודה ()toString הרבה יותר פשוטה מאשר ()join שכן היא לא מקבלת שום פרמטר/ארגומנט, אך יש לה מספר הגבלות. עם המתודה ()join אנחנו יכולים להגדיר איזה תו אנחנו נרצה שיפריד בין התווים במחרוזת. נסו לדוגמא להריץ שוב את שורה מספר 4, אך עם תו מפריד אחד כמו / או -. +
    let dogNames = ['Rocket','Flash','Bella','Slugger'];
    +dogNames.toString(); //Rocket,Flash,Bella,Slugger
    +
  12. +
+ +

הוספה והסרה של איברים במערך

+ +

בחלק זה נלמד כיצד להוסיף ולהסיר איברים במערך. אנו נעשה שימוש במערך myArray שהוגדר למעלה. לנוחיותכם, הנה הוא שוב:

+ +
let myArray = ['Manchester', 'London', 'Liverpool', 'Birmingham', 'Leeds', 'Carlisle'];
+ +

על מנת להוסיף איבר לסוף של מערך אנחנו משתמשים במתודות {{jsxref("Array.prototype.push()","push()")}} ובמתודה {{jsxref("Array.prototype.pop()","pop()")}} להסיר איבר מסוף המערך.

+ +
    +
  1. ננסה תחילה את המתודה ()push — שימו לב שאנחנו צריכים לכלול איבר אחד או יותר כארגומנט/פרמטר למתודה זו, אשר אותו נרצה להכניס לסוף המערך: + +
    myArray.push('Cardiff');
    +myArray;
    +myArray.push('Bradford', 'Brighton');
    +myArray;
    +
    +
  2. +
  3. כשאנו מבצעים את המתודה הזו, אנו מקבלים כערך מוחזר את ה-length החדש של המערך, לאחר ההוספה. אם נרצה לשמור את ה-length החדש, אנחנו נצטרך לשמור אותו במשתנה חדש: +
    let newLength = myArray.push('Bristol');
    +myArray;
    +newLength;
    +
  4. +
  5. הסרה של האיבר האחרון במערך מתבצע באמצעות המתודה ()pop על המערך. נסו את הקוד הבא: +
    myArray.pop();
    +
  6. +
  7. האיבר האחרון במערך הוסר, והערך המוחזר של המתודה זו היא האיבר האחרון שהוסר. על מנת לשמור את האיבר הזה במשתנה חדש, ניתן לרשום את הקוד הבא: +
    let removedItem = myArray.pop();
    +myArray;
    +removedItem;
    +
  8. +
+ +

{{jsxref("Array.prototype.unshift()","unshift()")}} וכן {{jsxref("Array.prototype.shift()","shift()")}} עובדות בדיוק אותו הדבר כמו ש-()push ו- ()pop, בהתאמה, למעט העבודה שהן מוסיפות/מסירות איברים מתחילת המערך ולא מסופו.

+ +
+

לתשומת לב:{{jsxref("Array.prototype.push()","push()")}} ו- {{jsxref("Array.prototype.pop()","pop()")}} המתודות משנות את מערך המקור.

+
+ +
    +
  1. מתודת ()unshift — הזינו את הקוד הבא לקונסולה: + +
    myArray.unshift('Edinburgh');
    +myArray;
    +
  2. +
  3. מתודת ()shift— הזינו את הקוד הבא לקונסולה: +
    let removedItem = myArray.shift();
    +myArray;
    +removedItem;
    +
  4. +
+ +

תרגול עצמאי : הדפסת המוצרים

+ +

נחזור לדוגמא שנתנו בתחילת המאמר - הדפסת שמות המוצרים והמחירים שלהם על חשבונית, וכן חישוב של המחירים שלהם בתחתית החשבונית. בדוגמא הבאה יש שורות עם הערות המכילות מספרים (לדוגמא: number 1). כל שורה שכזו נועדה לציין בפניכם את המיקום שבו תצטרכו להוסיף קוד לקוד הקיים:

+ +
    +
  1. מתחת להערה number 1 // יש מספר של מחרוזות, כאשר כל אחת כוללת שם של מוצר והמחיר שלו, מופרדים על ידי הסימן ;. אנו נרצה שתהפכו את המחרוזות הללו למערך ותאחסנו אותו במשתנה שנקרא products.
  2. +
  3. בשורה שבה נמצאת העקב number 2 // נמצאת לולאת ה-for שלנו. בשורה זו יש כרגע i <= 0, שזהו מבחן תנאי אשר גורם ל-for loop לרוץ רק פעם אחת, מכיוון שבפועל הוא ״תפסיק לרוץ כאשר i לא קטן יותר או שווה ל-0״, ו-i מתחיל ב-0. אנו נרצה שתחליפו את התנאי זה בקוד אחר כך שהלולאה תפסיק לרוץ כאשר i לא יהיה קטן יותר מהאורך של products.
  4. +
  5. מתחת להערה number 3 // אנו רוצים שתכתבו קוד שמפצל את האיבר הנוכחי במערך (name:price) ל-2 איברים נפרדים - אחד כולל את שם הפריט ואחד הכולל את מחירו. אם אתם לא בטוחים איך לבצע זאת, ראו מתודות שימושיות של מחרוזות והעמיקו בחלק {{anch("המרה בין מחרוזות ומערכים")}} של מאמר זה.
  6. +
  7. כחלק מהקוד למעלה, אולי גם נרצה להמיר את המחירים ממחרוזת למספר. אם אתם לא זוכרים כיצד לבצע זאת, קיראו את המאמר הבא, בחלק של ״מספרים מול מחרוזות״.
  8. +
  9. בראשית הקוד הנוכחי קיים משתנה שנקרא total אשר הוצהר והושם לו ערך של 0. בתוך הלולאה (מתחת number 4 //) אנו נרצה שתוסיפו שורת קוד שמוסיפה את האיבר שמייצג את המחיר הנוכחי למשתנה total בכל ריצה של הלולאה, כך שבסוף, המחיר הכולל (total)יודפס לחשבונית. אולי תרצו לחזור ולעיין ב-אופרטורים להשמה על מנת לעשות זאת.
  10. +
  11. בנוסף, נרצה שתשנו את השורה שמתחת להערה // number 5// כך שהמשתנה itemText יהיה בצורת כתיבה הזו לכל איבר ל-"current item name — $current item price", לדוגמא: "Shoes — $23.99" , כך שהמידע יוצג בתצורה הזו על גבי החשבונית. שימו לב שמדובר באיחוד של מחרוזות, לא משהו מורכב מדי שלא למדנו כבר.
  12. +
+ + + +

{{ EmbedLiveSample('Playable_code', '100%', 730, "", "", "hide-codepen-jsfiddle") }}

+ +

למידה עצמאית: 5 החיפושים האחרונים

+ +

שימוש נפוץ שאנחנו עושים במתודות של מערכים כמו {{jsxref("Array.prototype.push()","push()")}} ו- {{jsxref("Array.prototype.pop()","pop()")}} הוא כאשר אנחנו שומרים רישום של מספר פריטים מסויימים כגון עשרת החיפושים האחרונים שביצע המשתמש או כאשר אנחנו משתמשים באנימציה המציגה כמות מסויימת של פריטים המוצגים על גבי המסך המתחלפים ביניהם, כך שכל פעם יהיה רק מספר מסויים של פריטים. בדוגמאות אלו אובייקטים חדשים נוצרים ומתווספים למערך ואילו ישנים נמחקים על מנת לשמור על המספר שרצינו.

+ +

בדוגמאות הבאות אנחנו הולכים לעשות שימוש פשוט יחסית במתודות אלו - יש לנו אתר עם תיבת חיפוש. הרעיון הוא שכאשר ערכים מוכנסים לתיבת החיפוש, חמשת החיפושים הקודמים מוצגים ברשימה. כאשר כמות החיפושים הקודמים עולה על חמש, החיפוש הקודם ביותר נמחק מהרשימה ובמקומו נכנס החיפוש האחרון ביותר בתחילת הרשימה, כך שתמיד יוצגו לנו חמשת החיפושים האחרונים.

+ +
+

לתשומת לב: ביישום חיפוש אמיתי, יתאפשר לנו רוב הסיכויים ללחוץ על החיפושים הקודמים ולחזור לאותם חיפושים. בדוגמא הבא נרצה להשאיר את הנושא פשוט.

+
+ +

על מנת להשלים את המשימה, הנכם נדרשים:

+ +
    +
  1. להוסיף שורת מתחת להערה number 1// שמוסיפה את הערך הנוכחי שהוזן לתיבת החיפוש לתוך תחילת המערך. ניתן למצוא את הערך שהוזן לתיבת החיפוש באמצעות searchInput.value.
  2. +
  3. הוסיפו מתחת להערה number 2// שורת קוד שמסירה את הערך שנמצא כרגע בסוף המערך.
  4. +
+ + + +

{{ EmbedLiveSample('Playable_code_2', '100%', 700, "", "", "hide-codepen-jsfiddle") }}

+ +

לסיכום

+ +

לאחר קריאה של מאמר זה, אתם ככל הנראה מבינים כמה מערכים הם שימושים עבורנו. אנחנו נראה אותם לאורך כל הדרך, בדרך כלל בצירוף לולאת for על מנת לבצע פעולה כלשהו על כל איבר במערך. אנחנו נלמד במודול הבא את הדברים השימושיים והבסיסיים שניתן לבצע עם לולאות מסוגים שונים, וביניהן לולאות for.

+ +

כעת, בצעו את התרגול הנמצא במאמר הבא, המרכז את מרבית הנושאים שלמדנו במודול זה.

+ +

ראו גם

+ + + +

{{PreviousMenuNext("Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps/Silly_story_generator", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/index.html b/files/he/learn/javascript/first_steps/index.html new file mode 100644 index 0000000000..1cccf0cf39 --- /dev/null +++ b/files/he/learn/javascript/first_steps/index.html @@ -0,0 +1,64 @@ +--- +title: צעדים ראשונים ב - JavaScript +slug: Learn/JavaScript/First_steps +tags: + - JavaScript + - אופרטורים + - מאמר + - מדריך + - מודול + - מחרוזות + - מספרים + - מערכים + - משימה + - משתנים + - מתחילים +translation_of: Learn/JavaScript/First_steps +--- +
{{LearnSidebar}}
+ +

במודול JavaScript הראשון שלנו, אנחנו קודם כל נענה על כמה שאלות בסיסיות כמו "מהי JavaScript?", "איך היא ניראת?", ו"מה היא יכולה לעשות?", לפני שנתקדם לניסיון הפרקטי הראשון בכתיבת JavaScript. לאחר מכן נדון בכמה תכונות מפתח של JavaScript בפירוט, כגון משתנים, מחרוזות, מספרים ומערכים.

+ +

דרישות מוקדמות

+ +

לפני שנתחיל מודול זה, אינכם צריכים כל ידע מוקדם בJavaScript, אבל יש לכם צורך בהיכרות עם  HTML ו-CSS. מומלץ לעבור על המודולים הבאים לפני תחילת הלימוד אודות JavaScript:

+ + + +
+

הערה: אם אתם עובדים על מחשב\טבלט\מכשיר אחר שאין לכם אפשרות ליצור עליו קבצים אישיים, אתם יכולים לנסות את (רוב) דוגמאות הקוד על תוכנות קוד אינטרנטיות כמו JSBin או Thimble.

+
+ +

מדריכים

+ +
+
מה זה JavaScript?
+
ברוכים הבאים לקורס JavaScript למתחילים של MDN! במאמר ראשון זה אנו נסתכל על  JavaScript מלמעלה, ונענה על שאלות כמו "מה זה?", ו-"מה זה עושה?", ונדאג שתרגישו בנוח עם המטרה של JavaScript.
+
צלילה ראשונית לתוך JavaScript
+
לאחר שלמדתם משהו על התאוריה של JavaScript, ומה אתם יכולים לעשות איתה, נמשיך לקורס מזורז על הפיצ'רים הבסיסיים של JavaScript באמצעות הדרכה מעשית בלבד. כאן נבנה משחק "נחש את המספר" פשוט, צעד אחר צעד.
+
מה השתבש? פתרון בעיות ב-JavaScript
+
כשבניתם את משחק ״נחש את המספר״ במאמר הקודם, בטח שמתם לב שהוא לא עבד. אל חשש - המאמר הנוכחי נועד לחסוך לך זמן בפתרון הבעיה ולהעניק לך כמה טיפים פשוטים איך למצוא ולתקן שגיאות בתוכניות JavaScript.
+
אחסון האינפורמציה שאתם צריכים — משתנים
+
לאחר קריאת המאמרים הקודמים אתם אמורים לדעת מה היא JavaScript, מהם היכולות שלה, איך להשתמש בה לצד טכנולוגיות אינטרנט אחרות, ואיך ניראים הפיצ'רים הראשיים שלה במבט מלמעלה. במאמר זה נקבל את הבסיס האמיתי, בהתבוננות על איך לעבוד עם אבני הבנין הבסיסיות ביותר של JavaScript — המשתנים.
+
מתמתיקה בסיסית ב-JavaScript - מספרים ואופרטורים
+
בחלק הזה של הקורס, נדון במתמתיקה של JavaScript - כיצד אנו יכולים לצרף אופרטורים שונים ואפשרויות נוספות על מנת לבצע מניפולציות שונות על מספרים לצרכים שלנו.
+
התמודדות עם טקסט - מחרוזות ב-JavaScript.
+
במאמר זה נתמקד במחרוזות - כך טקסט נקרא בתכנות. נסכתל על הדברים הבסיסיים שצריך לדעת על מחרוזות כשלומדים תכנות בשפת JavaScript, כמו יצירת מחרוזות, חיבור מחרוזות וכיוצ״ב.
+
מתודות שימושיות של מחרוזות
+
לאחר שלמדנו את הבסיס של מחרוזות, נלמד מס׳ פעולות שימושיות שאנחנו יכולים לבצע על המחרוזות באמצעות מתודות מובנות, כגון מציאת האורך של מחרוזת טקס, חיבור ופיצול של מחרוזות, הוצאת תו אחד מתוך מחרוזת ועוד.
+
מערכים
+
במאמר האחרון של מודול זה, נסתכל על מערכים - דרך מסודרת לאחסכן רשימה של פריטי מידע, תחת משתנה אחד. נבין מדוע שימוש במערך הוא שימושי מאוד, ולאחר מכן נבין כיצד ליצור מערך, לאחזר מערך, להוסיף או להסיר איברים ממערך ועוד. 
+
+ +

בוחנים

+ +

הבחנים הבאים יבדקו את ההבנה שלכם בבסיס של JavaScript כפי שנלמד במדריך.

+ +
+
יישום אינטרנט ליצירת סיפורים טיפשיים
+
במשימה זו אתם תתבקשו לקחת את הידע שלמדתם במאמרים של המודול הזה, ולממש אותם בכך שתיצרו יישום שמייצר סיפורים טיפשיים באופן אקראי. בהצלחה!.
+
diff --git a/files/he/learn/javascript/first_steps/math/index.html b/files/he/learn/javascript/first_steps/math/index.html new file mode 100644 index 0000000000..e5c22c141f --- /dev/null +++ b/files/he/learn/javascript/first_steps/math/index.html @@ -0,0 +1,427 @@ +--- +title: מתמתיקה בסיסית ב-JavaScript - מספרים ואופרטורים +slug: Learn/JavaScript/First_steps/Math +translation_of: Learn/JavaScript/First_steps/Math +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps")}}
+ +

בשלב זה של הקורס, נדון קצת במתמתיקה שבשפת JavaScript - איך אנחנו יכולים להשתמש ב - {{Glossary("Operator","operators")}} ועוד אפשרויות על מנת להצליח לבצע מניפולציות וחישובים שונים על מספרים לצרכים שלנו

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה:הכרות עם שימוש במתמתיקה באופן בסיסי ב-JavaScript.
+ +

כולנו אוהבים מתמטיקה

+ +

האמת לא ממש. חלקנו כן וחלקנו ממש לא. אך לא ניתן להכחיש את העובדה שמתמטיקה היא נדבך חשוב בחיים ובתכנות בפרט. הרבה ממה שאנחנו עושים מתבסס על עיבוד מידע מספרי, חישוב ערכים חדשים וכד׳ ולכן לא נופתע לדעת של-JavaScript יש סט מלא של פונקציות מתמתיטקיות מובנה בה.

+ +

מאמר זה ידון רק בחלקים הבסיסים שאנחנו צריכים לדעת כרגע.

+ +

סוגי מספרים

+ +

בתכנות, אפילו המספר העשרוני שאנחנו מכירים, הוא יותר מורכב ממה שנאחנו חושבים. אנחנו מתשמשים במושגים שונים על מנת לתאר סוגים שונים של מספרים עשרוניים. לדוגמא:

+ + + +

יש לנו אפילו כמה מערכות של מספרים! לדוגמא, מערכת מספרים דצימלית היא מבוססת על בסיס 10, כלומר, היא עושה שימוש בספרות 0-9 בכל עמודה. אבל יש עוד. לדוגמא:

+ + + +

לפני שאתם מתחילים לחשוש ולהתבלבל, תנשמו. נדיר שנשתמש במספרים שאינם מספרים דצימליים.

+ +

חדשות טובות נוספות הן שלא כמו בשפות תכנות אחרות, לשפת JavaScript יש רק סוג מידע אחד עבודה מספרים - {{jsxref("Number")}}. כלומר, לא משנה איזה סוג מספרים אנחנו נקבל/נזין בקוד, אנחנו נטפל בו בדיוק באותה צורה.

+ +

זה הכל מספרים

+ +

בוא נשחק קצת עם המספרים ונבין את הסינטקס הבסיסי שנצטרך. הכניסו את הקוד הבא לקונסולה:

+ +
    +
  1. קודם כל, נצהיר על שני משתנים ונשים בהם ערכים. לאחר מכן נזין את שמות המשתנים בקונסולה לוודא שהערכים הושמו כראוי: +
    let myInt = 5;
    +let myFloat = 6.667;
    +myInt;
    +myFloat;
    +
  2. +
  3. מספרים אלו בעצם ערכים שנרשמים ללא גרשיים - נסו להזין מספרים עטופים בגרשיים וראו מה קורה.
  4. +
  5. בוא נבדוק האם הערכים השונים שהזנו הם מאותו סוג. האופרטור שבודק את זה הוא {{jsxref("Operators/typeof", "typeof")}}. רשמו את הקוד הבא מתחת לקוד הנוכחי: +
    typeof myInt;
    +typeof myFloat;
    + אתם אמורים לקבל "number" בשני המקרים. ברור לנו שעיסוק בסוג מספר אחד הרבה יותר קל מאשר עם כמה סוגים.
  6. +
+ +

Arithmetic operators - אופרטורים מתמתטיים

+ +

אופרטורים מתמטיים הם האופרטורים הבסיסים שאנחנו משתמשים בהם על מנת לבצע חישובים:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
אופרטורשםמטרהדוגמא
+חיבורחיבור6 + 9
-חיסורחיסור20 - 15
*כפלכפל3 * 7
/חילוקחילוק10 / 5
%שארית +

השארית מחלוקת שני מספרים

+
+

8 % 3 (מחזיר 2, שכן 3 נכנס ב- 8 פעמיים, והשארית היא 2,).

+
**חזקהמכפיל את ה-base ב-מעריך , לדוגמא, המספר שיהיה נקוב ב- base עם המספר שיהיה נקוב ב- מעריך יגרום להכפלה של המספר שבבסיס כפול המספר שבמעריך. הוצג לראשונה ב- EcmaScript 2016.5 ** 5 (מחזיר 3125, שזה זהה ל- 5 * 5 * 5 * 5 * 5).
+ +
+

לתשומת לב: לפעמים ניתקל במספרים המעורבים בחישובים כ- {{Glossary("Operand", "operands")}}.

+
+ +
+

לתשומת לבד: לפעמים אנחנו נראה שמשתמשים במתודה הישנה {{jsxref("Math.pow()")}} אשר פועלת בדרך דומה. לדוגמא, בקוד Math.pow(7, 3), 7 הבסיס הוא 7 ו-3 הוא המעריך בחזקה, והתוצאה תהיה 343. Math.pow(7, 3) is שזה שווה ל- 7**3.

+
+ +

כנראה לא צריך ללמד אתכם מתמתיקה בסיסית, אבל אנו רוצים לוודא את ההבנה שלכם לגבי הסינטקס. נסו להכניס את הדוגמאות הבאות לקונסולה על מנת להכיר את הסינטקס.

+ +
    +
  1. הכניסו דומאות פשוטות כמו: +
    10 + 7
    +9 * 8
    +60 % 3
    +
  2. +
  3. אתם גם יכולים לנסות להצהיר על משתנים להשים להם מספרים כערכים, ונסו לעשות עליהם חישובים - המשתנים יתנהגו בדיוק כמו שיתנהגו הערכים שהם מחזיקים בהם. כך לדוגמא: +
    let num1 = 10;
    +let num2 = 50;
    +9 * num1;
    +num1 ** 3;
    +num2 / num1;
    +
  4. +
  5. נסו להזין קצת ביטויים יותר מורכבים, כמו: +
    5 + 10 * 3;
    +num2 % 9 * num1;
    +num2 + num1 / 8 + 2;
    +
  6. +
+ +

כפי ששמתם לב, חלק מהחישובים שביצענו מחזירים לנו תוצאה בהתאם לכללי הקדימות המתמטיים:

+ +

סדר עדיפות אופרטורים

+ +

אם נסתכל על הדוגמא למעלה, נניח ש- num2 הוא 50 ו- num1 הוא 10 :

+ +
num2 + num1 / 8 + 2;
+ +

בני אדם רגילים עלולים להניח ש- "50 ועוד 10 שווה 60", ואז "8 ועוד 2 שווה 10", ואז "60 חלקי 10 שווה 6".

+ +

אבל הדפדפן הולך לפי כללי החישוב המתמטים "10 חלקי 8 שווה 1.25", ואז "50 עוד 1.25 ועוד 2 שווה 53.25".

+ +

זהו מה שנקרא סדר עדיפות בין אופרטורים — חלק מהאופרטורים יבוצעו או יחולו לפני אופרטורים אחרים כשהדפדפן ינסה לחשב את התוצאה של החישוב. סדר עדיפות בין אופרטורים בשפה הוא בהתאם למה שלמנו בשיעורי מתמתיקה - כפל וחילוק תמיד ראשונים, ואז חיבור וחיסור, כאשר החישוב מתבצע מצד שמאל לימין.

+ +

אם נרצה לעקוף זאת, נצטרך לעשות שימוש בסוגריים רגילות סביב לחלקים שנרצה לבצע קודם. כך, על נת לקבל את התוצאה של 6, נצטרף לרשום את הקוד בצורה כזאת:

+ +
(num2 + num1) / (8 + 2);
+ +

נסו כדי לראות.

+ +
+

לתשומת לב: רשימה מלאה של האופטורים ב- JavaScript וסדר העדיפות שלהם ניתן למצוא בביטויים ואופרטורים.

+
+ +

אופרטים להעלאה/הורדה - Increment and decrement operators

+ +

לפעמים אנחנו רוצים להוסיף לערך או להוריד ממנו מספר אחד. ניתן לבצע זאת בקלות באמצעות שימוש באופרטור (++) לצורך העלאה ב-1 ובאופרטור (--) לצורך הורדה ב-1. השתמשנו באופרטור ++ במשחק שלנו  "Guess the number" ב-מבט ראשוני ל- JavaScript כאשר הוספנו אחד למשתנה guessCount על מנת לעקוב אחר מספר הניחושים שביצע המשתשמש.

+ +
guessCount++;
+ +
+

לתשומת לב: אופרטורים אלו נמצאים בדרך כלל בלולאות, עליהן נלמד בהמשך הקורס. לדוגמא, אם אנחנו רוצים לעבור על רשימה של מחירים, ולהוסיף מס על כל אחד מהם, אנחנו יכולים להשתמש בלולאה על מנת לעבור על כל ערך ברשימה, ולעשות את החישוב המתאים של הוספת מס. האופטרטור הוספה ישמש להעביר אותנו לערך הבא כשנצטרך. ראה לדוגמא כיצד זה מבוצע בדף האינטרנט וגם בקוד המקור. בדקו האם אתם מזהים את האופרטורים שמשמשים להוספה/הורדה בתוך הלולאה, נדון בהם בהמשך.

+
+ +

ננסה לשחק קצת בקונסולה. על מנת להתחיל, שימו לב שאתם לא יכול החיל אותם ישר על מספר כלשהו, למרות שזה נראה מוזר, אבל בפועל, כשאנחנו מעלים את הערך, אנחנו לא מבצעים חישוב כלשהו, אלא אנחנו משימים את החדש, לאחר החישוב, בתוך המשתנה. הפעולה הבאה תחזיר לדוגמא שגיאה:

+ +
3++;
+ +

כלומר, אנחנו יכולים רק להעלות משתנה קיים ב-1. נסו להזין את הקוד הבא:

+ +
let num1 = 4;
+num1++;
+ +

משהו מוזר נוסף. כשאנחנו עושים זאת, הערך 4 מוחזר לנו בקונסולה. זה מכיוון שהדפדפן מחזיר את הערך הנוכחי ורק אז מעלה את הערך של המשתנה. ניתן לראות שהערך של המשתנה הועלה ב-1 אם נזין שוב את השם של המשתנה בקונסולה.

+ +
num1;
+ +

אותו הדבר לגבי --  נסו את הקוד הבא:

+ +
let num2 = 6;
+num2--;
+num2;
+ +
+

לתשומת לב: אנו יכולים לגרום לדפדפן לבצע זאת בצורה אחרת - כלומר, להעלות את המשתנה ב-1 ורק אז להחזיר לנו את הערך החדש של המשתנה באמצעות הוספת האופרטור לפני המשתנה ולא בסופו. נסו להזין את הקוד הבא ++num1 ואז --num2.

+
+ +

Assignment operators - אופרטורים להשמה

+ +

אופרטורים להשמה אלו אופרטים שמשימים ערך לתוך משתנה. ראינו זאת עם הבסיסי שבה: =- הוא פשוט משים את הערך מצד שמאל, למשתנה שמצויין מצד ימין.

+ +
let x = 3; // x contains the value 3
+let y = 4; // y contains the value 4
+x = y; // x now contains the same value y contains, 4
+ +

אך יש כאלה מורכבים יותר, שיכולים לתת לנו קיצורי דרך על מנת לרשום את הקוד שלנו נקי ויעיל יותר. הנפוצים הם:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
אופרטורשםמטרהדגומאקיצור עבור
+=חיבור והשמהמוסיף את הערך מצד ימין לערך שבמשתנה מצד שמאל, ואז מחזיר את הערך של המשתנה החדשx = 3;
+ x += 4;
x = 3;
+ x = x + 4;
-=חיסור והשמהמחסיר את הערך מצד ימין מתוך הערך שנמצא בתוך המשתנה מצד שמאל, ואז מחזיר את הערך של המשתנה החדשx = 6;
+ x -= 3;
x = 6;
+ x = x - 3;
*=כפל והשמהמכפיל את הערך מצד ימין עם הערך שנמצא במשתנה מצד שמאל, ואז מחזיר את הערך של המשתנה החדשx = 2;
+ x *= 3;
x = 2;
+ x = x * 3;
/=חילוק והשמהמחלק את הערך מצד ימין בערך שנמצא במשתנה מצד שמאל, ואז מחזיר את הערך של המשתנה החדשx = 10;
+ x /= 5;
x = 10;
+ x = x / 5;
+ +

נסו להזין חלק מהדוגמאות הרשומות למעלה לתוך הקונסולה, על מנת להבין את הרעיון. בכל מקרה, נסו לראות אם אתם מצליחים לדעת מה הערך שיוחזר לפני החישוב וההשמה.

+ +

כמובן שגם ניתן לבצע את החישובים וההשמות גם כאשר נמצאים משתנים משני צידי הסימן. לדוגמא:

+ +
let x = 3; // x contains the value 3
+let y = 4; // y contains the value 4
+x *= y; // x now contains the value 12
+ +
+

לתשומת לב: יש הרבה אופרטורים להשמה נוספים, אבל אלו הבסיסיים שאתם צריכים לדעת לעת עתה.

+
+ +

למידה אקטיבית: שינוי גודל של canvas box

+ +

בתרגיל זה נלמד כיצד לבצע מניפולציות על מספרים ואופרטורים, על מנת לשנות גודל של קופסא. הקופסא מצויירת באמצעות browser API אשר נקרא {{domxref("Canvas API", "", "", "true")}}. אין מה לדאוג לגבי איך הוא עובד, רק התרכזו בחישובים עצמם. הגובה והרוחב של הקופסא בפיקסלים יוגדרו על ידי המשתנים x ו- y, אשר קיבלו ערך התחלתי של 50.

+ +

{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/maths/editable_canvas.html", '100%', 620)}}

+ +

Open in new window

+ +

בבריבוע למעלה יש שתי שורות שמסומנות עם הערה - אלו השורות שאתם צריכים לעדכן על מנת להגדיל/להקטין את הקופסא, באמצעות שימוש באופרטורים השונים ו/או ערכים שונים. נסו את הדברים הבאים:

+ + + +

אל תדאגו אם אתם הורסים את הקוד לחלוטין. פשוט לחצו על הכפתור לאתחל את הכל.

+ +

Comparison operators - אופרטורים להשוואה

+ +

לפעמים נרצה לבצע מבחני אמת/שקר, ואז להריץ פעולות מסוימות בהתאם לתוצאת המבחן - בשביל זה נועדו אופרטורים להשוואה.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
אופרטורשםמטרהדוגמא
===השוואה מוחלטתאופרטור זה בודק הא הערכים מצד ימין וצד שמאל זהים לחלוטין5 === 2 + 4
!==שונים לחלוטיןבודק האם הערך מצד ימיןלא זהה לערך מצד שמאל5 !== 2 + 3
<קטן מ-בודק האם הערך בצד שמאל קטן מהערך בצד ימין.10 < 6
>גדול מ-בודק האם הערך בצד שמאל גדול מהערך בצד ימין.10 > 20
<=קטן מ- או שווה ל-בודק האם הערך בצד שמאל קטן מהערך בצד ימין או שווה לו.3 <= 2
>=גדול מ- או שווה ל-בודק האם הערך בצד שמאל גדול מהערך בצד ימין או שווה לו.5 >= 4
+ +
+

לתשומת לב: לפעמים תראו אנשים שמשתמשים באופרטור == או != במבחני בדיקת זהות/שונות שלהם. אלו אופרטורים תקינים לחלוטין בשפה, אבל הם שונים מ - ===/!==. הראשונים מבצעים בדיקה האם הערכים בצד ימין ובצד שמאל זהים מבחינת הערך עצמו, אך ״אדישים״ לגבי הזהות מבחינת סוג הערך. ואילו האחרונים מבצעים בדיקה של זהות/שונות מוחלטת - גם ערכים וגם סוג הערך. שימוש בבדיקת זהות/שונות מוחלטת נוטה להציג פחות שגיאות, אז המלצתנו היא להשתמש בהם.

+
+ +

אם תנסו להכניס כמה מהערכים הללו לקונסולה את תראה בחזרה ערכי true/false - אלו ערכים בולאינים, כפי שהזכרנו במאמר קודם. ערכים אלו מאוד שימושיים מכיוון שהם מאפשרים לבצע החלטות בקוד שלנו, ואנחנו משתמשים בהם בכל פעם שאנרצה לקבל החלטה מסוג כלשהו. בוליאנים יכולים לשמש עבור:

+ + + +

אנו נסתכל על דרך לכתוב קוד באמצעות שימוש בלוגיקה כשנלמד על משפטי תנאי במאמרים הבאים. לבינתיים, ראו דוגמא:

+ +
<button>Start machine</button>
+<p>The machine is stopped.</p>
+
+ +
const btn = document.querySelector('button');
+const txt = document.querySelector('p');
+
+btn.addEventListener('click', updateBtn);
+
+function updateBtn() {
+  if (btn.textContent === 'Start machine') {
+    btn.textContent = 'Stop machine';
+    txt.textContent = 'The machine has started!';
+  } else {
+    btn.textContent = 'Start machine';
+    txt.textContent = 'The machine is stopped.';
+  }
+}
+ +

{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/maths/conditional.html", '100%', 100)}}

+ +

פתח בחלון חדש

+ +

אתם יכולים לראות את האופרטור להשוואה מתקיים בתוך פונקציית ()updateBtn . במקרה זה, אנחנו לא בודקים האם שני ביטויים מתמידים הם בעלי ערך זהה, אלא אנחנו בודקים האם תוכן הטקסט של הכפתור כולל בתוכו מחרוזת מסויימת - אבל עדיין מדובר באותו עקרון שלמדנו עליו. אם הכפתור כולל בתוכו מחרוזת "Start machine" כשלוחצים עליו, אזי נשנה את השם שלו בהתאם. אם הוא כולל מחרוזת "Stop machine" אזי נבצע החלפה שוב.

+ +
+

לתשומת לב: אפשרות כזו להחליף בין שני מצבים נקראת toggle. הוא מחליף פשוט בין מצב אחד למצב שני.

+
+ +

לסיכום

+ +

במאמר זה כיסינו את הידע החשוב והכרחי שאתם צריכים לדעת על מספרים בשפה. לבינתיים. אתם תראו מספרים דיי הרבה פעמים, לכל אורך הלימוד, אז זה רעיון טוב שתתחילו להתרגל אליהם ולהבין אותם. אל תפחדו מהם.

+ +

במאמר הבא נבין איך השפה מאפשרת לנו לבצע מניפולציות שונות על מחרוזות וטקסטים ויצד נוכל לבצע בהם שימוש.

+ +
+

לתשומת לב: אם אתם מעוניינים להרחיב את הידיעות בנושא, ראו את הדפים שלנו בנושא  Numbers and dates ו-Expressions and operators.

+
+ +

{{PreviousMenuNext("Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/silly_story_generator/index.html b/files/he/learn/javascript/first_steps/silly_story_generator/index.html new file mode 100644 index 0000000000..8c4ca6772a --- /dev/null +++ b/files/he/learn/javascript/first_steps/silly_story_generator/index.html @@ -0,0 +1,139 @@ +--- +title: Silly story generator +slug: Learn/JavaScript/First_steps/Silly_story_generator +translation_of: Learn/JavaScript/First_steps/Silly_story_generator +--- +
{{LearnSidebar}}
+ +
{{PreviousMenu("Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}
+ +

בתרגול זה, תתבקשו ליישם את מה שלמדתם בכל המאמרים האחרונים במודול הנוכחי על מנת ליצור יישום קטן שמייצר סיפורים טיפשיים באופן רנדומלי. 

+ + + + + + + + + + + + +
ידע מוקדם:אנא וודאו כי עברתם על כל המאמרים במודול זה בטרם ביצוע התרגול.
מטרה:לוודא הבנה של יסודות שפת JavaScript ושימוש במשתנים, מספרים,אופרטורים, מחרוזות ומערכים. 
+ +

נקודת התחלה

+ +

על מנת להתחיל:

+ + + +
+

לתשומת לב: לחילופין, אתם יכולים לעשות שימוש ב- JSBin או Thimble על מנת להשלים את התרול. אתם יכולים להדביק לתוכם את ה HTML, CSS ואת JavaScript.

+
+ +

תקציר הפרוייקט

+ +

קיבלתם לידיכם HTML/CSS גולמי ומס׳ מחרווזת טקסט וכן מס׳ פונקציות של JavaScript. הנכם נדרשים לכתוב את הקוד הרלוונטי על מנת להפוך אותם לתוכנית שעושה את הדברים הבאים:

+ + + +

צילום המסך הבא יסייע לכם להבין כיצד זה אמור להיראות:

+ +

+ +

לתת לכם קצת יותר הכוונה, הסתכלו בפתרון העובד (אך אל תסתכלו על קוד המקור, אלא אם ממש נתקעתם)

+ +

פעולות נדרשות

+ +

אלו הפעולות שתידשו לבצע על מנת להשלים את התרגול בהצלחה

+ +

התקנה בסיסית:

+ +
    +
  1. צרו קובץ חדש שנקרא בשם main.js, באותה תיקיה שבה נמצא קובץ index.html שלכם
  2. +
  3. החילו את קובץ ה-JavaScript לתוך קובץ ה-HTML כפי שלמדנו באמצעות {{htmlelement("script")}} עם קישור ל-main.js. שימו אותו לפני תגית הסיום של </body>.
  4. +
+ +

אתחול משתנים ופונקציות:

+ +
    +
  1. בקובץ הטקסט הגולמי העתיקו את כל הקוד שמופיע תחת הכותרת "1. COMPLETE VARIABLE AND FUNCTION DEFINITIONS" והדביקו אותו בחלקו העליון של הקובץ main.js.זה נותן שלשה משתנים שמכילים הפניות לשדה הטקסט "Enter custom name" (המשתנה: customName) ולכפתור "Generate random story" (המשתנה:randomize) ולאלמנט {{htmlelement("p")}} (המשתנה:story) שבתחתית חלק ה HTML שאליו יועתק הסיפור בהתאמה. בנוסף יש לך פונקציה שנקראת randomValueFromArray() שלוקחת מערך, ומחזירה אחד מהפריטים המאוחסנים בתוך המערך באופן אקראי
  2. +
  3. כעת התבוננו בקטע השני שבקובץ הטקסט הגולמי "2. RAW TEXT STRINGS" זה מכיל מחרוזות טקסט   that will act as input into our program. We'd like you to contain these inside variables inside main.js: +
      +
    1. Store the third set of three strings inside an array called insertZ.
    2. +
    3. Store the second set of three strings inside an array called insertY.
    4. +
    5. Store the first set of three strings inside an array called insertX.
    6. +
    7. Store the first, big long, string of text inside a variable called storyText.
    8. +
    +
  4. +
+ +

Placing the event handler and incomplete function:

+ +
    +
  1. Now return to the raw text file.
  2. +
  3. Copy the code found underneath the heading "3. EVENT LISTENER AND PARTIAL FUNCTION DEFINITION" and paste it into the bottom of your main.js file. This: +
      +
    • Adds a click event listener to the randomize variable so that when the button it represents is clicked, the result() function is run.
    • +
    • Adds a partially-completed result() function definiton to your code. For the remainder of the assessment, you'll be filling in lines inside this function to complete it and make it work properly.
    • +
    +
  4. +
+ +

Completing the result() function:

+ +
    +
  1. Create a new variable called newStory, and set it's value to equal storyText. This is needed so we can create a new random story each time the button is pressed and the function is run. If we made changes directly to storyText, we'd only be able to generate a new story once.
  2. +
  3. Create three new variables called xItem, yItem, and zItem, and make them equal to the result of calling randomValueFromArray() on your three arrays (the result in each case will be a random item out of each array it is called on). For example you can call the function and get it to return one random string out of insertX by writing randomValueFromArray(insertX).
  4. +
  5. Next we want to replace the three placeholders in the newStory string — :insertx:, :inserty:, and :insertz: — with the strings stored in xItem, yItem, and zItem. There is a particular string method that will help you here — in each case, make the call to the method equal to newStory, so each time it is called, newStory is made equal to itself, but with substitutions made. So each time the button is pressed, these placeholders are each replaced with a random silly string. As a further hint, the method in question only replaces the first instance of the substring it finds, so you might need to make one of the calls twice.
  6. +
  7. Inside the first if block, add another string replacement method call to replace the name 'Bob' found in the newStory string with the name variable. In this block we are saying "If a value has been entered into the customName text input, replace Bob in the story with that custom name."
  8. +
  9. Inside the second if block, we are checking to see if the uk radio button has been selected. If so, we want to convert the weight and temperature values in the story from pounds and Fahrenheit into stones and centigrade. What you need to do is as follows: +
      +
    1. Look up the formulae for converting pounds to stone, and Fahrenheit to centigrade.
    2. +
    3. Inside the line that defines the weight variable, replace 300 with a calculation that converts 300 pounds into stones. Concatenate ' stone' onto the end of the result of the overall Math.round() call.
    4. +
    5. Inside the line that defines the temperature variable, replace 94 with a calculation that converts 94 Fahrenheit into centigrade. Concatenate ' centigrade' onto the end of the result of the overall Math.round() call.
    6. +
    7. Just under the two variable definitions, add two more string replacement lines that replace '94 fahrenheit' with the contents of the temperature variable, and '300 pounds' with the contents of the weight variable.
    8. +
    +
  10. +
  11. Finally, in the second-to-last line of the function, make the textContent property of the story variable (which references the paragraph) equal to newStory.
  12. +
+ +

רמזים וטיפים

+ + + +

Assessment

+ +

If you are following this assessment as part of an organized course, you should be able to give your work to your teacher/mentor for marking. If you are self-learning, then you can get the marking guide fairly easily by asking on the discussion thread for this exercise, or in the #mdn IRC channel on Mozilla IRC. Try the exercise first — there is nothing to be gained by cheating!

+ +

{{PreviousMenu("Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}

+ +

In this module

+ + diff --git a/files/he/learn/javascript/first_steps/strings/index.html b/files/he/learn/javascript/first_steps/strings/index.html new file mode 100644 index 0000000000..8523f144ac --- /dev/null +++ b/files/he/learn/javascript/first_steps/strings/index.html @@ -0,0 +1,172 @@ +--- +title: "התמודדות עם טקסט\_— מחרוזות (Strings) ב-JavaScript" +slug: Learn/JavaScript/First_steps/Strings +translation_of: Learn/JavaScript/First_steps/Strings +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps")}}
+ +

כעת, אנו נשים את הפוקוס על מחרוזות - כך נקראים חלקים של טקסט בתכנות. במאמר זה אנחנו נסתכל על הדברים המשותפים שאנחנו חייבים לדעת על מחרוזות כשלומדים JavaScript, כמו יצירת מחרוזות, חיבור מחרוזות וכד׳.

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה:הכרות עם הבסיס של מחרוזות וטקסט ב-JavaScript.
+ +

כוחן של מילים

+ +

מילים הן אחד הדברים החשובים לבני אדם - זוהי הדרך שמרבית מאיתנו מתקשרים אחד עם השני. מאחר ורשת האינטרנט היא בעיקרה מדיה המבוססת על טקסט המאפשרת לבני אדם להחליף ביניהם מידע, זה מאוד שימוש עבורנו לקבל שליטה על הדרך שבה מילים מוצגות ברשת האינטרנט. {{glossary("HTML")}} מעניק לנו את המבנה ואת המשמעות מאחורי הטקסט שלנו, {{glossary("CSS")}} מאפשר לנו לעצב אותו ואילו JavaScript מכילה מספר אפשרויות וכללים לבצע מניפולציות שונות על מחרוזות, ליצור הודעות/התראות שונות, להראות את הטקסט הנכון כשצריך ועוד.

+ +

כמעט כל התוכניות שעברנו עליהם בקורס עד כה כללו מניפולציה כלשהי על מחרוזות.

+ +

Strings — מחרוזות - הבסיס

+ +

אנו נעבוד עם מחרוזות בדיוק כמו שעבדנו עם מספרים, אך עם מספר הבדלים: נתחיל בכך שנפתח את הקונסולה ונזין מספר שורות קוד:

+ +

יצירת מחרוזת

+ +
    +
  1. על מנת להתחיל, הזן את השורות הבאות בקונסולה: +
    let string = 'The revolution will not be televised.';
    +string;
    + בדיוק כמו שעשינו עם מספרים, אנחנו מכריזים פה על משתנה ומשימים לו ערך של מחרוזת ואז מקבלים את הערך של המשתנה. ההבדל היחיד הוא שכאשר אנחנו כותבים מחרוזת, אנחנו צריכים להשתמש במרכאות כפולות "" או במרכאות בודדות - גרש מכל צד - ''.
  2. +
  3. אם לא נעשה זאת, או שנחסיר אותן בצד ימין או שמאל, אנחנו נקבל שגיאה. נסו להזין את השורות הבאות: +
    let badString = This is a test;
    +let badString = 'This is a test;
    +let badString = This is a test';
    + שורות קוד אלו לא יעבדו מכיוון שכל טקסט שאינו מוקף במלואו במרכאות נחשב כשם של משתנה או שם של מאפיין או מילה שמורה וכד׳. אם הדפדפן לא מוצא את אותה מילה, הוא מחזיר שגיאה. לדוגמא: "missing; before statement". אם הדפדפן יכול לראות מתי מתחילה מחרוזת, אבל לא מתי היא מסתיימת, מכיוון שחסר מרכאות בצד כלשהו, הוא יחזיר שגיאה כגון: "unterminated string literal". אם התוכנית שלנו מעלה שגיאות כאלו, צריך לחזור ולבדוק את כל המחרוזות ולוודא שלא שכחנו מרכאות.
  4. +
  5. הקוד הבא יעבוד אם הגדרנו לפני את המשתנה string: +
    let badString = string;
    +badString;
    + badString כעת הוגדר לקבל את אותו ערך כמו של string.
  6. +
+ +

מרכאות כפולות או בודדות

+ +
    +
  1. ב-JavaScript, אנחנו יכולים לבחור האם להשתמש במרכאות כפולות או במרכאות בודדות לשם עטיפת המחרוזת שלנו. שניהם יעבדו מצויין ובדיוק אותו דבר: +
    let sgl = 'Single quotes.';
    +let dbl = "Double quotes";
    +sgl;
    +dbl;
    +
  2. +
  3. יש מעט מאוד הבדלים ביניהם, וזה תלוי בכם במה להתשמש. אתם צריכים לבחור סוג אחד ואיתו להמשיך. צריך לזכור שלא ניתן להשתמש בשניהם במקביל כך שמצד אחד של מחרוזת יהיו מרכאות כפולות ומצד שני יהיו מרכאות בודדות, שכן זה יחזיר לנו שגיאה: +
    let badQuotes = 'What on earth?";
    +
  4. +
  5. הדפדפן יחשוב שהמחרוזת לא נסגרה מכיוון שכשאנחנו משתמשים בסוג אחד של מרכאות על מנת לעטוף טקסט, אנחנו יכולים להשתמש בסוג השני של המרכאות בתוך הטקסט עצמו. לדוגמא, השורות הקוד הבאות הן תקינות לחלוטין: +
    let sglDbl = 'Would you eat a "fish supper"?';
    +let dblSgl = "I'm feeling blue.";
    +sglDbl;
    +dblSgl;
    +
  6. +
  7. יחד עם זאת, צריך לזכור שאנחנו לא יכולים לכלול בתוך הטקסט את סוג המרכאות שאיתו אנחנו משתמשים על מנת לעטוף את אותו הטקסט. הדוגמא הבאה תציג לנו שגיאה, שכן הדפדפן לא יודע מתי המחרוזת מסתיימת: +
    let bigmouth = 'I've got no right to take my place...';
    + וזה מה שמוביל אותנו לנושא הבא. תווים לבריחה ממחרוזות.
  8. +
+ +

תווים לבריחה ממחרוזת

+ +

על מנת לתקן את הבעיה בקוד הקודם, אנחנו צריכים לברוח מסימן המרכאות אשר עוטף את הטקסט. תווי בריחה שכאלו משמעותם שאנחנו עושים משהו על מנת לוודא שהם ייקראו כטקסט רגיל ולא כחלק מהקוד שלנו. ב-JavaScript, אנחנו עושים את זה באמצעות שימוש בסימן \נסו את זה:

+ +
let bigmouth = 'I\'ve got no right to take my place...';
+bigmouth;
+ +

זה עובד מצוין. אנחנו יכולים גם לברוח מתווים אחרים באותה צורה. לדוגמא: "\,  ויש עוד. ראו Escape notation לפרטים נוספים.

+ +

Concatenating strings - שרשור מחרוזות

+ +
    +
  1. שרשור נשמעת כמו מילה מורכבת שמשמעותה הפשוטה הוא חיבור ביחד. חיבור מחרוזות יחד ב-JavaScript מתבצע באמצעות האופרטור (+), באותה צורה כשהשתמשנו בו לביצוע חיבור בין מספרים, אבל בהקשר שלנו הוא מבצע משהו אחר. נסו את הקוד הבא בקונסולה: +
    let one = 'Hello, ';
    +let two = 'how are you?';
    +let joined = one + two;
    +joined;
    + התוצאה של המשתנה שנקרא joined תכיל את הערך של "Hello, how are you?"
  2. +
  3. במשתנה השלישי, פשוט חיברנו שתי מחרוזות יחד, אך אנחנו יכולים לחבר כמה מחרוזות שנרצה, כל עוד אנחנו כוללים את סימן + בין כל אחת מהמחרוזות. נסו לדוגמא: +
    let multiple = one + one + one + one + two;
    +multiple;
    +
  4. +
  5. אתם גם יכולים לערבב בין משתנים לבין מחרוזות בפועל. נסו את הבא: +
    let response = one + 'I am fine — ' + two;
    +response;
    +
  6. +
+ +
+

לתשומת לב: כשאנחנו מכניסים מחרוזת טקסט ממשית לתוך הקוד שלנו, כשהיא עטופה במרכאות (כפולות או בודדות), זה נקרא string literal.

+
+ +

Concatenation in context - שרשור בתוכן

+ +

בוא נסתכל על שרשור בפועל - הנה דוגמא ממאמרים קודמים בקורס:

+ +
<button>Press me</button>
+ +
const button = document.querySelector('button');
+
+button.onclick = function() {
+  let name = prompt('What is your name?');
+  alert('Hello ' + name + ', nice to see you!');
+}
+ +

{{ EmbedLiveSample('Concatenation_in_context', '100%', 50, "", "", "hide-codepen-jsfiddle") }}

+ +

כאן אנחנו משתמשים ב- {{domxref("window.prompt()", "window.prompt()")}} פונקציה בשורה 4, אשר מבקשת מהמשתמש לענות לשאלה באמצעות חלון קופץ, ולאחר מכן מאחסנת את הטקסט שהמשתמש הכניס לתוך משתנה נתון - במקרה הזה - המשתנה name.

+ +

לאחר מכן אנחנו משתמשים ב-{{domxref("window.alert()", "window.alert()")}} בשורה 5, על מנת להציג למשתמש חלון קופץ נוסף המיכל מחרוזת שהרכבנו משתי מחרוזות בנויות ומהמשתנה name, באמצעות שרשור של המחרוזות.

+ +

מספרים vs. מחרוזות

+ +
    +
  1. מה יקרה כאשר אנחנו ננסה לחבר (לשרשר) בין מחרוזת לבין מספר? נזין את הקוד הבא בקונסולה: +
    'Front ' + 242;
    +
    + היינו מצפים שניסיון כזה יחזיר לנו שגיאה, אך זה עובד בצורה מצויינת. נסיון להציג מחרוזת כמספר לא עושה הגיון, אבל הצגת מספר כמחרוזת כן, ולכן הדפדפן ממיר את המספר למחרוזת ומאחד בין שתי המחרוזות.
  2. +
  3. ניתן אפילו לעשות זאת עם שני מספרים - אנחנו יכולים להכריח מספר להפוך למחרוזת על ידי עטיפה שלו במרכאות. נסו את הקוד הבא. שימו לב שאנחנו משתמשים באופרטור typeof על מנת לבדוק האם הערך של המשתנה הוא מספר או מחרוזת: +
    let myDate = '19' + '67';
    +typeof myDate;
    +
  4. +
  5. אם יש לנו משתנה שיש לו ערך של מספר ואנחנו רוצים להמירו למחרוזת ללא ביצוע שינוי נוסף בו, או יש לנו משתנה שיש לו ערך שהוא מחרוזת ואנחנו רוצים להמירו למספר ולא לעשות בו שינוי נוסף, אנחנו יכולים להשתמש במתודות הבאות: +
      +
    • האובייקט {{jsxref("Number")}} יהפוך כל דבר שמועבר אליו למספר, אם הוא יכול. נסו את הקוד הבא: +
      let myString = '123';
      +let myNum = Number(myString);
      +typeof myNum;
      +
    • +
    • מצד שני, לכל מספר יש מתודה שנקראת ()toString שתמיר כל מספר למחרוזת תואמת. נסו לדוגמא את הקוד הבא: +
      let myNum = 123;
      +let myString = myNum.toString();
      +typeof myString;
      +
    • +
    + אלו יכולים להיות מאוד שימושיים בסיטואציות שונות. לדומא, אם משתמש מכניס מספר לתוך שדה טקסט בטופס, זה תמיד יהיה מחרוזת, גם אם הוא הזין מספרים. לעומת זאת, אם אנחנו רוצים להוסיף את המספר שהמשתמש הזין למשהו, זה חייב להיות מספר, ולכן אנחנו נצטרך להעביר את הקלט הזה דרך ()Number. עשינו זאת בדיוק, בדוגמא שלנו ב-משחק ״נחש את הזיכרון״, בשורה 54.
  6. +
+ +

לסיכום

+ +

זה היה הבסיס של מחרוזת ב-JavaScript. במאמר הבא אנחנו נסתכל על מתודות מובנות הזמינות למחרוזות בשפה זו ונלמד כיצד אנחנו יכולים להשתמש בהם על מנת לבצע מניפולציות למחרוזות שלנו או לשנות את המבנה שלהן.

+ +

{{PreviousMenuNext("Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/useful_string_methods/index.html b/files/he/learn/javascript/first_steps/useful_string_methods/index.html new file mode 100644 index 0000000000..8315262c35 --- /dev/null +++ b/files/he/learn/javascript/first_steps/useful_string_methods/index.html @@ -0,0 +1,673 @@ +--- +title: מתודות שימושיות של מחרוזות +slug: Learn/JavaScript/First_steps/Useful_string_methods +tags: + - JavaScript + - אובייקט + - חילוץ וחיבור מחרוזות + - מחרוזות + - מחרוזות ב-JavaScript + - מציאת האורך של מחרוזת + - מציאת חלק ממחרוזת +translation_of: Learn/JavaScript/First_steps/Useful_string_methods +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}
+ +

לאחר שלמדנו את הבסיס של מחרוזות, נתקדם הלאה ונלמד אילו פעולות אנחנו יכולים לבצע על מחרוזות תוך שימוש במתודות מובנות, כגון מציאת אורך המחרוזת, חיבור ופיצול של מחרוזות, הפרדת תו בודד במחרוזת ועוד.

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה: +

הבנה כי מחרוזות הן בעצם סוג של אובייקט, ולמידה כיצד להשתמש במתודות בסיסיות הזמינות עבור אובייקט מסוג שכזה וכיצד לבצע מניפלוציות במחרוזות.

+
+ +

מחרוזות הן בעצם סוג של אובייקט

+ +

רוב הדברים ב-JavaScript הן בעצם אובייקט. כאשר אנחנו יוצרים מחרוזת כזו לדוגמא:

+ +
let string = 'This is my string';
+ +

המשתנה שלנו בעצם הופך למעין מופע של אובייקט מחרוזת (a string object instance), וכתוצאה מכך לא מעט מתודות ומאפיינים הופכים לזמינים עבור המשתנה. 

+ +

ניתן לראות את רשימת המתודות ומאפיינים אלו בדף בנושא  {{jsxref("String")}} - ראו את הכמות הלא מבוטלת הזו של מתודות ומאפיינים. 

+ +

לפני שאתם נלחצים - אנחנו לא באמת צריכים לזכור את כל המתודות והמאפיינים הללו, לפחות לא בתחילת דרכנו. אך יש כאלו שנרצה לזכור ולתרגל שכן אנחנו נשתמש בהן לעיתים תכופות. 

+ +

נתחיל בתרגול על ידי הכנסת הדוגמאות הבאות לקונסולה:

+ +

מציאת האורך של מחרוזת

+ +

זהו מאפיין המאפשר לנו למצוא את האורך של המחרוזת - אנחנו פשוט משתמשים במאפיין {{jsxref("String.prototype.length", "length")}}. נסו להכניס את הקוד הבא בקונסולה:

+ +
let browserType = 'mozilla';
+browserType.length;
+ +

זה אמור להחזיר לכם את המספר 7, מכיוון שהמחרוזת ״mozilla״ מורכבת מ-7 תווים. זה מאוד שימושי מסיבות רבות: לדוגמא, לפעמים נרצה למצוא את האורך של כל שם ברשימה של שמות ואז להציג אותם לפי האורך שלהם, או לתת למשתמש הודעה ששם המשתמש שהוא הכניס הוא ארוך מדי אם הוא עבר אורך מסויים.

+ +

אחזור של תו ספציפי במחרוזת

+ +

בהקשר זה, אנחנו יכולים לקבל תו ספציפי במחרוזת, על ידי שימוש ב - סוגריים מרובעות בסוף המחרוזת שלנו - כלומר, אנחנו צריכים לכלול ([]) בסוף שם המשתנה שלנו.

+ +

בתוך הסוגריים המרובעות אנחנו צריכים להכניס את מס׳ התו שאנחנו רוצים לקבל בחזרה, כך שאם נרצה את התו הראשון במחרוזת, נזין את הקוד הבא:

+ +
browserType[0];
+ +

שימו לב שמחשבים מתחילים לספור מ-0 ולא מ-1. על מנת לקבל את התו האחרון של כל מחרוזת, אנחנו יכולים להשתמש בקוד הבא המבצע שימוש גם במאפיין length שלמדנו למעלה.

+ +
browserType[browserType.length-1];
+ +

האורך של "mozilla" הוא 7 תווים, אך מכיוון שהמונה (או יותר נכון - אינדקס) מתחיל לספור מ-0, אז המיקום של התו 6 בעצם הוא המיקום של התו האחרון במחרוזת, ולכן אנחנו צריכים את הקוד length-1. אנחנו יכולים להשתמש בזה לדוגמא על מנת למצוא את האות הראשון בסדרה של מחרוזות ולסדר המחרוזות לפי סדר האלפבית.

+ +

מציאת חלק ממחרוזת וחילוצו

+ +
    +
  1. לפעמים נרצה לבדוק האם מחרוזת מסויימת בת מספר תווים נמצאת בתוך מחרוזת גדולה יותר. (באופן כללי אנחנו בודקים האם מחרוזת המשנה קיימת בתוך המחרוזת). ניתן לבצע זאת באמצעות מתודת {{jsxref("String.prototype.indexOf()", "indexOf()")}} אשר מקבלת {{glossary("parameter")}} אחד — את מחרוזת המשנה שאנחנו נרצה לחפש בתוך המחרוזת הגדולה יותר. נסו להזין את הקוד הבא: + +
    browserType.indexOf('zilla');
    + קיבלנו בחזרה את הערך 2 שמציין שמחרוזת-המשנה "zilla" מתחילה במיקום 2 בתוך המחרוזת "mozilla". קוד שכזה יכול לעזור לנו לסנן מחרוזות. לדוגמא, אם יש לנו רשימה של כתובות אינטרנט, ונרצה לסנן רק את אלו המכילים את המחרוזת "zilla".
  2. +
+ +
    +
  1. שימו לב שניתן לבצע סינון זאת גם על דרך השלילה אשר תהיה כמובן יעילה יותר. נסו את הקוד הבא: +
    browserType.indexOf('vanilla');
    + קוד זה יחזיר  לנו את התוצאה 1- אשר משמעותה היא כי מחרוזת המשנה שהזנו למתודה לא קיימת במחרוזת הראשית. כאן לדוגמא, המחרוזת 'vanilla' לא נמצאה במחרוזת הראשית.
    +
    + אנו יכולים לעשות בזה שימוש למצוא את כל המופעים של מחרוזות שכן מכילים את מחרוזת המשנה 'mozilla' או שאינם מכילים, אם אנחנו משתמשים באופרטור שלילה כפי שמצויין למטה. + +
    if(browserType.indexOf('mozilla') !== -1) {
    +  // do stuff with the string
    +}
    +
  2. +
  3. כשאנחנו יודעים היכן מחרוזת המשנה שלנו מתחילה בתוך מחרוזת ראשית, ואנחנו יודעים באיזה תו אנחנו רוצים שמחרוזת המשנה תסתיים, אנחנו יכולים להשתמש ב - {{jsxref("String.prototype.slice()", "slice()")}} נסו להזין את הקוד הבא: +
    browserType.slice(0,3);
    + קוד זה יחזיר לנו כתוצאה "moz" - הארגומנט הראשון שהזנו - 0 - הוא המיקום של התו הראשון במחרוזת המשנה שנרצה לחלץ, והארגומנט השני שהזנו - 3 - יהיה התו שאחרי התו האחרון במחרוזת שנרצה לחלץ. זאת אומרת, החיתוך של המחרוזת מתרחש החל מהתו הראשון ועד, אך לא כולל, התו הרביעי. להזכירכם - מחשבים מתחילים לספור מ-0 ולא מ-1.
    +  
  4. +
  5. בנוסף, אם נרצה לחלץ את כל התווים אחרי תו מסויים, אנחנו יכולים להשמיט את הארגומנט השני. כלומר אנחנו ניתן למתודה רק את מיקום התו שאיתו נתחיל את החילוץ. ראו את הקוד הבא: +
    browserType.slice(2);
    + קוד זה מחזיר את הערך "zilla" - מכיוון שהאות שבמיקום 2 היא האות z ומכיוון שהשמטנו את הארגומנט השני, המתודה חילצה את כל התווים עד סוף המחרוזת browserType.
  6. +
+ +
+

להזכירכם: הפרמטר השני של ()slice הוא אופציונלי: אם נשמיט אותו, החיתוך יסתיים בסוף המחרוזת המקורית. יש גם אופציות נוספות. ראו בדף בנושא {{jsxref("String.prototype.slice()", "slice()")}} ללמוד על הנושא יותר.

+
+ +
+

להזכירכם: מתודות ומאפיינים אלו לא שומרות את התוצאה של המניפולציה או החישוב שהן ביצעו. על מנת לשמור את התוצאה, אנחנו צריכים לשמור אותה במשתנה חדש. כמו כן, שימו לב שמתודות ומאפיינים אלו לא שינו את מחרוזת המקור שעליה ביצענו את החישובים/מניפולציות השונות.

+
+ +

שינוי אות גדולה לאות קטנה, וההיפך

+ +

המתודות של מחרוזת {{jsxref("String.prototype.toLowerCase()", "toLowerCase()")}} ו- {{jsxref("String.prototype.toUpperCase()", "toUpperCase()")}} מאפשרות להמיר מחרוזת בשלמותה לאותיות גדולות או לאותיות קטנות, בהתאמה. זה יכול להיות מאוד שימוש כשנרצה לנרמל את כל הערכים שהמשתמש מכניס לפני שנעביר לשרת. להזכירכם - JavaScript היא case sensitive.

+ +

נסו להכניס את הקוד הבא וראו מה התוצאה:

+ +
let radData = 'My NaMe Is MuD';
+radData.toLowerCase();
+radData.toUpperCase();
+ +

נסו לבדוק מה הערך של המשתנה radData והאם מתודות אלו שמרו בו את התוצאה של הפעלתן.

+ +

עדכון חלקים של מחרוזת

+ +

אנחנו יכולים להחליף מחרוזת אחת במחרוזת אחרת באמצעות המתודה {{jsxref("String.prototype.replace()", "replace()")}}. מתודה זו עובדת בצורה פשוטה וברורה, למרות שניתן לבצע איתה דברים מורכבים יותר, אך לא ניכנס אליהם במדריך זה.

+ +

מתודה זו מקבלת 2 ארגומנטים/פרמטרים - הראשון הוא המחרוזת שאנחנו רוצים להחליף והשני הוא המחרוזת שתיכנס במקום. נסו את הדוגמא הבאה:

+ +
browserType.replace('moz','van');
+ +

שימו לב שגם הפעם הערך שמוחזר לנו הוא המחרוזת החדשה שבוצעה בה ההחלפה, אך הערך הזה לא נשמר במחרוזת שעליה עשינו את ההחלפה. על מנת לקבל את המחרוזת החדשה במשתנה שלנו browserType אנחנו נצטרך לקבוע שהערך שלו יהיה התוצאה של ההפעלה של מתודה זו. כלומר - browserType = browserType.replace('moz','van');

+ +

דוגמאות ללמידה עצמאית

+ +

בחלק זה אנחנו נדריך אתכם לבצע כמה מניפוליות וחישובים על מחרוזות. בכל תרגיל המפורט למטה, יש לנו כמה מערכים של מחרוזות, ולולאה שמחשבת את הערך של כל איבר במערך ומציגה אותו ברשימה. אתם לא צריכים להבין כרגע מערכים או לולאות - אלו יוסברו בהמשך המדריך. אתם צריכים לכתוב בכל תרגיל קוד שיציג את התוצאה של המחרוזות הללו, בהתאם לתרגיל.

+ +

כל דוגמא מגיע עם כפתור "Reset" על מנת לאתחל את הקוד במידה והוא השתבש וכן כפתור "Show solution" על מנת להציג את פתרון אפשרי במקרה ונתקעתם.

+ +

סינון של הודעות

+ +

בתרגיל זה אנחנו מתחיל בתרגיל פשוט - יש לנו מערך של הודעה ברכה, אבל אנחנו רוצים למיין ולסנן רק את אלו שמכילות הודעה ברכה בהקשר של כריסמס. אנחנו רוצים שתכתוב משפט תנאי בתוך הקוד ( ... )if אשר יבדוק כל מחרוזת וידפיס אותה לרשימה רק אם היא מכילה ברכה של כריסמס.

+ +
    +
  1. חשבו תחילה איך אנחנו יכולים לבדוק האם ההודעה בכל פעם היא הודעה הקשורה לכריסמס. איזה מחרוזת מוצגת בכל ההודעות האלו ואיזו מתודה יכולה לבדוק האם המחרוזת נמצאת בתוך ההודעה הזו (אשר גם היא מחרוזת).
  2. +
  3. אנחנו נלמד לכתוב משפט תנאי בתצורת operand1 operator operand2. האם הערך הנמצא מצד שמאל שווה לערך הנמצא מצד ימין? האם תוצאת הפעלת המתודה מצד שמאל, תהא שווה לערך בצד ימין לצד ימין? רמז - במקרה הנוכחי יהיה עדיף לבדוק האם תוצאת הפעלת המתודה בצד שמאל שונה מהערך בצד ימין.
  4. +
+ + + +

{{ EmbedLiveSample('Playable_code', '100%', 590, "", "", "hide-codepen-jsfiddle") }}

+ +

שינוי אותיות גדולות וקטנות

+ +

בתרגיל זה יש לנו שמות של ערים בבריטניה, אך יש ערבוב של אותיות גדולות וקטנות בשמות הערים. אנחנו רוצים שתשנו את המחרוזות כך שכולן יהיו עם אותיות קטנות, למעט האות הראשונה שתהיה אות גדולה. דרך מומלצת לעשות זאת:

+ +
    +
  1. החליפו את כל המחרוזת שנמצאת משתנה input למחרוזת עם אותיות קטנות ואחסנו את התוצאה במשתנה חדש.
  2. +
  3. נסו לקבל את האות הראשונה של המחרוזת המאוחסנת במשתנה החדש שיצרתם ואחסנו את האות הראשונה במשתנה חדש נוסף.
  4. +
  5. השתמשו במשתנה זה כמשתנה המכיל את מחרוזת המשנה והחליפו את האות הראשונה לאות גדולה. אחסנו את התוצאה של ההחלפה במשתנה חדש.
  6. +
  7. שנו את הערך של המשתנה result כך שייה שווה לתוצאה הסופית ולא input.
  8. +
+ +
+

רמז: הפרמטרים של מתודות של מחרוזות לא חייבים להיות מחרוזות מפורשות אלא הם יכולים להיות גם משתנים וגם משתנים שמופעלים עליהן מתודות נוספות.

+
+ + + +

{{ EmbedLiveSample('Playable_code_2', '100%', 550, "", "", "hide-codepen-jsfiddle") }}

+ +

חילוץ וחיבור מחרוזות

+ +

בתרגיל זה, יש לנו מערך שכולל מס׳ מחרוזות המכילות מידע על תחנות רכבת באנגליה. המחרוזות הן פריטי מידע המכילים 3 אותיות עם קוד התחנה, לאחר מכן מידע ממוחשב כלשהו ולאחריו הסימן ; ולאחר מכן שם התחנה. לדוגמא:

+ +
MAN675847583748sjt567654;Manchester Piccadilly
+ +

אנו רוצים שתחלצו את קוד התחנה ואת שם התחנה, ותאחסנו אותה במחרוזת חדשה במבנה הבא:

+ +
MAN: Manchester Piccadilly
+ +

אנו ממליצים לכם לבצע זאת בצורה הבאה:

+ +
    +
  1. חלצו את שלושת האותיות שהן קוד התחנה ואחסנו אותן במשתנה חדש.
  2. +
  3. מצאו את האינדקס (מיקום) של התו ;.
  4. +
  5. חלצו את שם התחנה באמצעות האינדקס של תו ; כנקודת התחלה ואחסנו את התוצאה במשתנה חדש.
  6. +
  7. אחדו בין שני המשתנים ביחד עם מחרוזת רגילה למחרוזת אחת שלמה.
  8. +
  9. שנו את הערך של משתנה  result שיהיה שווה לערך של המחרוזת החדשה ולא של input.
  10. +
+ + + +

{{ EmbedLiveSample('Playable_code_3', '100%', 585, "", "", "hide-codepen-jsfiddle") }}

+ +

לסיכום

+ +

היכולת שלנו לטפל בטקסט בדרכים שונות במהלך כתיבת הקוד שלנו היא חשובה מאוד וחשוב לשלוט בה - במיוחד ב - JavaScript שכן אתרי האינטרנט ויישומי האינטרנט נועדו לתקשורת בין אנשים. מאמר זה נועד להעניק לכם את הבסיס שאתם צריכים לדעת על מניפולציות של מחרוזת לעת עתה ויסייע לכם כשניכנס לדברים קצת יותר מורכבים. המאמר הבא יעסוק בסוג האחרון מבין סוגי המידע שנעבור עליהם כרגע - מערכים.

+ +

{{PreviousMenuNext("Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/variables/index.html b/files/he/learn/javascript/first_steps/variables/index.html new file mode 100644 index 0000000000..3bed3e5bb3 --- /dev/null +++ b/files/he/learn/javascript/first_steps/variables/index.html @@ -0,0 +1,333 @@ +--- +title: אחסון המידע שאנחנו צריכים - משתנים וקבועים +slug: Learn/JavaScript/First_steps/Variables +translation_of: Learn/JavaScript/First_steps/Variables +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps")}}
+ +

לאחר קריאת המאמרים הקודמים, אתם אמורים לדעת כבה מהי JavaScript ומה היא יכולה לעשות עבורנו, כיצד אנו משתמשים ביחד עם טכנולוגיות web אחרות ומה המאפיינים העיקריים שלה ממעוף הציפור. במאמר זה אנחנו ניכנס לעומק של הבסיס של השפה ונסתכל על אחד מאבני הבניין של השפה - משתנים.

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה:לקבל הכרות עם הבסיס של המשתנים.
+ +

כלים נדרשים

+ +

לאורך מאמר זה, תתבקשו להזין מספר שורות קוד בקונסולה על מנת לבדוק את ההבנה שלכם. אם אתם משתמשים בדפדפן במחשב, הדרך הכי טובה לבדוק את הקוד היא באמצעות הקונסולה של JavaScript. לפרטים נוספים אודות הקונסולה ראה What are browser developer tools.

+ +

מהו משתנה?

+ +

משתנה הוא קופסאת אחסון או קונטיינר, לערך מסויים. כמו שאנחנו עשויים להשתמש במספר על מנת לסכום, או במחרוזת על מנת להציג טקסט. משהו מיוחד בנוגע למשתנים הוא שהערכים שלהם יכולים להשתנות. נסתכל על הדוגמא הבאה:

+ +
<button>Press me</button>
+ +
const button = document.querySelector('button');
+
+button.onclick = function() {
+  let name = prompt('What is your name?');
+  alert('Hello ' + name + ', nice to see you!');
+}
+ +

{{ EmbedLiveSample('What_is_a_variable', '100%', 50, "", "", "hide-codepen-jsfiddle") }}

+ +

בדוגמא זו, לחיצה על הכפתור תריץ מספר שורות קוד. הקוד בשורה 4 מקפיץ הודעה על גבי המסך של המשתמש ומבקש ממנו להכניס את שמו ומאחסן את הערך שהוזן במשתנה שעונה לשם name. הקוד בשורה 5 מקפיץ לנו הודעה נוספת עם שמו של המשתמש, שנלקח מהערך של המשתנה name.

+ +

על מנת להבין מדוע זה שימושי כל כך, נסו לחשוב על דרך לרשום את הדוגמא הזו, מבלי להשתמש במשתנה. זה עלול להיראות כמו הקוד הבא:

+ +
let name = prompt('What is your name?');
+
+if (name === 'Adam') {
+  alert('Hello Adam, nice to see you!');
+} else if (name === 'Alan') {
+  alert('Hello Alan, nice to see you!');
+} else if (name === 'Bella') {
+  alert('Hello Bella, nice to see you!');
+} else if (name === 'Bianca') {
+  alert('Hello Bianca, nice to see you!');
+} else if (name === 'Chris') {
+  alert('Hello Chris, nice to see you!');
+}
+
+// ... and so on ...
+ +

אתם אולי לא מבינים עדיין את הסינטקס להצהרה על משתנה, אבל אתם מבינים את הרעיון - אם לא היו לנו משתנים, היינו צריכים לרשום כמויות עצומות של קוד, על מנת לבדוק האם השם אשר הוכנס על ידי המשתמש תואם למה שהגדרנו ואז להציג את ההודעה המתאימה. ברור ששימוש באופציה זו אינו יעיל בשום צורה ופשוט לא יעבוד שכן לא ניתן להכניס את האפשרויות בעולם.

+ +

קיומם של משתנים הוא הגיוני ונדרש, וככל שנלמד יותר לעומק JavaScript אנחנו נתרגל לשימוש בהם באופן טבעי.

+ +

דבר מיוחד נוסף לגבי משתנים הוא העובדה שהם יכולים להכיל כל דבר - לא רק מחרוזת או מספרים. משתנים יכולים להכיל גם מידע מורכב ואפילו פונקציות שלמות. נלמד על כך בהמשך.

+ +
+

לתשומת לב: אנחנו אומרים שמשתנים מכילים ערכים. זוהי אבחנה שחשוב לשים אליה. משתנים אינם ערכים בעצמם; הם רק קופסאות אחסון - קונטיינרים, בעבור ערכים. ניתן לדמיין זאת באמצעות התמונה הבאה:

+
+ +

+ +

Declaring a variable - הצהרה על משתנה

+ +

על מנת לעשות שימוש במשתנה, אנחנו קודם כל צריכים ליצור אותו, או יותר מדוייק, אנחנו צריכים להצהיר עליו. על מנת להצהיר על משתנה אנחנו צריכים להשתמש במילה השמורה var או במילה השמורה let ולאחריהן השם שנרצה לתת למשתנה:

+ +
let myName;
+let myAge;
+ +

כאן הצהרנו על שני משתנים שנקראים myName ו- myAge. נסו להזין את השורות האלו בקונסולה. לאחר מכן, נסו ליצור משתנה אחד או שניים בעצמכם.

+ +
+

לתשומת לב: ב- JavaScript, כל הוראת קוד (לא כל שורת קוד) צריכה להסתיים עם הסימן ; - אם לא נכניס סימן זה, הקוד רוב הסיכויים יעבוד, אבל אם נכניס מספר שורות קוד ביחד, כנראה שלא. אנא התרגלו להוסיף ; בעת שאתם כותבים את הקוד שלכם.

+
+ +

אתם יכולים לבדוק האם הערכים האלו קיימים בסביבת ההרצה על ידי הדפסה של שמות המשתנים לקונסולה כפי שמתואר להלן:

+ +
myName;
+myAge;
+ +

כרגע למשתנים אלו אין כל ערך. אנחנו רק הצהרנו עליהם ובכך הם נוצרו, אך הם כרגע מעין ״קופסאות ריקות״. כשאנחנו מזינים את שמות המשתנים בקונסולה, קיבלנו בקונסולה undefined. אם לא היינו מצהירים עליהם, היינו מקבלים הודעת שגיאה אחרת. נסו להבין מה ההבדל ביניהם.

+ +
scoobyDoo;
+ +
+

לתשומת לב: אל תבלבלו בין משתנה שקיים אך לא מכיל ערך לבין משתנה שלא קיים כלל. אם נחשוב עליהם כקופסאות אחסון, משתנה שהוצהר אך לא נתנו לו ערך יצר לנו קופסא לאחסון, אך לא אחסנו בו כלום. כלומר הקופסא קיימת אך ריקה. במקרה כזה נקבל את הערך undefined.

+ +

לעומת זאת, משתנה שלא הוצהר, זה כאילו לא יצרנו את קופסת האחסון מלכתחילה ולכן הודעת השגיאה שנקבל היא שהדפדפן לא מוצא את המשתנה, כלומר, הוא לא מוצא את קופסאת האחסון (כי לא יצרנו אותה).

+
+ +

השמת ערך למשתנה

+ +

ברגע שהצהרנו על משתנה, אנחנו יכולים לאתחל אותו עם ערך שנשים לו. אנחנו עושים זאת על ידי סימן ה-״שווה״ - =, ולאחריו הערך שאנחנו נרצה לשים במשתנה. לדוגמא:

+ +
myName = 'Chris';
+myAge = 37;
+ +

נסו להזין בקונסולה את הקוד הבא. אתם אמורים להראות שהערכים שהשמנו למשתנים מוחזרים לנו בקונסולה. שוב, אנחנו יכולים לקבל את הערך של משתנה על ידי הזנת שמו לקונסולה. ננסה זאת שוב:

+ +
myName;
+myAge;
+ +

ניתן להצהיר על משתנה וכן להשים לו ערך באותו זמן:

+ +
let myDog = 'Rover';
+ +

זו ככל הנראה תהיה הדרך שבה נרשום משתנים - הצהרה והשמה באותה שורה שכן זוהי דרך מהירה יותר מאשר לרשום הצהרה בשורה אחת והשמה של ערך בשורה אחרת.

+ +

ההבדלים בין var לבין let

+ +

בשלב זה אתם בטח שואלים ״מדוע צריכים שתי מילים שמורות על מנת להגדיר משתנים?״ או ״למה יש לנו גם את let וגם את var

+ +

הסיבות הן בעיקר היסטוריות. כאשר JavaScript רק נוצרה, היה קיים רק var. זה עבד במרבית המקרים, אבל היו לו סוגיות שונות בצורה ובדרך שבה הוא עבד. ההתנהגות שלו הייתה יכולה להיות מבלבלת וקצת מעצבנת. לכן, let נוצר בגרסאות מודרניות יותר של JavaScript, והציג מילה שמורה חדשה ליצירת משתנים, אשר פועלת בצורה אחרת מהמילה השמורה var ומתקנת את הסוגיות שהיו עם דרך ההגדרה הקודמת. סתם לידיעה - דרך זו של שימוש במילה השמורה let החלה בגרסת העדכון החשובה של JavaScript שנקראת ES2015 (ES6).

+ +

אנו נגע במספר הבדלים פשוטים ביניהם, אך לא ניגע בכולם כרגע. אנו נבין את ההבדלים ביניהם בהמשך ככל שניכנס יותר לעומק של השפה. אם תרצו להרחיב בנושא - היכנסו לקישור let reference page.

+ +

נתחיל בכך שאם נרשום מספר שורות קוד שבהן נצהיר על משתנה ונשים בו ערך. אנחנו יכולים להצהיר על משתנה באמצעות שימוש במילה var גם לאחר שהשמנו בו ערך והתוכנית עדיין תעבוד. כלומר, המשתנה יהיה מוכר לדפדפן. לדוגמא:

+ +
myName = 'Chris';
+
+function logName() {
+  console.log(myName);
+}
+
+logName();
+
+var myName;
+ +
+

לתשומת לב: קוד זה לא יעבוד כשננסה להכניס אותו שורה אחר שורה בקונסולה אלא רק כשנכניס את כל שורות קוד באמצעות JavaScript.

+
+ +

זה עובד בגלל מנגנון שנקרא Hoisting. אם תרצו להרחיב על כך, ראו קישור לפרטים נוספים על הנושא. בקצרה המשמעות שלו היא שב-JavaScript הצהרות על משתנים (והצהרות בכללי) מבוצעות לפני שיתר הקוד רץ, כלומר, הצהרה על משתנה במקום מסויים בקוד תהיה זהה להצהרה שלו בתחילת הקוד. יחד עם זאת, חשוב לשים לב שמנגנון זה משפיע רק על הצהרה של משתנים ולא על השמה של ערכים אליהם. הערכים שלהם יושמו אליהם בהתאם לסדר ריצת הקוד. 

+ +

Hoisting לא מתבצע יותר עם המילה השמורה let. אם נשנה את var ל- let בדוגמא למעלה, זה יציג לנו שגיאה. זה דבר טוב כמובן - הצהרה על משתנה לאחר שאנחנו משימים לו ערך גורמת לבלבול ולקושי בקריאה והבנה של הקוד.

+ +

בנוסף, כשאנחנו משתמשים ב- var, אנחנו יכולים להצהיר על המשתנה שיצרנו באמצעותו כמה פעמים שנרצה ואילו בעת שימוש ב- let אנחנו לא יכולים. הקוד הבא לדוגמא, יעבוד:

+ +
var myName = 'Chris';
+var myName = 'Bob';
+ +

אבל הקוד הבא יקפיץ לנו שגיאה בשורה השנייה:

+ +
let myName = 'Chris';
+let myName = 'Bob';
+ +

ולכן נצטרך לרשום אותו בצורה הבאה:

+ +
let myName = 'Chris';
+myName = 'Bob';
+ +

שוב, מדובר בכלל חשוב אשר נועד למנוע הצהרה חוזרת של משתנה. אנו ממליצים לבצע שימוש כמה שיותר ב- let מאשר ב-var . אין שום סיבה שנרצה להשתמש ב- var, אלא אם אנחנו צריכים לתמוך בגרסאות ישנות של אינטרנט אקספלורר (גרסאות ישנות אלו לא תומכות ב- let אלא רק החל מגרסה 11; הדפדפן אדג׳ לעומת זאת תומך במלואו ב-let).

+ +
+

לתשומת לב: אנחנו בשלבים של עדכון הדוגמאות בקורס זה לשימוש ב- let במקום var.

+
+ +

עדכון משתנה

+ +

ברגע שמשתנה הוצהר והושם לו ערך, אנחנו יכולים לשנות (או לעדכן) את הערך שנמצא באותו משתנה, פשוט על ידי השמה של ערך אחר. נסו להזין את הקוד הבא בקונסולה:

+ +
myName = 'Bob';
+myAge = 40;
+ +

מס׳ כללים בנוגע לשמות משתנים

+ +

אנחנו יכולים לקרוא למשתנים שלנו כמעט בכל שם שנרצה, אבל ישנן מס׳ הגבלות. באופן כללי, היצמדו לשימוש באותיות לועזיות (0-9, a-z, A-Z) ולתו מסוג קו תחתון.

+ + + +
+

לתשומת לב: ניתן למצוא את רשימת המילים השמורות של השפה ב- Lexical grammar — keywords.

+
+ +

דוגמא למתן שמות למשתני בצורה נכונה:

+ +
age
+myAge
+init
+initialColor
+finalOutputValue
+audio1
+audio2
+ +

דוגמא למתן שמות למשתני בצורה לא נכונה:

+ +
1
+a
+_12
+myage
+MYAGE
+var
+Document
+skjfndskjfnbdskjfb
+thisisareallylongstupidvariablenameman
+ +

מילים שמורות:

+ +
var
+Document
+
+ +

נסו ליצור מספר משתנים חדשים בהתאם לכללים שציינו למעלה.

+ +

סוגי משתנים

+ +

ישנם מס׳ סוגים של מידע שנוכל לאחסן במשתנים. בחלק הזה אנחנו נתאר אותם בקצרה. במאמרים הבאים אנו נלמד אותם לעומק.

+ +

Numbers - מספרים

+ +

אנחנו יכולים לאחסן מספרים כמשתנים, בין אם הם מספר שלם, כמו 30, או בין אם הם מספר עשרוני, כמו 2.456. ב- JavaScript אנחנו לא צריכים להצהיר על סוג המשתנה, בשונה מכמה שפות תכנות אחרות. כשאנחנו נותנים למשתנה ערך מסוג מספר, אנחנו לא משימים את הערך בין גרשיים. לדוגמא:

+ +
let myAge = 17;
+ +

Strings - מחרוזות

+ +

מחרוזות אלו חלקים של טקסט. כאשר אנחנו נותנים למשתנה ערך של מחרוזת, אנחנו צריכים לעטוף את המחרוזת בגרשיים כפולות " " או בגרש בודד ' ' אחרת JavaScript עלולה לנסות לפרש את הקוד כשם נוסף למשתנה. לדוגמא:

+ +
let dolphinGoodbye = 'So long and thanks for all the fish';
+ +

Booleans - בוליאנים

+ +

בוליאנים אלו ערכי אמת או שקר. משתנים שמכילים ערך בוליאני יכולים לקבל 2 ערכים: או ערך אמת true או ערך שקר false. לדוגמא:

+ +
let iAmAlive = true;
+ +

בפועל, אנחנו נשתמש במשתנה מסוג בוליאני בדרך הבאה:

+ +
let test = 6 < 3;
+ +

כאן ביצענו שימוש באופרטור ״קטן מ-״ (<) על מנת לבדוק האם 6 קטן מ-3. כפי שניתן לצפות, זה יחזיר לנו false מכיוון ש-6 לא קטן מ-3. נלמד יותר לעומק על אופרטורים אלו בהמשך הקורס.

+ +

Arrays - מערכים

+ +

מערך הוא בעצם אובייקט יחיד, המכיל מספר ערכים המתוחמים בתוך סוגריים מרובעות. נסו להכניס את הקוד הבא לתוך הקונסולה:

+ +
let myNameArray = ['Chris', 'Bob', 'Jim'];
+let myNumberArray = [10, 15, 40];
+ +

ברגע שהמערכים האלו מוצהרים, אנחנו יכולים לגשת לכל איבר במערך באמצעות ציון המיקום שלו במערך. נסו את הקוד הבא:

+ +
myNameArray[0]; // should return 'Chris'
+myNumberArray[2]; // should return 40
+ +

הסוגרים המרובעות מצביעות על האינדקס המתאים למיקום של האיבר שאנחנו רוצים שיוחזר. שימו לב שהמיקומים במערך מתחילים מ-0. כלומר, האיבר הראשון במערך הוא באינדקס 0.

+ +

נרחיב על מערכים במאמר בהמשך הקורס.

+ +

Objects - אובייקטים

+ +

בתכנות, אובייקט הוא מבנה של קוד שמדמה אובייקט בעולם האמיתי. הוא מייצג מעין קופסא המכילה מידע על הגובה שלו, הרוחב שלו, האורך שלו או מאפיינים נוספים. אנחנו יכולים לדוגמא לייצר אובייקט שידמה אדם, ואובייקט זה יכיל מידע על שמו, גובה, משקל, שפה מדוברת וכד׳.

+ +

הכניסו את הקוד הבא בקונסולה:

+ +
let dog = { name : 'Spot', breed : 'Dalmatian' };
+ +

על מנת לאחזר מידע המאוחסן באובייקט, השתמשו בקוד הבא:

+ +
dog.name
+ +

אנו לא נעמיק בנושא אובייקטים כעת - אנו נלמד לעומק את הנושא במאמר בהמשך.

+ +

השמת משתנים דינאמית

+ +

JavaScript היא שפה דינאמית. שלא כמו שפות אחרת, ב- JavaScript אנחנו לא צריכים לציין איזה סוג של מידע יאוסן בתוך המשתנה (מספרים, מחרוזות, מערכים וכד׳).

+ +

לדוגמא, אם אנחנו מצהירים על משתנה ונותנים לו ערך שעטוף בגרשיים, הדפדפן יתייחס אליו כמחרוזת:

+ +
let myString = 'Hello';
+ +

אם נכניס מספרים בתוך הגרשיים, הוא עדיין יהיה מחרוזת!

+ +
let myNumber = '500'; // oops, this is still a string
+typeof myNumber;
+myNumber = 500; // much better — now this is a number
+typeof myNumber;
+ +

נסו להכניס את הקוד שלמעלה לקונסולה שורה אחר שורה, וראו התוצאות. אתם תשימו לב שאתם משתמשים באופטור מיוחד שנקרא typeof - אופרטור זה מחזיר לנו את סוג הערך המאוחסן במשתנה שהעברנו לו. הפעם הראשונה שהרצנו את האופרטור הזה, הוא החזיר string, שכן באותה נקודה, המשתנה myNumber מכיל מחרוזת, '500'. בדקו מה הקונסולה תחזיר כשנזין את שתי השורות האחרונות.

+ +

Constants - קבועים

+ +

להרבה שפות תכנות יש את הקונספט של constant או קבוע — משתנה שברגע שהכנסו לו ערך, אותו ערך לא משתנה. ישנן הרבה סיבות מדוע נרצה להשתמש בהם, מסיבות אבטחה - (אם סקריפט של אתר אחר מנסה לשנות את הערכים זה עלול ליצור בעיות) עד לפתרון שגיאות של הקוד והבנה שלו (קשה יותר לשנות בטעות ערכים שלא אמורים להשתנות).

+ +

בשלביה המוקדים של השפה, קבועים לא היו קיימים. כעת יש בשפה קבועים ואנו משתמשים במילה השמורה const, על מנת להצהיר עליהם ולהשים בערך ערך שלא ישתנה:

+ +
const daysInWeek = 7;
+const hoursInDay = 24;
+ +

const עובד בדיוק כמו let, למעט העובדה שאנחנו לא יכולים לתת ל- const ערך חדש. בדוגמא הבאה, השורה השנייה תציג לנו שגיאה בקונסולה:

+ +
const daysInWeek = 7;
+daysInWeek = 8;
+ +

לסיכום

+ +

במאמר זה למדנו אודות משתנים - מהם, כיצד ליצור אותם ומה ההבדל בינם לקבועים. במאמר הבא אנחנו נלמד אודות מתמתיקה בסיסית ב-JavaScript.

+ +

{{PreviousMenuNext("Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps/Maths", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/what_is_javascript/index.html b/files/he/learn/javascript/first_steps/what_is_javascript/index.html new file mode 100644 index 0000000000..4ad34985e7 --- /dev/null +++ b/files/he/learn/javascript/first_steps/what_is_javascript/index.html @@ -0,0 +1,447 @@ +--- +title: מה זה JavaScript? +slug: Learn/JavaScript/First_steps/What_is_JavaScript +tags: + - API + - JavaScript + - דרכים להוסיף JavaScript לדף + - הסבר על JavaScript + - מדריך + - מה זה JavaScript? + - מתחילים +translation_of: Learn/JavaScript/First_steps/What_is_JavaScript +--- +
{{LearnSidebar}} 
+ +
{{NextMenu("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps")}}
+ +

ברוך הבא לקורס JavaScript למתחילים של MDN. במאמר זה נסתכל על JavaScript ממעוף הציפור, ונענה על שאלות כגון ״מה זה JavaScript?״ ״מה ניתן לבצע איתה?״ ונוודא שאתם מרגישים בנוח עם מטרתה. 

+ + + + + + + + + + + + +
Prerequisites:ידע בסיסי בשימוש במחשב וכן הבנה בסיסית של HTML ו-CSS.
Objective:להכיר מעט את JavaScript, מה היא יכולה לעשות, ואיך היא קשורה לאתר אינטרנט.
+ +

הגדרה ממבט על

+ +

JavaScript היא שפת תכנות (שפת סקריפט) אשר מאפשרת לכם ליישם דברים מורכבים על גבי דפי האינטרנט - בכל פעם שדף אינטרנט מבצע משהו דינאמי כגון עדכון תכנים באופן עיתי, הצגת מפה אינטרקאטיבית, אנימציות שונות דו ותלת מימדיות וכד׳ (בניגוד לדף אינטרנט סטטי שרק מציג תוכן ללא שינוי כלשהו בדף), ניתן להיות בטוח ש JavaScript לוקחת בכך חלק. JavaScript היא החלק השלישי ב-״עוגת״ טכנולוגיות ה-web כאשר את שתי הטכנולוגיות האחרות למדנו בחלקים אחרים של איזור הלמידה (HTML and CSS).   

+ +

+ + + +

שלושת השפות בנויות כשכבות אחת על גבי השנייה, כפי שניתן לראות באיור למעלה. ננסה להבין זאת באמצעות טקסט פשוט. נסמן את הטקסט באמצעות HTML, על מנת לתת לו מבנה ומטרה. בדוגמא הנוכחית למטה, סימנו את הטקסט באלמנט מסוג פסקה <p>.

+ +
<p>Player 1: Chris</p>
+ +

+ +

לאחר מכן, נוסיף כמה כללי CSS  על מנת לעצב את האלמנט בצורה מסויימת:

+ +
p {
+  font-family: 'helvetica neue', helvetica, sans-serif;
+  letter-spacing: 1px;
+  text-transform: uppercase;
+  text-align: center;
+  border: 2px solid rgba(0,0,200,0.6);
+  background: rgba(0,0,200,0.3);
+  color: rgba(0,0,200,0.6);
+  box-shadow: 1px 1px 2px rgba(0,0,200,0.4);
+  border-radius: 10px;
+  padding: 3px 10px;
+  display: inline-block;
+  cursor: pointer;
+}
+ +

+ +

לבסוף, נוסיף קוד JavaScript על מנת להעניק דינאמיות והתנהגות מסוימת:

+ +
const para = document.querySelector('p');
+
+para.addEventListener('click', updateName);
+
+function updateName() {
+  let name = prompt('Enter a new name');
+  para.textContent = 'Player 1: ' + name;
+}
+
+ +

{{ EmbedLiveSample('A_high-level_definition', '100%', 80, "", "", "hide-codepen-jsfiddle") }}

+ +

נסו ללחוץ על הגרסה האחרונה של הטקסט וראו מה קורה.אתם יכולים למצוא את קוד המקור כאן, או דף אינטרנט.

+ +

JavaScript יכולה לבצע הרבה יותר - נעמיק בכך בהמשך הלמידה.

+ +

אז מה ניתן לבצע עם JavaScript?

+ +

הבסיס של JavaScipt מכיל כמה ממאפייני התכנות הנפוצים, אשר מאפשרים לנו לבצע פעולות כגון: 

+ + + +

דבר מעניין נוסף הוא הפונקציונליות אשר בנויה על גבי הבסיס של שפת JavaScript. אותם API (Application Programming Interfaces ) מעניקים לנו אפשרויות משמעותיות אשר נוכל להשתמש בהם כאשר אנו כותבים את קוד ה-JavaScript שלנו. API בתרגום לעברית זה ״ממשק תכנות יישומים״.  

+ +

API אלו חלקי קוד מוכנים אשר מאפשרים לנו כמפתחים להטמיע תוכניות אשר בצורה אחרת היה קשה מאוד להטמיע או אף בלתי אפשרי. הם מאפשרים לנו את הנוחות בתכנות, כפי שרהיטים מוכנים להרכבה (לדוגמא: איקאה) מאפשרים את הנוחות (המסויימת) בעיצוב דירה. הרבה יותר נוח לקחת סט מוכן להרכבה של מדף המכיל פלטת עץ מוכנה, עם ברגים ומעמד על מנת ליצור מדף קיר, מאשר להתחיל מהבסיס: למצוא את העץ המתאים, לחתוך אותו לגודל המתאים, למצוא את הברגים המתאימים ועוד. 

+ +

באופן כללי, API מחולקים לשתי קטגוריות:

+ +

+ +

API של הדפדפן (Browser APIs) - אלו API מובנים בתוך דפדפן האינטרנט, ומאפשרים לקבל מידע/נתונים מסביבת העבודה של המשתמש או לבצע דברים שימושים מורכבים. לדוגמא: 

+ + + +
+

לתשומת לב: הרבה מהדוגמאות אשר הובאו למעלה, לא יעבדו בדפדפנים ישנים - במהלך הניסוי והטעיה במהלך הקורס, אנו נצטרך לעשות שימוש בדפדפנים מתקדמים על מנת לעשות שימוש בדוגמאות אלו. דפדפנים אלו יכולים להיות כרום, פיירפוקס, אדג׳, אופרה.

+ +

כמו כן, ככל ונתקדם בשלבי הפיתוח של הקוד, נצטרך לקחת בחשבון בדיקת תאימות לדפדפנים השונים יותר לעומק, על מנת שנוכל לאפשר ללקוח להשתמש בכל הפונקציונליות שהגדרנו. 

+
+ +

API של צד שלישי לא בנויים בתוך הדפדפן כברירת מחדל, ובעיקרון אנחנו נצטרך לקבל את הקוד והמידע שלהם ממקום אחר באינטרט. לדוגמא:

+ + + +
+

לתשומת לב: APIs אלו הם נושאים מתקדמים יחסית, ואנחנו לא נגע בהם במודול זה. ניתן למצוא מידע נוסף ב- Client-side web APIs module.

+
+ +

כמובן שיש עוד המון אפשרויות!, יחד עם זאת, כנראה שלאחר למידה כה קצרה של JavaScript, עדיין לא ניצור את פייסבוק או גוגל הבאים, אך ככל שנתקדם במדריך ונבין לעומק הבסיס, נתקדם בדרך הנכונה. 

+ +

מה JavaScript עושה על גבי דף האינטרנט?

+ +

כאן אנו נתחיל להסתכל על קוד, ובזמן שנעשה זאת, ננסה להבין מה קורה בפועל, כאשר JavaScript רצה בדף האינטרנט.

+ +

בוא נחזור בקצרה על מה בפועל קורה כשאנו מעלים דף אינטרנט בדפדפן. (דיברנו על כך במאמר כיצד CSS עובד).  

+ +

כאשר אנו מעלים דף אינטרנט בדפדפן, אנו מריצים את הקוד (HTML, CSS ו-JavaScript) בתוך סביבת הריצה (לשונית הדפדפן). ניתן לדמות את זה למפעל אשר מקבל אליו את חומרי הגלם (הקוד) ומייצר מכך מוצר מסויים (דף האינטרנט).

+ +

+ +

JavaScript מורצת על ידי מנוע JavaScript של הדפדפן עצמו, לאחר שה-HTML וה-CSS הורכבו והושמו בדף האינטרנט שלנו. דבר זה מאפשר לנו להבטיח שהמבנה והעיצוב של דף האינטרנט הושלם ונמצא במקומו בזמן ש-JavaScript תחל לרוץ. 

+ +

מכיוון ששימוש נפוץ ועיקרי ב-JavaScript הוא האפשרות להעניק דינאמיות ולשנות HTML ו-CSS באמצעות Document Object Model API, מדובר בדבר הכרחי על מנת לאפשר זאת, שכן אם JavaScript תרוץ לפני שרכיבי ה-HTML וכללי העיצוב של CSS נטענו במלואם, עלולים להתרחש שגיאות באותה דינאמיות שביקשנו להעניק באמצעות JavaScript.

+ +

אבטחה בדפדפן

+ +

לכל לשונית בדפדפן יש מעין ״סל״ או מתחם נפרד להרצת הקוד - בשפה מקצועית סלים או מתחמים אלו נקראים ״סביבות הרצה״ - זאת אומרת, שברוב המקרים, קוד בכל לשונית ירוץ באופן נפרד לחלוטין מלשוניות אחרות בדפדפן, ולא יוכל לגשת או להשפיע על הקוד בלשונית אחרת. דבר זה הינו דבר חשוב בהיבטי אבטחה - שכן אחרת, זה היה פותח אפשרות נוספות להאקרים לפרוץ לאתרים או לגנוב מידע. 

+ +
+

לתשומת לב: יש דרכים להעביר קוד ומידע בין אתרים/לשוניות שונות בצורה בטוחה, אך נושאים אלו הינם מתקדמים ולא נדון בהם בקורס זה.

+
+ +

סדר הריצה של JavaScript 

+ +

כאשר הדפדפן נתקל בבלוק של קוד JavaScript, באופן כללי הוא יריץ את הקוד מלמעלה למטה. המשמעות היא שצריך לשים לב לסדר שבו אנו רושמים את הקוד. לדוגמא, בוא נחזור לבלוק הקוד שראינו בדוגמא הקודמת:

+ +
const para = document.querySelector('p');
+
+para.addEventListener('click', updateName);
+
+function updateName() {
+  let name = prompt('Enter a new name');
+  para.textContent = 'Player 1: ' + name;
+}
+ +

שורה 1: בקוד זה אנחנו בעצם מגדירים משתנה מסוג const (נרחיב עליו בהמשך) וקוראים לו בשם para, ובאמצעות ה-DOM אנחנו בוחרים את האלמנט HTML הראשון מסוג p.

+ +

שורה 2: אנו מצמידים למשתנה שהגדרנו מתודה בשם event listener (נרחיב על כך בהמשך) שבעצם ״תקשיב״ כאשר יתרחש אירוע מסוג לחיצה על ה-para, כלומר, על האלמנט p, קוד בלוק שנקרא updateName ירוץ. בלוק קוד זה הניתן לשימוש חוזר נקרא ״פונקציה״ והוא מוגדר בשורות 5-8. 

+ +

שורות 5-8: הגדרה של הפונקציהupdateName. פונקציה זו מבקשת מהמשתמש להכניס את שמו ומשנה את התוכן של para לשמו של המשתמש ביחד עם המחרוזת ״:Player 1״. 

+ +

אם נשנה את הסדר של שורות הקוד, לדוגמא, נחליף בין שורות 1 ו-3, אנו נקבל שגיאה בקונסולה של הדפדפן מסוג:  TypeError: para is undefined. זה אומר שכאשר אנו מבצעים להוסיף את  event listener- האובייקט para עדין לא קיים ולכן לא נוכל להוסיף לו event listener.

+ +
+

לתשומת לב: זוהי שגיאה מאוד נפוצה - חשוב לשים לב שהאובייקטים שאנו מנסים לגשת אליהם קיימים בטרם אנו מנסים לעשות איתם פעולות כלשהן. 

+
+ +

קוד מהודר מול קוד מפורש

+ +

יכול להיות שנתקלתם במושגים כגון שפה מפורשת או שפה מקומפלת/מהודרת בהקשר של תכנות ופיתוח תוכנה.

+ +

בשפות שהינן שפות ״מפורשות״, הקוד רץ מלמעלה למטה, והתוצאה של הרצת הקוד תוחזר במיידי, אנו לא צריכים לעשות טרנספורמציה כלשהי לקוד או שינוי שלו לתבנית אחרת לפני שהדפדפן יריץ אותו. 

+ +

לעומת זאת, שפות ״מקומפלות״ או שפות ״מהודרות״ נדרשות לבצע שינוי כלשהו לתבנית אחרת לפני שהן יוכלו לרוץ על ידי המחשב. לדוגמא,  C/C++ מקומפלות לשפת assembly לפני הרצה על ידי המחשב. 

+ +

JavaScript היא שפה קלת-משקל ומעין מפורשת (בפועל, כן מבוצע קימפול, אך לא נרחיב על הנושא). לשני סוגי השפות יש יתרונות, אך לא נדון בהם כרגע. 

+ +

קוד צד-לקוח מול קוד צד-שרת

+ +

ככל הנראה נתקלת במושגים קוד צד-לקוח (client-side) וקוד צד-שרת (server-side), במיוחד בהקשר של פיתוח Web. קוד צד-לקוח הוא קוד שרץ על גבי המחשב של המשתמש - כאשר צופים בדף אינטרנט, קוד צד-לקוח של דף האינטרנט יורד, מורץ ומוצג על ידי הדפדפן. במודול זה אנו נגע בעיקר ב- JavaScript של צד-לקוח.

+ +

קוד צד-שרת מצד שני, ​​רץ על גבי השרת, והתוצאה שלו מורדת ומוצגת בדפדפן. דוגמאות לשפות פיתוח קוד צד-שרת כוללות בין היתר את PHP, Python, Ruby, ASP.NET וגם...JavaScript. כן, JavaScript יכולה לשמש גם כשפת פיתוח עבור צד-שרת. לדוגמא, בסביבת הפיתוח הפופולרית Node.js. ניתן להרחיב את הלמידה על JavaScript כשפת קוד-שרת בנושא הייעודי שלנו Dynamic Websites – Server-side programming

+ +

קוד דינאמי מול קוד סטאטי

+ +

המושג דינאמיות (dynamic) משמש הן לתיאור קוד צד-לקוח והן לתיאור קוד צד-שרת והוא מתייחס לאפשרות לעדכן את התצוגה של דף אינטרנט או יישום אינטרנט כך שיוצגו דברים אחרים בנסיבות אחרות ויווצר תוכן חדש ככל שנדרש. 

+ +

קוד צד-שרת יוצר באופן דינאמי תוכן חדש על גבי השרת, לדוגמא, מייצא מידע מתוך מאגר הנתונים, ואילו קוד צד-לקוח מייצר תוכן חדש בתוך הדפדפן של המשתמש, לדוגמא, יוצר טבלת HTML חדשה וממלא אותה בנתונים שביקשנו מהשרת, ואז מציג למשתמש את הטבלה על גבי דף האינטרנט.

+ +

המשמעות של דינאמיות היא מעט שונה בין צד לקוח לצד שרת, אך הן קשורות אחת לשנייה, ובדרך כלל, הן עובדות יחד. 

+ +

דף אינטרנט שאין לו שום דינאמיות או עדכוני תוכן מכונה כדף סטאטי - הוא מציג למשתמש את אותו תוכן כל הזמן.  

+ +

כיצד נוסיף JavaScript לדף האינטרנט שלנו?

+ +

JavaScript מוחלת על דף ה-HTML בדרך דומה לדרך שבה אנו מחילים את כללי ה-CSS.

+ +

בעוד ש-CSS משתמש באלמנט {{htmlelement("link")}} על מנת להחיל גיליונות עיצוב חיצוניים ובאלמנט {{htmlelement("style")}} על מנת להחיל גיליונות עיצוב פנימיים, JavaScript צריכה אלמנט אחד - {{htmlelement("script")}}. נבין לעומק כיצד זה עובד: 

+ +

Internal JavaScript (כאלמנט בתוך קובץ ה-HTML)

+ +
    +
  1. אנא צרו העתק על גבי המחשב שלך של קובץ הדוגמא שלנו:  apply-javascript.html.
  2. +
  3. פתחו את הקובץ בדפדפן ובעורך הקוד. אתם תראו ש-HTML יצר דף אינטרנט פשוט המכיל כפתור שניתן ללחוץ עליו.
  4. +
  5. בעורך הקוד - הוסיפו את הקוד הבא בתוך ה-head, לפני התגית </head>:
  6. +
  7. +
    <script>
    +
    +  // JavaScript goes here
    +
    +</script>
    +
  8. +
  9. כעת, נוסיף קצת קוד JavaScript בתוך האלמנט {{htmlelement("script")}} על מנת שהדף יבצע משהו מעניין. לשם כך, הוסיפו את הקוד הבא מתחת לכיתוב:  " :JavaScript goes here line //״
  10. +
  11. +
    document.addEventListener("DOMContentLoaded", function() {
    +  function createParagraph() {
    +    let para = document.createElement('p');
    +    para.textContent = 'You clicked the button!';
    +    document.body.appendChild(para);
    +  }
    +
    +  const buttons = document.querySelectorAll('button');
    +
    +  for(let i = 0; i < buttons.length ; i++) {
    +    buttons[i].addEventListener('click', createParagraph);
    +  }
    +});
    +
  12. +
  13. שמרו את הקובץ ורעננו את הדפדפן - כעת אתם אמורים להבחין שכאשר אתם לוחצים על הכפתור, נוצרת כל פעם פסקה חדשה מתחת לפסקה הקודמת. 
  14. +
+ +
+

לתשומת לב: אם הדוגמא לעיל לא עובדת לכם, בדקו האם שמרתם את הקובץ בשם הנכון, האם יש לו סיומת html? האם הוספתם את האלמנט  {{htmlelement("script")}}  ישר לפני תגית הסגירה </head> ? להזכירכם, JavaScript היא case sensitive, וזה יכול להיות מאוד מסתכל. שימו לב לדקויות.

+
+ +
+

לתשומת לב: תוכלו לראות את הגרסה הזו גם ב- GitHub בקישור  apply-javascript-internal.html  או כדף אינטרנט.

+
+ +

External JavaScript (קישור קובץ JavaScript חיצוני)

+ +

הכנסת JavaScript בתוך קובץ ה-HTML עובד בסדר גמור, אך מה אם נרצה לשים את קוד ה-JavaScript בקובץ אחר? נראה כיצד עושים זאת. 

+ +
    +
  1. ראשית, צרו קובץ חדש באותה תיקיה שבו נמצא ה-HTML בשם script.js - וודאו שיש לו את סיומת הקובץ js על מנת שהוא יזוהה כ-JavaScript.
  2. +
  3. החליפו את האלמנט {{htmlelement("script")}}  עם האלמנט  {{htmlelement("script")}} : +
    <script src="script.js" defer></script>
    +
  4. +
  5. בתוך קובץ script.js, הוסיפו את הקוד הבא: +
    function createParagraph() {
    +  let para = document.createElement('p');
    +  para.textContent = 'You clicked the button!';
    +  document.body.appendChild(para);
    +}
    +
    +const buttons = document.querySelectorAll('button');
    +
    +for(let i = 0; i < buttons.length ; i++) {
    +  buttons[i].addEventListener('click', createParagraph);
    +}
    +
  6. +
  7. שמרו ורעננו את הדפדפן. זה עובד בדיוק אותו דבר כמו JavaScript פנימי, רק שכעת JavaScript נמצאת בקובץ חיצוני. באופן כללי, שמירת קוד JavaScript בקובץ חיצוני עדיפה בהיבט ארגון הקוד ושימוש חוזר שלו בכמה קבצי HTML. בנוסף, זה מאפשר לנו לקרוא את ה-HTML בצורה ברורה יותר.
  8. +
+ +
+

לתשומת לב: ניתן לראות את הגרסה הזו של הקוד בקישור  apply-javascript-external.html וכ-דף אינטרנט.

+
+ +

Inline JavaScript handlers (בתוך אלמנט ספציפי בקובץ ה-HTML)

+ +

תשומת לב כי לפעמים אנחנו ניתקל בקוד JavaScript שכתוב בתוך אלמנט ספציפי בקובץ ה-HTML.  זה נראה כך: 

+ +
+
function createParagraph() {
+  let para = document.createElement('p');
+  para.textContent = 'You clicked the button!';
+  document.body.appendChild(para);
+}
+ +
<button onclick="createParagraph()">Click me!</button>
+
+ +

ניתן את את הקוד הבא:

+ +

{{ EmbedLiveSample('inline_js_example', '100%', 150, "", "", "hide-codepen-jsfiddle") }}

+ +

לקוד זה יש את פונקציונליות זהה לקוד בשני החלקים הקודמים, כלומר, הוא מבצע בדיוק את אותו דבר, רק שאלמנט הכפתור - {{htmlelement("button")}}  מכיל בתוכו onclick  על מנת לאפשר לפונקציה לרוץ כשהכפתור נלחץ.

+ +

בבקשה לא לבצע שימוש בשיטה זו. זה נחשב ל-bad practice ״לזהם״ את קובץ HTML יחד עם JavaScript ולא יעיל, שכן בשיטה זו צריך להוסיף ()onclick=״createParagraph״ לכל אחד מהכפתורים שיהיו בדף. 

+ +

לעומת זאת, שימוש ב-JavaScript בלבד, מאפשר לנו לבחור את כל הכפתורים באמצעות קוד פשוט וקצר. לדוגמא: 

+ +
const buttons = document.querySelectorAll('button');
+
+for(let i = 0; i < buttons.length ; i++) {
+  buttons[i].addEventListener('click', createParagraph);
+}
+ +

ממבט ראשון זה אומנם נראה ארוך יותר מהאפשרות הראשונה, אך זה יעבוד לכל הכפתורים בדף, בכל כמות שהיא.

+ +
+

לתשומת לב: נסו לשנות קצת את הגרסה של  apply-javascript.html והוסיפו עוד כפתורים לקובץ. כשתרעננו את הדף, תראו שכל כפתור שנלחץ, יוצר פסקה. 

+
+ +

שיטות לטעינת סקירפטים (Script)

+ +

ישנן כמה סוגיות עם טעינה של סקריפטים באותו זמן. 

+ +

בעיה נפוצה היא שכל אלמנטי ה-HTML בדף מועלים בסדר שבה הם מוצגים/רשומים, מלמעלה למטה. אם אנחנו משתמשים ב-JavaScript על מנת לבצע מניפולציות באלמנטים על הדף (או יותר מדוייק, ב-DOM) הקוד שלנו לא יעבוד אם ה- JavaScript תיטען לפני שה-HTML שבו אנחנו מנסים לבצע מניפולציה ייטען בעצמו.

+ +

בדוגמאות למעלה, ה-JavaScript הפנימי וקובץ ה-JavaScript החיצוני נטענו ורצו בראש המסמך, לפני שה-HTML הועבר. זה יכול ליצור שגיאה. על מנת למנוע זאת כאשר אנחנו מוסיפים JavaScript בתור אלמנט script, הוספנו את הקוד הבא: 

+ +
document.addEventListener("DOMContentLoaded", function() {
+  ...
+});
+ +

זהו ״מאזין לאירוע״  - event listener, אשר ״מקשיב״ לאירוע של הדפדפן בשם ״DOMContentLoaded״. אירוע זה מעיד על כך שגוף ה-HTML, כל תוכן שנמצא בתוך תגית <body>  הועלה בשלמותו. כך, קוד JavaScript בתוך אותו בלוק לא ירוץ עד אשר האירוע יחל, ולכן השגיאה נמנעה. בהמשך הקורס, נרחיב על אירועים ומאזינים לאירועים. 

+ +

על מנת למנוע זאת כאשר אנחנו מוסיפים JavaScript באמצעות קובץ חיצוני, אנחנו משתמשים באפשרות יחסית חדשה של JavaScript, שהיא תכונה (attribute) בשם defer. שבעצם אומרת לדפדפן להמשיך לטעון את ה-HTML ברגע שהוא הגיע לתגית האלמנט <script>:

+ +
<script src="script.js" defer></script>
+ +

בשני המקרים, ה-HTML ייטען במקביל והקוד יעבוד. 

+ +
+

תשומת לב: במקרה של JavaScript הנטען מקובץ חיצוני, אנחנו לא השתמשנו באירוע DOMContentLoaded  מכיוון שתכונת ה-defer פתרה את הבעיה. יחד עם זאת, ניתן להשתמש ב-defer ב-JavaScript חיצוני בלבד. 

+
+ +

דרך מקובלת (ואינה נדרשת יותר) לפתור את הבעיה הזו, הייתה לשים את אלמנט ה-script שלנו ישר מתחת לתגית הסגירה של body. כלומר ישר מתחת לתגית <body/>כך שראשית ייטען כל ה-body. הבעיה עם פתרון זה הוא שטעינה/העברה של הסקריפט חסום לחלוטין עד אשר ה-HTML DOM נטען באופן מלא. באתרים גדולים עם כמות גדולה של JavaScript, זה יכול ליצור בעית ביצועים אשר גורמת להאטת האתר.

+ +

async and defer

+ +

בעיקרון יש  דרכים נוספות לעקוף את סוגיית חסימת הסקריפט - באמצעות — async and defer.

+ +

סקריפטים בעלי תכונה של async יורדו מבלי לחסום את הדף ויורצו ברגע שהסקריפט יסיים לרדת. אין בטחון שהסקריפטים ירוצו בסדר מסויים, אלא שהם פשוט לא יעצרו מיתר הדף מלהיות מוצג. דרך טובה להשתמש ב-async היא כאשר הסקריפטים בדף רצים באופן עצמאי אחד מהשני ולא תלויים בסקריפטים אחרים בדף.

+ +

לדוגמא:

+ +
<script async src="js/vendor/jquery.js"></script>
+
+<script async src="js/script2.js"></script>
+
+<script async src="js/script3.js"></script>
+ +

לא ניתן להסתמך על כך שהסקריפטים ירוצו בסדר שבה הם הועלו. הראשון עלול להיטען אחרי השני או השלישי, ובמקרה הזה, כל פונקציה לדוגמא אשר מוגדרת באותם סקריפטים ותלויה בקובץ הראשון, תציג שגיאה כי הקובץ הראשון לא מוגדר בזמן שהסקריפט השני או השלישי רץ.

+ +

defer לעומת זאת, גורמת לכך שהסקריפטים ירוצו בסדר שבה הם מוצגים על גבי הדף ותריץ אותם ברגע שהסקריפט והתוכן יורדו:

+ +
<script defer src="js/vendor/jquery.js"></script>
+
+<script defer src="js/script2.js"></script>
+
+<script defer src="js/script3.js"></script>
+ +

פה מה שיקרה הוא שכל הסקריפטים עם התכונה של defer ייטענו בסדר שבו הם מופיעים בדף. כך, אנחנו יכולים להיות בטוחים שהקובץ הראשון ייטען במלואו לפני שהקובץ השני יתחיל לרוץ וכך הלאה. 

+ +

לסיכום:

+ + + +

הערות ב-JavaScript

+ +

כמו בשפות HTML ו-CSS, גם ב-JavaScript  ניתן להוסיף הערות בקוד, אשר הדפדפן יתעלם מהן ונועדו על מנת לספק הוראות למפתחים אחרי כיצד הקוד עובד, וגם לכם, אפשרות לרענן את זכרונכם. הערות הן מאוד שימושיות, אנא בצעו בהם שימוש לעיתים קרובות, במיוחד עבור יישומים גדולים. 

+ +

יש שני סוגי הערות:

+ + + +

לדוגמא: 

+ +
// Function: creates a new paragraph and appends it to the bottom of the HTML body.
+
+function createParagraph() {
+  let para = document.createElement('p');
+  para.textContent = 'You clicked the button!';
+  document.body.appendChild(para);
+}
+
+/*
+  1. Get references to all the buttons on the page in an array format.
+  2. Loop through all the buttons and add a click event listener to each one.
+
+  When any button is pressed, the createParagraph() function will be run.
+*/
+
+const buttons = document.querySelectorAll('button');
+
+for (let i = 0; i < buttons.length ; i++) {
+  buttons[i].addEventListener('click', createParagraph);
+}
+ +
+

לתשומת לב: באופן כללי - יותר הערות בדרך כלל עדיף מקצת מדי הערות, אבל צריך לשים לב לא למצוא את עצמנו מוסיפים יותר מדי הערות שמסבירות מהן המשתנים שהגדרנו (במקרה כזה יהיה עדיף כבר להשתמש בשם הגיוני) או הערות שמסבירים פעולות פשוטות ומובנות בקלות. 

+
+ +

לסיכום

+ +

כעת עשינו את הצעד הראשון לתוך עולמה של JavaScript. התחלנו עם קצת תיאוריה על מנת להתחיל להתרגל להשתמש בה ולהבין מה הדברים שאנחנו יכולים לבצע איתה. בהמשך הדרך ראינו מספר דוגמאות קוד ולמדנו כיצד JavaScript משתלבת באתר האינטרנט שלנו, יחד עם רכיבים נוספים. 

+ +

JavaScript עלולה להיראות לנו קצת מלחיצה או לא ברורה בשלב זה, אבל אל דאגה, בקורס הזה אנחנו הולכים בצעדים קטנים אשר נבנים בצורה הגיונית. במאמר הבא אנחנו נעבור ליישום פרקטי ונתחיל בבניית דוגמאות JavaScript שלכם באופן עצמאי.

+ + + +

{{NextMenu("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/first_steps/what_went_wrong/index.html b/files/he/learn/javascript/first_steps/what_went_wrong/index.html new file mode 100644 index 0000000000..87d85d786c --- /dev/null +++ b/files/he/learn/javascript/first_steps/what_went_wrong/index.html @@ -0,0 +1,251 @@ +--- +title: מה השתבש? - פתרון שגיאות ב-JavaScript +slug: Learn/JavaScript/First_steps/What_went_wrong +translation_of: Learn/JavaScript/First_steps/What_went_wrong +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps")}}
+ +

כשבנינו את המשחק ״שחק את המספר״ במאמר הקודם, בטח שמתם לב שהוא לא עבד. מאמר זה לספק לכם הבנה כיצד למצוא שגיאות ב-JavaScript ולתקן אותן.

+ + + + + + + + + + + + +
ידע מוקדם:הבנה בסיסית של מחשב, הבנה בסיסית של  HTML, CSS ו - JavaScript.
מטרה: +

הכרת חלק מהאפשרויות ללתיקון שגיאות בקוד שלנו.

+
+ +

סוגי שגיאות

+ +

באופן כללי, כשאנחנו מבצעים משהו שגוי בקוד, יש שתי סוגי שגיאות עיקריות שאנחנו ניתקל בהן:

+ + + +

אוקיי, זה לא כזה פשוט — יש חלוקות נוספות של סוגי השגיאות, וככל ונעמיק בחומר ננחקור אותן לעומק. לצרוך הלימוד כרגע, החלוקה הזו תספיק בהתחלה. 

+ +

דוגמא לשגיאה

+ +

לשם התחלה, חזרו למשחק ״נחש את המספר״ שלנו - רק שהפעם, השתמשו בקובץ שלנו שהוכנסו בו מכלתחילה מספר שגיאות. כנסו ל-Github והכינו לעצמכם עותק מקומי של number-game-errors.html (ראו זאת בדף אינטרנט כאן).

+ +
    +
  1. לשם התחלה, פתחו את הקובץ המקומי בעורך הקוד ובדפדפן.
  2. +
  3. נסו לשחק עם המשחק, אתם תשימו לב כשאתם לוחצים על כפתור - "Submit guess", הכפתור לא עושה כלום.
  4. +
+ +
+

לתשומת לב: יכול להיות שהגרסה שאתם עבדתם עליה לא עובדת בעצמה, ואולי אותה תרצו לתקן. בכל מקרה, אנא השתמשו בגרסה שלנו לאורך המאמר הנוכחי, על מנת שתוכלו ללמוד את הטכניקות שאנחנו נלמד כאן. לאחר מכן, חזרו לדוגמא שלכם ונסו לתקן אותה אם היא אינה עובדת.

+
+ +

בשלב זה, ניעזר בקונסולה בדפדפן על מנת לראות את שגיאות הסינטקס, וננסה לתקן אותם. 

+ +

תיקון שגיאות סינטקס

+ +

בתחילת הקורס, הזנו מס׳ פקודות של JavaScript לתוך הקונסולה. יתרון נוסף של שימוש בקונסולה הוא העובדה שהקונסולה מחזירה לכם הודעות בדבר שגיאות כתיב כאשר אלו קיימות בקוד ה-JavaScript אשר נטען על ידי מנוע ה- JavaScript של הדפדפן. בוא נבדוק זאת:

+ +
    +
  1. לך ללשונית שבה יש את הקובץ number-game-errors.html פתוח ופתחו את הקונסולה. אתם אמורים לראות שגיאה שמכילה את המלל הבאה:
  2. +
  3. זו שגיאה דיי קלה לאיתור, והדפדפן מספק לם מס׳ נתונים על מנת לעזור לכם - צילום המסך לעיל לקוח מדפדפן Firefox אבל גם דפדפנים אחרים מספקים את אותו מידע. משמאל לימין, זו השגיאה: +
      +
    • ״x״ אדום אשר יעיד על כל שקיימת שגיאה.
    • +
    • הודעת שגיאה על מנת להראות שמשהו השתבש: "TypeError: guessSubmit.addeventListener is not a function".
    • +
    • טקסט וקישור "Learn More" אשר יוביל לדף MDN שמסביר מהי השגיאה - שימו לב שקישור זה קיים רק בפיירפוקס ולא בכרום.
    • +
    • השם של קובץ JavaScript אשר מקושר ללשונית ה-Debugger. אם נעקוב אחרי הקישור נראה את השורה המדויקת שבה התרחשה השגיאה.
    • +
    • מספר השורה שבה התרחשה השגיאה, ומספרו של התו באותה השורה שבו השגיאה נראתה לראשונה. במקרה שלנו, השגיאה התרחשה בשורה 86, ובתו מספר 3.
    • +
    +
  4. +
  5. אם נסתכל על שורה 86 בעורך הקוד שלנו, אנחנו נמצא את השורה הבאה: +
    guessSubmit.addeventListener('click', checkGuess);
    +
  6. +
  7. הודעת שגיאה זו אומרת ש-"guessSubmit.addeventListener is not a function", אז אנחנו כנראה אייתנו משהו בצורה לא נכונה. אם אנחנו לא בטוחים לגבי האיות הנכון של כתיב/סינטקס מסויים, הפתרון הכי טוב זה להסתכל על המאפיינים של הסינטקס באתר של MDN. הדרך הכי טובה לעשות זאת היא לחפש "mdn name-of-feature" במנוע החיפוש האהוב עליכם. הנה קישור לדוגמא לחסוך לכם זמן ()addEventListener.
  8. +
  9. אז, אם אנחנו מסתכלים על הדף, השגיאה הופיעה ככל הנראה מכיוון שאייתנו את שם המתודה לא נכון. זכרו ש- JavaScript היא case sensitive, וכל שינוי קטן באיות, או שימוש באותיות גדולות/קטנות לא בצורה נכונה, יגרום לשגיאה. שינוי של addeventListener לכתיב הנכון: addEventListener אמור לתקן את הבעיה.
  10. +
+ +
+

לתשומת לבכם ראו גם: TypeError: "x" is not a function. זהו דף שלנו שמסביר בפרטים על השגיאה הזו.

+
+ +

שגיאות סינטקס - דוגמאות נוספות

+ +
    +
  1. שמרו את הדף ורעננו אותו, אתם אמורים לראות שהשגיאה נעלמה.
  2. +
  3. כעת, אם המשתמש ינסה להכניס ניחוש וללחוץ על הכפתור של Submit, תתקבל שגיאה נוספת:
  4. +
  5. הפעם, השגיאה מדווחת עם ההודעה הבאה: "TypeError: lowOrHi is null", בשורה 78. +
    לתשומת לבכם: Null הוא ערך מיוחד שמשמעותו הוא ״כלום״ או ״ללא ערך״. כך lowOrHi אמנם הוצהר ואותחל, אבל הוא ללא כל ערך, אין לו סוג ואין ערך שהוזן לתוכו.
    + +
    לתשומת לבכם: שגיאה זו לא הופיעה ברגע שהדף נטען שכן שגיאה זו נמצאת בתוך פונקציה - בתוך () { ... }checkGuess. אנו נלמד על פונקציות במאמרים הבאים, ואנו נלמד שקוד אשר נמצא בתוך פונקציה רץ בסקופ (מתחם) אחר ונפרד מהסקופ שרץ בו קוד מחוץ לפונקציה. במקרה הנוכחי - הקוד שבתוך הפונקציה אשר גרם לשגיאה לא רץ עד אשר הפונקציה ()checkGuess לא הופעלה בשורה 26.
    +
  6. +
  7. :הסתכלו על הקוד בשורה 78 +
    lowOrHi.textContent = 'Last guess was too high!';
    +
  8. +
  9. שורה זו מנסה לקבוע את ה-property - מאפיין:  textContent של המשתנה lowOrHi למחרוזת טקסט, אך זה לא יעבוד מכיוון ש-lowOrHi לא מכיל את הערך שהוא אמור להכיל. בו ננסה לחפש מופעים נוספים של lowOrHi בקוד שלנו. הפעם הראשונה שנמצא אותו תהיה בשורה 48: +
    var lowOrHi = document.querySelector('lowOrHi');
    +
  10. +
  11. בשלב זה אנחנו מנסים להפוך את המשתנה שלנו להפנייה לאלמנט בדף ה-html, בוא ננסה לראות האם הוא מקבל את הערך null אחרי ההרצה של השורה הזו. הוסיפו את הקוד הבא לאחר שורה 49: +
    console.log(lowOrHi);
    + +
    +

    לתשומת לבכם: ()console.log מקבלת ארגומנט כלשהו ומדפיסה אותו לקונסולה. זוהי פונקציה מאוד שימושית לשם איתור שגיאות.

    +
    +
  12. +
  13. שמור ורענן, ואתה אמור לראות ש-()console.log החזירו את התשובה הבאה בקונסולה: אנו רואים שהערך של lowOrHiהוא בעצם null בנקודה זו ולכן הבעיה היא עם שורה 48.
  14. +
  15. ננסה לחשוב מהי הבעיה. שורה 28 משתמשת במתודה ()document.querySelector על מנת לקבל הפניה לאלמנט באמצעות בחירת סלקטור ה-CSS שלו. אם נסתכל בדף, אנחנו יכולים למצוא את האלמנט הרלוונטי: +
    <p class="lowOrHi"></p>
    +
  16. +
  17. כאן אנחנו רואים שאנחנו צריכים סלקטור של קלאס, אשר מתחיל עם (.), אבל הסלקטור שמועבר במתודה ()querySelector בשורה 48 אינו מכיל נקודה. זו יכולה להיות הבעיה, נסו לשנות את lowOrHi ל- .lowOrHi וראו מה קורה.
  18. +
  19. שמרו ורעננו את הדף ו-()console.log אמור להחזיר את האלמנט <p> שרצינו. 
  20. +
+ +
+

לתשומת לב: ראו גם TypeError: "x" is (not) "y" -  דף ההסבר שלנו לפרטים נוספים על שגיאה זו.

+
+ +

שגיאות סינטקס נוספות

+ +
    +
  1. אם תנסו לשחק עכשיו במשחק, אתם אמורים להצליח להתקדם יותר - אך עד שהמשחק יסתיים, בין אם ניחשתם את המספר האקראי הנכון ובין אם נגמרו לך הנסיונות.
  2. +
  3. כשהמשחק נגמר, אנחנו מקבלים שגיאה מסוג - "TypeError: resetButton.addeventListener is not a function"! אשר מופיעה בשורה 94.
  4. +
  5. אם נסתכל על שורה 94, נראה כי עשינו טעות באיות המתודה addEventListener ולכן נדרש לשנות את הקוד. להזכירכם, מתודות הן case senitive.
  6. +
+ +

טעות לוגית

+ +

בשלב זה, המשחק אמור לעבוד טוב, אך עם זאת, אחרי כמה משחקים, אתם תשימו לב שהמספר האקראי שלנו הוא תמיד 0 או 1. זה לא ממש מה שהתכוונו.

+ +

יש בוודאות שגיאה לוגית במשחק איפשהו, אך אנחנו לא מקבלים הודעת שגיאה, המשחק עובד, אך לא כמו רצינו.

+ +
    +
  1. נסו לחפש את המשתנה randomNumber ואת השורות בקוד שבהן אנחנו מגדירים אותו. הוא נמצא בשורה 44: + +
    var randomNumber = Math.floor(Math.random()) + 1;
    + והשורה שבה אנחנו מייצרים מספר אקראי חדש לאחר כל משחק היא בשורה 113:
  2. +
  3. +
    randomNumber = Math.floor(Math.random()) + 1;
    +
  4. +
  5. על מנת לבדוק האם השורות האלו הן הבעיתיות, אנחנו ניעזר ב- ()console.log ונכניס את הקוד הבא מתחת לשתי שורות לעיל: +
    console.log(randomNumber);
    +
  6. +
  7. שמרו ורעננו את הדף ותנסו לשחק מספר משחקים. אתם תראו ש- randomNumber שווה ל-1 בכל פעם שאנחנו מדפיסים אותו לקונסולה:
  8. +
+ +

מעבר על הלוגיקה של המשחק

+ +

על מנת לתקן זאת, בואו נחשוב כיצד שורה זו עובדת. קודם כל, אנחנו קוראים ל-()Math.random, אשר מייצרת מספר עשרוני בין 0 ל-1, לדוגמא: 0.5676665434:

+ +
Math.random()
+ +

לאחר מכן אנחנו מעבירים את התוצאה של הפעלת פונקציית ()Math.random דרך הפונקציה ()Math.floor, אשר בתורה, מעגלת את המספר ולאחר מכן מוסיפים 1. אם לא נוסיף 1, פונקציה זו יכולה לעגל כלפי מטה ואז נקבל 0.

+ +
Math.floor(Math.random()) + 1
+ +

1.  אנחנו צריכים להכפיל את המספר האקראי שלנו ב-100 לפני שנעביר אותו הלאה. זה ייתן לנו מספר אקראי בין 0 ל-99:

+ +
Math.floor(Math.random()*100);
+ +

הוספת של 1, תאפשר לנו לקבל ערך בין 1 ל-100:

+ +
Math.floor(Math.random()*100) + 1;
+ +

נסו לשמור ולרענן את הדף ואתם תראו שהמשחק עושה מה שהוא צריך לבצע - מייצר מספר אקראי בין 0 ל-100.

+ +

שגיאות נפוצות נוספות

+ +

ישנן שגיאות נפוצות נוספות שאנחנו ניתקל בהן: 

+ +

SyntaxError: missing ; before statement

+ +

שגיאה זו לרוב אומרת ששכחנו לשים ; בסוף שורות הקוד, אבל היא יכולה להיות גם מסיבה אחרת. אם נשנה את השורה שנמצאת בתוך הפונקציה ()checkGuess :

+ +
var userGuess = Number(guessField.value);
+ +

לקוד הבא:

+ +
var userGuess === Number(guessField.value);
+ +

זה יקפיץ לנו הודעת שגיאה מכיוון ש- JavaScript חושבת שאתה מנסה לבצע משהו אחר. צריך תמיד לוודא שאנחנו לא מערבבים בין סימן השווה שנועד להשים ערך (=) לבין שלושת סימני השווה שנועדו לבדוק האם ערך אחד שווה לשני, ולהחזיר true/false:

+ +
+

לתשומת לב: ראה גם SyntaxError: missing ; before statement דף ההסבר שלנו לפרטים נוספים על שגיאה זו.

+
+ +

המשחק תמיד נותן למשתמש לנצח, לא משנה מה הניחוש שהמשתמש הכניס

+ +

זה יכול להיות עוד סימן לכך שעירבבנו בין סימן ההשמה לבין סימן הבדיקה. לדוגמא, אם אנחנו את השורה הבאה שנמצא בתוך()checkGuess:

+ +
if (userGuess === randomNumber) {
+ +

לקוד הבא:

+ +
if (userGuess = randomNumber) {
+ +

תוצאת המבחן תהיה תמיד true, מה שגורם לכך שתמיד הניחוש של המשתמש נכון.

+ +

SyntaxError: missing ) after argument list

+ +

זוהי שגיאה פשוטה יחסית - היא אומרת באופן כללי ששכחנו לשים סוגריים בסוף קריאה לפונקציה או מתודה:

+ +
+

לתשומת לבד: ראה גם SyntaxError: missing ) after argument list  - דף ההסבר שלנו לפרטים נוספים על שגיאה זו.

+
+ +

SyntaxError: missing : after property id

+ +

שגיאה זו בדרך כלל מתייחסת לכך שלא יצרנו אובייקט ל JavaScript בצורה נכונה. נסה לשנות את הקוד הבא:

+ +
function checkGuess() {
+ +

לקוד הבא:

+ +
function checkGuess( {
+ +

זה גרם לדפדפן לחשוב שאנחנו מנסים להעביר את התוכן של הפונקציה כארגומנט של הפונקציה.

+ +

SyntaxError: missing } after function body

+ +

זו גם שגיאה פשוטה יחסית - היא אומרת בעיקרון ששכחנו אחת מהסוגריים המסולסלות במשפט תנאי שרשמנו. 

+ +

SyntaxError: expected expression, got 'string' or SyntaxError: unterminated string literal

+ +

שגיאות אלו אומרות באופן כללי ששכחנו להוסיף סימן של ' או ". בשגיאה הראשונה string יוחלף עם תו לא ידוע שהדפדפן ימצא במקום המרכאות או הגרש. השגיאה השניה אומרת שהמחרוזת לא הסתיימה עם גרש או מרכאות.

+ +
+

לתשומת לב: ראה גם SyntaxError: Unexpected token וגם SyntaxError: unterminated string literal  - דפי ההסבר שלנו לפרטים נוספים על שגיאות אלו.

+
+ +

לסיכום

+ +

כעת אנו יודעים את הבסיס להבין שגיאות בתוכניות JavaScript הפשוטות שלנו. רק צריך לזכור שזה לא יהיה תמיד קל להבין מה השתבש לנו בקוד. כאשר שגיאה מתרחשת, הסתכלו על מספר השורה שאתם מקבלים, ולכו לאותה שורה על מנת לנסות לאתר מה השתבש. זכרו תמיד שהשגיאה לא תמיד תהיה בשורה זו, ושאולי היא לא תהיה אחת מאלו שעברנו עליהם במאמר זה.

+ +

ראה גם

+ +
+ +
+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/index.html b/files/he/learn/javascript/index.html new file mode 100644 index 0000000000..e606644330 --- /dev/null +++ b/files/he/learn/javascript/index.html @@ -0,0 +1,83 @@ +--- +title: JavaScript +slug: Learn/JavaScript +tags: + - JavaScript + - "JavaScript\_למתחילים" + - מדריך + - מדריך JavaScript + - מדריך javascript למתחילים + - מודול + - מתחיל + - נושא + - קידוד +translation_of: Learn/JavaScript +--- +
{{LearnSidebar}}
+ +

{{Glossary("JavaScript")}} הינה שפת תכנות אשר מאפשרת ליישם דברים מורכבים על גבי דפי האינטרנט. ברגע שדף האינטרנט עושה יותר מסתם להציג אינפורמציה סטטית — להציג עידכוני תוכן משתנים, מפות אינטרקטיביות או גרפים 2D/3D מונפשים, וכן הלאה — כנראה שJavaScript מעורב מאחורי הקלעים.

+ +

מסלול למידה

+ +

ניתן לטעון שJavaScript הינה מסובכת יותר ללימוד מאשר טכנולוגיות קשורות כמו HTML וCSS. לפני הניסיון ללמוד JavaScript, מומלץ מאוד לעשות היכרות לפחות עם שתי הטכנולוגיות המוזכרות, ואולי גם עם עוד טכנולוגיות. ניתן להתחיל בלעבוד על המודולים הבאים:

+ + + +

ניסיון קודם עם שפות תכנות אחרות יכול לעזור גם כן.

+ +

אחרי היכרות עם הבסיס של  JavaScript, הרמה שלכם אמורה להיות מספיק גבוהה בשביל לעבור ללמוד על נושאים מתקדמים, לדוגמא:

+ + + +

מודולים

+ +

נושא זה מכיל את המודולים הבאים, כאשר כל מודול מורכב ממספר מאמרים, מומלץ לעבור עליהם לפי הסדר המוצג.

+ +
+
צעדים ראשונים ב-JavaScript
+
במודול JavaScript הראשון שלנו, אנחנו קודם כל נענה על כמה שאלות בסיסיות כמו "מהי JavaScript?", "איך היא ניראת?" ו-"מה היא יכולה לעשות?", לפני שנתקדם לניסיון הפרקטי הראשון בכתיבת JavaScript. לאחר מכן נדון בכמה תכונות מפתח של JavaScript בפירוט, כגון משתנים, מחרוזות, מספרים ומערכים.
+
אבני הבניין של JavaScript
+
במודול זה, נגע באבני הבניין והמאפיינים הבסיסיים של JavaScript, נפנה את תשומת ליבנו לטיפוסים הנפוצים של בלוקי הקוד כמו משפטי תנאי, לולאות, פונקציות, ואירועים (Events). כבר ניתקלנו בדברים אלו בקורס, אך לא הרחבנו עליהם — במודול זה אנו נדון בכולם לעומק.
+
הכרות עם אובייקטים של JavaScript
+
ב-JavaScript, רוב הדברים הינם אובייקטים, מפיצ'רי הליבה של JavaScript כמו מחרוזות ומערכים עד לAPIים של הדפדפן הבנויים על גבי JavaScript. אתם יכולים אפילו לבנות אובייקטים בעצמכם כדי לעטוף פונקציות ומשתנים קשורים לחבילות אפקטיביות. חשוב להבין את העקרון של תכנות מונחה-העצמים של JavaScript אם אנחנו רוצים להתקדם עם הידע שלנו בשפה ולכתוב קוד יותר אפקטיבי, לכן סיפקנו את המודול הזה לעזור לכם. כאן נלמד את התאוריה והתחביר של אובייקט בפירוט, נראה איך יוצרים אובייקטים משלכם, ונסביר מה זה מידע בפורט JSON ואיך עובדים איתו ועם אובייקטים בכללי.
+
+ + + +
+
Asynchronous JavaScript
+
+

במודול זה אנחנו נסתכל על asynchronous JavaScript ונסביר את החשיבות של עקרון זה. כיצד אנחנו יכולים לבצע בכך שימוש על מנת לטפל באירועים מסויימים כמו קבלת מידע ומקורות משרת.

+
+
+ + + +
+
 Web API - צד לקוח
+
כאשר כותבים JavaScript עבור צד-לקוח לדפי אינטרנט או יישומי אינטרנט, כמעט מיד תתקלו בצורך להשתמש ב - API - אלו ממשקים עבור ביצוע מניפולציות של היבטים שונים של הדפדפן ומערכות ההפעלה שהאתר רץ עליהם, או אפילו מניפולציה לנתונים מאתרים או שירותים אחרים. במודול הזה אנו נחקור מהם API ואיך להשתמש בכמה מה-API הנפוצים שתפגשו בהם לעיתים קרובות במהלך עבודת הפיתוח. 
+
+ +

פתרון בעיות נפוצות ב - JavaScript

+ +

המאמר בנושא שימוש ב-JavaScript לפתרון בעיות נפוצות מספק קישורים לקטעי תוכן המסבירים איך להשתמש ב-JavaScript על מנת לפתור בעיות מאוד נפוצות בתהליך הפיתוח.

+ +

ראה בנוסף

+ + + +
+
JavaScript on MDN
+
נקודת הפתיחה הראשית לליבת התיעוד של JavaScript ב-MDN — כאן תמצאו תיעוד נרחב על כל ההיבטים של שפת JavaScript, וכמה הדרכות מתקדמות המכוונות למפתחי JavaScript מנוסים.
+
Coding math
+
סדרות של הדרכות וידאו מצויינות ללימוד המתמטיקה הנצרכת להפיכתכם למתכנתים אפקטיביים, מועבר על ידי Keith Peters.
+
diff --git a/files/he/learn/javascript/objects/adding_bouncing_balls_features/index.html b/files/he/learn/javascript/objects/adding_bouncing_balls_features/index.html new file mode 100644 index 0000000000..23197f00c4 --- /dev/null +++ b/files/he/learn/javascript/objects/adding_bouncing_balls_features/index.html @@ -0,0 +1,214 @@ +--- +title: Adding features to our bouncing balls demo +slug: Learn/JavaScript/Objects/Adding_bouncing_balls_features +translation_of: Learn/JavaScript/Objects/Adding_bouncing_balls_features +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}
+ +

במשימה זאת, אתה צריך להשתמש בכדורים הקופצים מהמאמר הקודם כנקודת התחלה ולהוסיף אליה כמה פיצ'רים חדשים ומעניינים.

+ + + + + + + + + + + + +
Prerequisites:Before attempting this assessment you should have already worked through all the articles in this module.
Objective:To test comprehension of JavaScript objects and object-oriented constructs
+ +

Starting point

+ +

To get this assessment started, make a local copy of index-finished.html, style.css, and main-finished.js from our last article in a new directory in your local computer.

+ +

Alternatively, you could use a site like JSBin or Glitch to do your assessment. You could paste the HTML, CSS and JavaScript into one of these online editors. If the online editor you are using doesn't have separate JavaScript/CSS panels, feel free to put them inline <script>/<style> elements inside the HTML page.

+ +
+

Note: If you get stuck, then ask us for help — see the {{anch("Assessment or further help")}} section at the bottom of this page.

+
+ +

Hints and tips

+ +

A couple of pointers before you get started.

+ + + +

Project brief

+ +

Our bouncy ball demo is fun, but now we want to make it a little bit more interactive by adding a user-controlled evil circle, which will eat the balls if it catches them. We also want to test your object-building skills by creating a generic Shape() object that our balls and evil circle can inherit from. Finally, we want to add a score counter to track the number of balls left to capture.

+ +

The following screenshot gives you an idea of what the finished program should look like:

+ +

+ + + +

To give you more of an idea, have a look at the finished example (no peeking at the source code!)

+ +

Steps to complete

+ +

The following sections describe what you need to do.

+ +

Creating our new objects

+ +

First of all, change your existing Ball() constructor so that it becomes a Shape() constructor and add a new Ball() constructor:

+ +
    +
  1. The Shape() constructor should define the x, y, velX, and velY properties in the same way as the Ball() constructor did originally, but not the color and size properties.
  2. +
  3. It should also define a new property called exists, which is used to track whether the balls exist in the program (have not been eaten by the evil circle). This should be a boolean (true/false).
  4. +
  5. The Ball() constructor should inherit the x, y, velX, velY, and exists properties from the Shape() constructor.
  6. +
  7. It should also define a color and a size property, like the original Ball() constructor did.
  8. +
  9. Remember to set the Ball() constructor's prototype and constructor appropriately.
  10. +
+ +

The ball draw(), update(), and collisionDetect() method definitions should be able to stay exactly the same as they were before.

+ +

You also need to add a new parameter to the new Ball() ( ... ) constructor call — the exists parameter should be the 5th parameter, and should be given a value of true.

+ +

At this point, try reloading the code — it should work just the same as it did before, with our redesigned objects.

+ +

Defining EvilCircle()

+ +

Now it's time to meet the bad guy — the EvilCircle()! Our game is only going to involve one evil circle, but we are still going to define it using a constructor that inherits from Shape() to give you some practice. You might want to add another circle to the app later on that can be controlled by another player, or have several computer-controlled evil circles. You're probably not going to take over the world with a single evil circle, but it will do for this assessment.

+ +

The EvilCircle() constructor should inherit x, y, velX, velY, and exists from Shape(), but velX and velY should always equal 20.

+ +

You should do this something like Shape.call(this, x, y, 20, 20, exists);

+ +

It should also define its own properties, as follows:

+ + + +

Again, remember to define your inherited properties as parameters in the constructor, and set the prototype and constructor properties correctly.

+ +

Defining EvilCircle()'s methods

+ +

EvilCircle() should have four methods, as described below.

+ +

draw()

+ +

This method has the same purpose as Ball()'s draw() method: It draws the object instance on the canvas. It will work in a very similar way, so you can start by copying the Ball.prototype.draw definition. You should then make the following changes:

+ + + +

checkBounds()

+ +

This method will do the same thing as the first part of Ball()'s update() function — look to see whether the evil circle is going to go off the edge of the screen, and stop it from doing so. Again, you can mostly just copy the Ball.prototype.update definition, but there are a few changes you should make:

+ + + +

setControls()

+ +

This method will add an onkeydown event listener to the window object so that when certain keyboard keys are pressed, we can move the evil circle around. The following code block should be put inside the method definition:

+ +
let _this = this;
+window.onkeydown = function(e) {
+    if (e.key === 'a') {
+      _this.x -= _this.velX;
+    } else if (e.key === 'd') {
+      _this.x += _this.velX;
+    } else if (e.key === 'w') {
+      _this.y -= _this.velY;
+    } else if (e.key === 's') {
+      _this.y += _this.velY;
+    }
+  }
+ +

So when a key is pressed, the event object's keyCode property is consulted to see which key is pressed. If it is one of the four represented by the specified keycodes, then the evil circle will move left/right/up/down.

+ +

For a bonus point, can you tell us why we've had to set let _this = this; in the position it is in? It is something to do with function scope.

+ +

collisionDetect()

+ +

This method will act in a very similar way to Ball()'s collisionDetect() method, so you can use a copy of that as the basis of this new method. But there are a couple of differences:

+ + + +

Bringing the evil circle into the program

+ +

Now we've defined the evil circle, we need to actually make it appear in our scene. To do this, you need to make some changes to the loop() function.

+ + + +

Implementing the score counter

+ +

To implement the score counter, follow the following steps:

+ +
    +
  1. In your HTML file, add a {{HTMLElement("p")}} element just below the {{HTMLElement("h1")}} element containing the text "Ball count: ".
  2. +
  3. In your CSS file, add the following rule at the bottom: +
    p {
    +  position: absolute;
    +  margin: 0;
    +  top: 35px;
    +  right: 5px;
    +  color: #aaa;
    +}
    +
  4. +
  5. In your JavaScript, make the following updates: +
      +
    • Create a variable that stores a reference to the paragraph.
    • +
    • Keep a count of the number of balls on screen in some way.
    • +
    • Increment the count and display the updated number of balls each time a ball is added to the scene.
    • +
    • Decrement the count and display the updated number of balls each time the evil circle eats a ball (causes it not to exist).
    • +
    +
  6. +
+ + + +

Assessment or further help

+ +

If you would like your work assessed, or are stuck and want to ask for help:

+ +
    +
  1. Put your work into an online shareable editor such as CodePen, jsFiddle, or Glitch.
  2. +
  3. Write a post asking for assessment and/or help at the MDN Discourse forum Learning category. Your post should include: +
      +
    • A descriptive title such as "Assessment wanted for Adding bouncing balls features".
    • +
    • Details of what you have already tried, and what you would like us to do, e.g. if you are stuck and need help, or want an assessment.
    • +
    • A link to the example you want assessed or need help with, in an online shareable editor (as mentioned in step 1 above). This is a good practice to get into — it's very hard to help someone with a coding problem if you can't see their code.
    • +
    • A link to the actual task or assessment page, so we can find the question you want help with.
    • +
    +
  4. +
+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}

+ +

In this module

+ + diff --git a/files/he/learn/javascript/objects/basics/index.html b/files/he/learn/javascript/objects/basics/index.html new file mode 100644 index 0000000000..25c439e43d --- /dev/null +++ b/files/he/learn/javascript/objects/basics/index.html @@ -0,0 +1,257 @@ +--- +title: JavaScript object basics +slug: Learn/JavaScript/Objects/Basics +translation_of: Learn/JavaScript/Objects/Basics +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects")}}
+ +

במאמר בזה, אנחנו נסתכל על עקרונות הסינטקס הבסיסים של אובייקטים ב-JavaScript, ונבקר בחלק ממאפייניה של השפה שנתקלנו בה בעבר, ונבין כי חלק ממאפיינים אלו הם בעצם אובייקטים.

+ + + + + + + + + + + + +
Prerequisites:Basic computer literacy, a basic understanding of HTML and CSS, familiarity with JavaScript basics (see First steps and Building blocks).
מטרה:הבנת התיאוריה הבסיסית שעומדת מאחורי תכנות מונחה עצמים, כיצד זה מתקשר ל-JavaScript (״רוב הדברים הם אובייקטים״) והבנה כיצד ניתן לעבוד עם אובייקטים ב-JavaScript.
+ +

Object - אובייקטים - עקרונות יסוד

+ +

אובייקט הוא אוסף של מידע או פונקציונליות (בדרך מכיל מספר של מתשנים ופונקציות, אשר נקראים - מאפיינים (properties) ו-מתודות (Methodes) כאשר ה נמצאים בתוך אובייקט). נסתכל על דוגמא על מנת להמחיש את הנושא.

+ +

על מנת להתחיל, הכינו עותק מקומי של oojs.html. קובץ זה מכיל אלמנט של {{HTMLElement("script")}} על מנת שבתוכו נכתוב את הקוד שלנו. אנחנו נשתמש בתצורה הזו על מנת להבין את הסינטקס הבסיסי של אובייקטים. יד עם קובץ זה, פתחו את developer tools JavaScript console.

+ +

כמו בהרבה דברים ב-JavaScript, יצירת אובייקט לרוב מתחילים עם הגדרת ואתחול של משתנה. נסו להכניס את הקוד הבא לתוך קוד ה-JavaScript שבקובץ ולאחר מכן שמרו ורעננו את הדף:

+ +
const person = {};
+ +

כעת פתחו את הקונסולה בדפדפן והזינוו person בתוך הקונסולה, ולאחר מכן לחצו על , Enter/Return. אתם אמורים לקבל תוצאה שנראית כך:

+ +
[object Object]
+Object { }
+{ }
+
+ +

מזל טוב, הרגע יצרתם את האובייקט הראשון שלכם. יחד עם זאת, זהו אובייקט ריק, אז אין יותר מדי מה לעשות איתו. נעדכן את האובייקט שלנו בתוך הקובץ כך שייראה בתצורה הזו:

+ +
const person = {
+  name: ['Bob', 'Smith'],
+  age: 32,
+  gender: 'male',
+  interests: ['music', 'skiing'],
+  bio: function() {
+    alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
+  },
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name[0] + '.');
+  }
+};
+
+ +

לאחר שמירה ורענון של הדף, נסו להכניס את השורות הבאות לתוך הקונסולה:

+ +
person.name
+person.name[0]
+person.age
+person.interests[1]
+person.bio()
+person.greeting()
+ +

כעת יש לנו מידע ופונקציונליות בתוך האובייקט שלנו, ואנחנו יכולים לגשת אליהם באמצעות סינטקס פשוט:

+ +
+

לתשומת לב: אם אינכם מצליחים לגרום לכך לעבוד, נסו להשוות אל מול הקובץ שלנו oojs-finished.html או ראו את דף האינטרנט ופתחו את הקונסולה בדף זה

+
+ +

אובייקט מורכב ממספר רכיבים, כאשר לכל רכיב יש שם   (name ו- age למעלה), וכן יש לכל רכיב value (e.g. ['Bob', 'Smith'] ו- 32). כל צמד של שם/ערך חייב להיות מופרד באמצעות פסיק , והשם והערך בכל צמד חייבים להיות מופרדים עם :. הסינטקס תמיד ייראה בתבנית הזו:

+ +
const objectName = {
+  member1Name: member1Value,
+  member2Name: member2Value,
+  member3Name: member3Value
+};
+ +

הערך של אובייקט יכול להיות כל דבר - במקרה שלנו, האובייקט person, יש לנו מחרוזת, מספר, שני מערכים, ושתי פונקציות. ארבעת הצמדים הראשונים הם פריטי מידע והם נקראים כ-properties של האובייקט. שני הצמדים האחרונים אלו פונקציות שמאפשרות לאובייקט לעשות משהו עם המידע הזה, והם נקראים ה-methods של האובייקט.

+ +

אובייקט כמו זה נקרא object literal — אנחנו באופן ליטראלי )מפורש) יצרנו את האובייקט והתוכן שלו. זה בניגוד לאובייקטים שנוצרו ממחלקות (classs) שאשר נראה בהמשך.

+ +

זה נפוץ מאוד ליצור אובייקט באמצעות object literal כאשר אנחנו רוצים להעביר סדרה של פריטי מידע קשורים ובמבנה מסויים, לדוגמא, שליחת בקשה לשרת לצורך השמה במאגר המידע. שליחה של אובייקט יחיד היא יעילה יותר מאשר שליחה של כמה פריטי מידע בנפרד, וזה קל יותר לעבוד עם אובייקט מאשר עם מערך, כאשר אנחנו רוצים לזהות כל פריט באמצעות השם שלךו.

+ +

Dot notation שימוש בנקודה-

+ +

למעלה, אנחנו יכולים לגשת למתודות ולפרופ׳ של האובייקט באמצעות dot notation. שם האובייקט - person - משמש כ-namespace - הוא חייב להיות מוכנס ראשון, על מנת לקבל גישה לכל מה שמוכס - encapsulated בתוך האובייקט. לאחר מכן אנחנו רושמים נקודה . ואז את הפריט מידע שאנחנו רוצים לגשת אליו - זה יכול שם של פרופ׳ מסויים, או לקרוא לאחת מהמתודות של האובייקט. לדוגמא:

+ +
person.age
+person.interests[1]
+person.bio()
+ +

Sub-namespaces

+ +

זה אפילו אפשרי ליצור אובייקט כערך מסויים של אובייקט אחר. כך לדוגמא, נסו לשנות את ערך הפרופ׳ name מהקוד הבא:

+ +
name: ['Bob', 'Smith'],
+ +

לקוד זה:

+ +
name : {
+  first: 'Bob',
+  last: 'Smith'
+},
+ +

כאן אנחנו בעצם יצרנו sub-namespace. זה אולי נשמע מורכז, אבל זה לא - על מנת לגשת לפריטים אלו, אנחנו פשוט צריכים להשתמש בעוד נקודה .. נסו להזין את הקוד הבא בקונסולה:

+ +
person.name.first
+person.name.last
+ +

חשוב: לאחר שינוי זה אתם צריכים גם לשנות במתודות את צורת הכתיבה לאור כך ששינינו את הערך ממערך לאובייקט

+ +
name[0]
+name[1]
+ +

לתצורה הבאה:

+ +
name.first
+name.last
+ +

אחרת המתודות לא יעבדו.

+ +

Bracket notation - שימוש בסוגריים מרובעות

+ +

יש דרך נוספת לגשת לפרופ׳ של האובייקט - באמצעות שימוש בסוגריים מרובעות. במקום הקוד הבא:

+ +
person.age
+person.name.first
+ +

אנחנו יכולים להשתמש בקוד זה:

+ +
person['age']
+person['name']['first']
+ +

זה אמנם נראה מאוד דומה לדרך שבה אנחנו ניגשים לאיברים במערך, ובעיקרון זה אותו דבר - אך במקום להשתמש במספר אינדקס על מנת לבחור איבר מסויים, כפי שנאחנו עושים במערך, אנחנו משתמשים בשם המקושר לכל ערך. זה לא פלא שלפעמים אובייקטים נקראים associative arrays — they map strings to values in the same way that arrays map numbers to values.

+ +

הגדרת מפתחות ומתודות לאובייקט -

+ +

עד עכשיו ראינו כיצד לאחזר או לגשת לפריטים באובייקט - אך אנחנו יכולים גם לקבוע או לעדכן את הערכים של אותם פריטים ואף ליצור מפתחות ומתודות חדשים, באמצעות הצהרה על אותו מפתח או מתודה שאנחנו רוצים לעדכן או קבוע - (באמצעות שימוש בנקודה או בסוגריים המרובעות) כך:

+ +
person.age = 45;
+person['name']['last'] = 'Cratchit';
+ +

נסו להזין את השורות למעלה ואז לגשת לערכים ששיניתם באמצעות הקוד על מנת לראות השינוי:

+ +
person.age
+person['name']['last']
+ +

קביעת ערכים במפתחות או מתודות קיימים היא רק אחד מהדברים שניתן לעשות. כפי שאמרנו למעלה, ניתן גם ליצור מפתחות ומתודות חדשים לאותו אובייקט. נסו להזין את הקוד הבא בקונסולה:

+ +
person['eyes'] = 'hazel';
+person.farewell = function() { alert("Bye everybody!"); }
+ +

וכעת בדקו את המפתח והמתודה החדשה כך:

+ +
person['eyes']
+person.farewell()
+ +

היבט מאוד שימושי של שימוש בסוגריים מרובעות הוא שזה לא רק מאפשר לנו לקבוע את הערכים של אותו פריט בצורה דינאמית, אלא גם את השם של אותו פרופ׳ . נניח שאנחנו רוצים שהמשתמשים יוכלו לאחסן ערכים שונים בתוך המידע, באמצעות One useful aspect of bracket notation is that it can be used to set not only member values dynamically, but member names too. Let's say we wanted users to be able to store custom value types in their people data, by typing the member name and value into two text inputs. We could get those values like this:

+ +
let myDataName = nameInput.value;
+let myDataValue = nameValue.value;
+ +

We could then add this new member name and value to the person object like this:

+ +
person[myDataName] = myDataValue;
+ +

To test this, try adding the following lines into your code, just below the closing curly brace of the person object:

+ +
let myDataName = 'height';
+let myDataValue = '1.75m';
+person[myDataName] = myDataValue;
+ +

Now try saving and refreshing, and entering the following into your text input:

+ +
person.height
+ +

הוספת פרופ׳ לאובייקט באמצעות המתודה לעיל אינה אפשרית כאשר אנחנו משתמשים ב-dot notation, כי היא מקבלת רק ערך ליטרלי ולא ערך של משתנה מסויים שמצביע על אותו שם.

+ +

מה זה "this"?

+ +

אולי שמתם לב למשהו קצת מוזר בתוך המתודות שלנו. ראו את הדוגמא הבאה:

+ +
greeting: function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+}
+ +

אתם בטח שואלים את עצמכם מה זה "this". המילה השומרה this מתייחסת לאובייקט שהקוד נכתב בתוכו - אז במקרה הזה המילה this שווה ל- person. אז מדוע לא לרשום person במקום? כפי שתראו במאמר שלנו בנושאObject-oriented JavaScript מתחילים - כאשר אנחנו יוצרים קונסטרקטורים, המילה this היא מאוד שימושית - היא תבטיח לנו תמיד שהערכים הנכונים מושמים בכל פעם שהמופעים של האובייקט פפפפפ נוצרים - גלומר - אובייקטים שנוצרו - 999999, יהיו בעלי ערכים שונים article, when we start creating constructors and so on, this is very useful — it will always ensure that the correct values are used when a member's context changes (e.g. two different person object instances may have different names, but will want to use their own name when saying their greeting).

+ +

ננסה לפשט ולהסביר באמצעות יצירת שני אובייקטים של person:

+ +
const person1 = {
+  name: 'Chris',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+const person2 = {
+  name: 'Brian',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

במקרה הזה, person1.greeting() יציג "Hi! I'm Chris.";ואילו person2.greeting() יציג "Hi! I'm Brian.", למרות שהקוד של המתודה הוא זהה לחלוטין. כפי שאמרנו למעלה, this שוה בעצם לאובייקט שהקוד נמצא בתוכו - this לא משמעותי ושימושי כאשר אנחנו יוצרים אובייקטים באמצעות אובייקט ליטראלי, אבל הוא מאוד שימושי כאשר אנחנו יוצרים אובייקטים באופן דינאמי (באמצעות קונסטרקטורים לדוגמא). זה יהיה ברור יותר בהמשך הדרך, מבטיחים.

+ +

השתמשנו באובייקטים לאורך כל הזמן הזה

+ +

ככל שאנחנו עוברים על הדוגמאות הללו, אתם בטח חושבים לעצמכם שכבר השתמשנו בעבר בתחביר של סימון נקודה, וזה נכון. בכל פעם שאנחנו השתמשנו בדוגמא אשר משתמשת ב-api מובנה של הדפדפן או באובייקט של javascript, אנחנו בעצם השתמשנו באובייקטים, מכיוון שאותם מאפיינים נבנו באמצעות שימוש במבנה זה למבנה של אובייקט שהסברנו עליו במודול זה, אמנם בצורה קצת יותר מורכבת מהדוגמאות שלנו כאן, אך במבנה דומה.

+ +

כאשר אנחנו משתמשים במתודות של מחרוזות כאלו לדוגמא:

+ +
myString.split(',');
+ +

אנחנו בעצם מתשמשים במתודה שזמינה לנו באמצעות האובייקט הגלובלי String. בכל פעם שאנחנו יוצרים מחרוזת בקוד שלנו, מחרוזת זו באופן אוטומטי נוצרת כמופע/מודל/דוגמא של String, ולכן יש לה מספר מתודות ופרופ׳ אשר זמינות לה.

+ +

כאשר אנחנו ניגשים ל-document object model אמצעות הקוד הבא:

+ +
const myDiv = document.createElement('div');
+const myVideo = document.querySelector('video');
+ +

אנחנו מתשמשים במתודות שזמינות לנו עבור מופע של ה-Document. לכל דף אינטרנט שמועלה, נוצר מופע של Document, אשר נקרא documentוהוא מייצג את כל המבנה, תוכן ומאפיינים נוספים של אותו דף (כמו לדוגמא ה-URL שלו). וזה אומר שיש לו מספר מתודות ומפתחות שזמינים עבור אותו מופע זה.

+ +

אותו הדבר בערך קורה עם הרבה אובייקטים מובנים/api שאנחנו משתשמים בהם — Array, Math, וכך הלאה.

+ +

שימו לב שbuilt in Objects/APIs לא תמיד יוצרים מופעים של אובייקט באופן אוטומטי. לדוגמא, ה- Notifications API — אשר מאפשר לדפדפנים מודרניים להזרים התראות מערת - דורש מאיתנו לייצר מופע אובייקט חדש באמצעות קונסטקטור, עבור כל התראה שנרצה להזרים. נסו להזין את הקוד הבא בקוסולה: :

+ +
const myNotification = new Notification('Hello!');
+ +

אנו נסתכל על קונסרקטורים במאמרים הבאים.

+ +
+

לתשומת לב: זה מאוד שימושי לחשוב על הדרך שבה אובייקטים מתקשרים כ- message passing — כאשר אובייקט אחד צריך אובייקט אחד על מנת לבצע פעולה מסויימת, הוא לרוב ישלח הודעה לאותו אובייקט באמצעו תאחת המתודות שלו ויחכה לתגובה, שהיא בעצם ה- return value.

+
+ +

לסיכום

+ +

סיימנו את המאמר הראשונות שלנו בנושא אובייקטים ב-JavaScript - כעת, אמור להיות לכם בסיס כיצד לעבוד עם אובייקטים ב-JavaScript, כיצד ליצור אובייקטים פשוטים בעצמם. הבנו גם שאובייקטים הם מבנה מאוד שימוש לאחסון של מידע ופונקציונליות הקשורים זה בזה - אם היינו מנסים לשמור את כל המידע והפונקציונליות של האובייקט person כמשתנים ופונקציות נפרדות, זה היה מאתגר וקשה. אבוייקטים מאפשרים לנו לשמור את המידע בצורה בטוחה ונעולה בתוך חבילה משל עצמו.

+ +

במאמר הבא אנחנו נסתכל על תיאוריית object-oriented programming (OOP), וכיצד טכניקות שכאלו יכולות לשמש אותנו ב- JavaScript.

+ +

{{NextMenu("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/objects/index.html b/files/he/learn/javascript/objects/index.html new file mode 100644 index 0000000000..3b033ee0e8 --- /dev/null +++ b/files/he/learn/javascript/objects/index.html @@ -0,0 +1,54 @@ +--- +title: Introducing JavaScript objects +slug: Learn/JavaScript/Objects +tags: + - Article + - Assessment + - Beginner + - CodingScripting + - Guide + - JavaScript + - Learn + - NeedsTranslation + - Objects + - TopicStub + - Tutorial +translation_of: Learn/JavaScript/Objects +--- +
{{LearnSidebar}}
+ +

ב-JavaScript, רוב הדברים הם אובייקטים, החל מהמאפיינים הבסיסיים של השפה כמו מחרוזות ומערכים, ועד {{Glossary("API", "APIs")}} של הדפדפן שבנויים מעל JavaScript. אנחנו אפילו יכולים ליצור אובייקטים בעצמנו, ו-״להכמיס״ בתוכן פונקציות ומשתנים כך שיתפקדו ככלי להכלת מידע מאוד שימושי. חשוב להבין את הרעיון של JavaScript סביב האובייקט אם אנחנו רוצים להמשיך להתקדם בלמידה שלנו של שפה זו, ומודול זה נועד בדיוק לשם כך. במודול זהנלמד על התיאוריה סביב אובייקט וכן על הסינטקס בפרטים ומשם נמשיך ליצירת אובייקטים בעצמנו

+ +

ידע מוקדם

+ +

לפני שתחלו בלימוד מודול זה, ודאו כי אתם בקיאים ב- {{Glossary("HTML")}} וב- {{Glossary("CSS")}}. אנו ממליצים לכם לעבור על המודולים בנושא הכרות עם HTML ו-הכרות עם CSS לפני שתמשיכו בלימוד JavaScript.

+ +

בנוסף, הנכם מתבקשים לעבור על עקרונות הבסיס של JavaScript לפני שתמשיכו במודול זה. לשם כך, וודאו כי הנכם בקיאי במודול צעדים ראשונים ב-JavaScript ובמודול אבני הבניין של JavaScript לפני שתמשיכו במודול זה.

+ +
+

הערה: אם אתם עובדים על מחשב\טבלט\מכשיר אחר שאין לכם אפשרות ליצור עליו קבצים אישיים, אתם יכולים לנסות את (רוב) דוגמאות הקוד על תוכנות קוד אינטרנטיות כמו JSBin או Thimble.

+
+ +

Guides

+ +
+
אובייקטים - עקרונות יסוד
+
במאמר הראשון אנחנו נסתכל על אובייקטים ב-JavaScript, ועל הסינטקס הבסיסי והחשוב של אובייקט. נבקר שוב כמה מאפשרויות של JavaScript שכבר ראינו לפני כן בקורס, ונבין שהרבה מאותן אפשרויות אלו שנתקלנו בהן בעבר, הן בעצם אובייקטים.
+
תכנות מונחה עצמים ב-JavaScript למתחילים
+
לאחר שסיימנו עם הבסיס, נתמקד במעבר על התיאוריה של תכנות מונחה עצמים (object-oriented programming (OOP)) ב-JavaScript (object-oriented JavaScript (OOJS)) - מאמר זה יספק רקע בסיסי על תכנות מונחה עצמים בכלל, ולאחר מכן נחקור כיצד JavaScript מחקה (מלשון: חיקוי) מחלקות (classes) באמצעות constructor functions ואיך אנחנו יוצרים אובייקט.
+
Object prototypes - טיפוסי אב של אובייקט
+
טיפוסי אב - Prototypes - זה המנגנון שבאמצעותו אובייקטים ב-JavaScript יורשים מאפיינים אחד מהשנים, והם עובדים בצורה אחרת מאשר מנגנוני הורשה הקיימים בשפות לתכנות מונחה עצמים אחרות. במאמר זה אנחנו נחקור את ההבדלים השונים, נסביר כיצד שרשרת אבי הטיפוס (prototype chains) עובדת ונסתכל על איך ה-property שנקרא prototype יכול לשמש על מנת להוסיף מתודות לקונסטרקטורים קיימים.
+
הורשה ב-JavaScript
+
לאחר שסיימנו להסביר על התיאוריה של תכנות מונחה עצמים (object-oriented programming (OOP)) ב-JavaScript, מאמר זה יראה איך ליצור מחלקות בנות (constructors) שיורשות מאפיינים מהמחלקות ״הורים״ With most of the gory details of OOJS now explained, this article shows how to create "child" object classes (constructors) that inherit features from their "parent" classes. In addition, we present some advice on when and where you might use OOJS.
+
Working with JSON data
+
JavaScript Object Notation (JSON) is a standard text-based format for representing structured data based on JavaScript object syntax, which is commonly used for representing and transmitting data on web sites (i.e. sending some data from the server to the client, so it can be displayed on a web page). You'll come across it quite often, so in this article we give you all you need to work with JSON using JavaScript, including parsing the JSON so you can access data items within it and writing your own JSON.
+
Object building practice
+
In previous articles we looked at all the essential JavaScript object theory and syntax details, giving you a solid base to start from. In this article we dive into a practical exercise, giving you some more practice in building custom JavaScript objects, which produce something fun and colorful — some colored bouncing balls.
+
+ +

Assessments

+ +
+
Adding features to our bouncing balls demo
+
In this assessment, you are expected to use the bouncing balls demo from the previous article as a starting point, and add some new and interesting features to it.
+
diff --git a/files/he/learn/javascript/objects/inheritance/index.html b/files/he/learn/javascript/objects/inheritance/index.html new file mode 100644 index 0000000000..1d43d06d14 --- /dev/null +++ b/files/he/learn/javascript/objects/inheritance/index.html @@ -0,0 +1,437 @@ +--- +title: Inheritance in JavaScript +slug: Learn/JavaScript/Objects/Inheritance +translation_of: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

לאחר שסיימנו עם התיאוריה והפרטים של OOJS, המאמר הזה נועד להראות כיצד ליצור מחלקות אובייקטים ״ילדים״ (constructors) אשר יורשים מההורים שלהם. אנו גם נציג מעין עצות מתי נרצה להשתמש ב-OOJS ונסתכל כיצד ״מחלקות״ מתבצעות בסינטקס המודרני של ECMAScript.

+ + + + + + + + + + + + +
ידע מוקדם: +

אוריינות מחשב בסיסית, הבנה בסיסית ב-HTML ו ב-CSS, היכרות עם יסודות ה-JavaScript (ראה First steps and Building blocks) and OOJS basics (see Introduction to objects).

+
מטרה:להבין כיצד זה אפשרי להחיל הורשה ב- JavaScript
+ +

Prototypal inheritance

+ +

עד עכשיו ראינו קצת הורשה בפעולה - ראינו כיצד prototype chains עובדות וכיצד מתודות ופרופ׳ מורשים בהמשך השרשרת. אבל זה היה בעיקר עם פונקציות מובנות של הדפדפן. כיצד אנחנו יכולים ליצור אובייקט ב-JavaScript אשר יורש מאובייקט אחר?

+ +

דוגמא:

+ +

ראשית, עשו עותק מקומי של הקובץ או ראו אותו כדף אינטרנט. בקוד זה אתם תראו את ה-Person() constructor שהשתמשנו בו לאורך המודול, עם שינוי קל - הגדרנו רק את ה-properties בתוך ה-constructor.

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

כל המתודות כולן מוגדרות בתוך ה-constructor's prototype. לדוגמא:

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +
+

לתשומת לבך: בקוד המקור אתם גם תראו מתודות נוספות שמוגדרות, ()bio ו-()farewell. אנחנו נראה בהמשך כיצד מתודות אלו יכולות להיות מורשות ל-constructors אחרים.

+
+ +

נניח ואנחנו רוצים ליצור מחלקה של Teacher, כמו זו שהסברנו בתחילת המודול לגבי תכנות מונחה עצמים, ומחלקה זו יורשת את כל הפרופ׳ והמתודות של Person, אבל גם כוללת:

+ +
    +
  1. ה- property החדש בשם subject — אשר יכיל את נושאי הלימוד שהמורה מלמד.
  2. +
  3. מתודה ()greeting מעודכנת, אשר תהיה יותר רשמית מהמתודה ()greeting הרגילה.
  4. +
+ +

הגדרה של ה-constructor function בשם ()Teacher

+ +

הדבר הראשון שאנחנו צריכים לעשות הוא להגדיר את ה-constructor בשם ()Teacher - הוסיפו את הקוד הבא מתחת לקוד הנוכחי:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

זה נראה מאוד דומה ל-constructor בשם Person, אבל משהו פה שונה, משהו שלא ראינו עד כה - פונקציה בשם ()call.

+ +

פונקציה זו היא בעיקרון מאפשרת לנו לקרוא לפונקציה שהוגדרה במקום אחר, אבל לקרוא לה בהקשר הנוכחי.

+ +

הפרמטר הראשון שהפונקציה הזו מקבלת מכיל את הערך של this שאנחנו נרצה להשתמש בו כאשר אנחנו מריצים את הפונקציה, והפרמטרים האחרים אלו פרמטרים שאמורים להיות מועברים לפונקציה עצמה כאשר היא מופעלת.

+ +

אנחנו רוצים שה-Teacher() constructor יקבל את אותם פרמטרים כמו שה-Person() constructor שהוא יורש ממנו מקבל, ולכן אנחנו מציינים אותם כפרמטרים בתוך ההפעלה של ה-()call.

+ +

השורה האחרונה בתוך ה-constructor פשוט מגדירה property בשם subject אשר ייחודי למורים, שאנשים רגילים מ-()Person לא מקבלים.

+ +

יכלנו גם לרשום זאת כך, ללא שימוש ב-()call:

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

אבל בקוד למעלה זו הגדרה מחדש של ה-properties כחדשים, לא כאלו שיורשים מ- ()Person, אז זה סותר את מה שאנחנו מנסים לעשות - זה לא מוריש אלא זה יוצר חדשים. מה גם שזה לוקח יותר שורות קוד.

+ +

ירושה מ-constructor שלא מקבל פרמטרים

+ +

שימו לב שאם ה-constructor שאנחנו יורשים ממנו לא מקבל את הערכים של ה-property שלו מקבלים מפרמטרים, אז אנחנו לא צריכים לציין אותם כפרמטרים בתוך ה-()call. לדומא, אם היה לנו משהו פשוט כמו זה: :

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

אנחנו יכולים לרשת את ה-properties של ה-width ואת ה-height, באמצעות שימוש בקוד הרשום מטה:

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

שימו לב שאנחנו רק צריכים לציין את ה-this בתוך ה-()call, ללא פרמטרים נוספים, שכן אנחנו לא יכולים יורשים שום דבר מ-()Brick שהוא קיבל דרך פרמטרים.

+ +

קביעת ה-prototype וה-constructor של ()Teacher

+ +

עד עכשיו הכל עובד תקין, אך יש לנו בעיה. הגדרנו אמנם constructor חדש, ויש לו את ה-property בשם prototype, אשר כברירת מחדל מכיל רק הפנייה ל-constructor function עצמה.

+ +

הוא לא מכיל שום מתודות של ה-property בשם prototype של ה-Person constructor. על מנת לראות זאת, הכניסו (Object.getOwnPropertyNames(Teacher.prototype לתוך הקונסולה.

+ +

לאחר מכן הכניסו זאת שוב, והחליפו את המילה Teacher במילה Person. ה-constructor החדש לא יורש את אותן מתודות. על מנת לראות זאת, השוו את הפלט של Person.prototype.greeting והפלט של Teacher.prototype.greeting. אנחנו צריכים לגרום ל-()Teacher לירוש מתודות שמוגדרות ב-prototype של ()Person. איך עושים זאת?

+ +
    +
  1. הוסיפו את הקוד הבא מתחת לקוד שהוספתם לפני כן: +
    Teacher.prototype = Object.create(Person.prototype);
    + כאן ()create מגיע שוב לעזרה. במקרה הזה, אנחנו משתמשים בו על מנת ליצור אובייקט חדש שיהיה הערך של Teacher.prototype. האובייקט החדש הוא בעל Person.prototype כאובייקט ה-prototype שלו, ולכן, הוא יירש ממנו אם וכאשר יצטרך, את כל המתודות שזמינות ב-Person.prototype.
  2. +
  3. אנחנו צריכים לעשות משהו נוסף לפני שנמשיך הלאה. לאחר שהוספנו את השורה הקודמת, ה-property בשם constructor שווה כעת ל- ()Person, מכיוון שאנחנו הרגע הגדרנו את Teacher.prototype אליו. נסו לשמור את הקוד ולהעלות את הדף בדפדפן וראו זאת על ידי הקלדת Teacher.prototype.constructor בקונסולה.
  4. +
  5. זה יכול להיות בעיה, ולכן אנחנו צריכים לתקן זאת. ניתן לעשות זאת באמצעות הקלדת הקוד הבא מתחת לקוד הנוכחי שלנו.: +
    Object.defineProperty(Teacher.prototype, 'constructor', {
    +    value: Teacher,
    +    enumerable: false, // so that it does not appear in 'for in' loop
    +    writable: true });
    +
  6. +
  7. כעת, אם תשמרו ותרעננו את הדף, הקלדת Teacher.prototype.constructor לקונסולה אמורה להחזיר לכם ()Teacher, כפי שרצינו, ובנוסף אנחנו יורשים מ-()Person.
  8. +
+ +

הענקה ל- Teacher() פונקציית/מתודת greeting() חדשה

+ +

אנו רוצים להגדיר פונקציית ()greeting חדשה בתוך ה-Teacher() constructor שלנו.

+ +

הדרך הפשוטה ביותר לעשות זאת היא להגדיר זאת בתוך ה-prototype של ()Teacher - הוסיפו את הקוד הבא מתחת לקוד הנוכחי:

+ +
Teacher.prototype.greeting = function() {
+  var prefix;
+
+  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

מתודה זו מקפיצה את הברכה של המורה, ומשתמשת במילים הנכונות בהתאם למין המורה באמצעות משפטי תנאי שהוגדרו.

+ +

נסו את הדוגמא הבאה:

+ +

כעת שהכנסו את כל הקוד, נסו ליצור אובייקטים חדשים מ-()Teacher באמצעות הכנסת הקוד הבא מתחת לקוד הנוכחי:

+ +
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

כעת, שמרו את הדף ורעננו ונסו לגשת לפרופ׳ והמתודות של האובייקט teacher1 החדש שלנו. לדוגמא:

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

הכל אמור לעבוד כשורה. השורות 1,2,3 ו-6 משתמשות במתודות/פרופ׳ שנורשו מה-Person() constructor שלנו. השורה 4 משתמשת בפרופ׳ שזמין רק באמצעות ה-Teacher() constructor שלנו. שורה 5 הייתה יכולה להיות מורשת מ-()Person, אבל מכיוון של-()Teacher יש את אותה מתודה, אז היא ניגשת למתודה שנמצאת ב-()Teacher.

+ +
+

לתשומת לב: אם נתקלתם בבעיות, נסו להשוות את הקוד שלכם ל קוד הסופי או ראו אותו כ דף אינטרנט.

+
+ +

הטכניקות שראינו עד כה, אינן היחידות ליצירת מחלקות ירושה ב-JavaScript, אבל הן עובדות בסדר, הן נותנות לכם הבנה כיצד להחיל ירושה ב-JavaScript.

+ +

אולי תהיו מעוניינים לבדוק חלק מהאפשרויות החדשות ש-{{glossary("ECMAScript")}} מאפשרת לנו, בצורה ״נקייה״ יותר, באמצעות Classes. אנו נגע בדרך זו בהמשך. שימו לב כי דרך חדשה זו לא תומכת בכל הדפדפנים. כל יתר הדרכים שראינו תומכות בדפדנים ישנים גם כמו IE9 ומוקדם יותר ויש דרכים גם לאפשר תמיכה לדפדפנים ישנים יותר.

+ +

דרך מקובלת היא להשתמש בספריית JavaScript - לפופולריות שביניהן יש סט של פונקציונליות שזמין עבור ביצוע הורשה בצורה פשוטה ומהירה. אחת מהן היא CoffeeScript אשר מאפשרת class ,extends לדוגמא.

+ +

המשך תרגול

+ +

במאמר שלנו בנושא OOP theory section, כללנו גם מחלקת Student באופן עקרוני, אשר יורשת תכונות מ-Person וגם יש לה מתודת ()greeting שונה מזו של Person ומשל Teacher. נסתכל כיצד ה-greeting של התלמידים אמורה להיות וננסה לבנות constructor בשם ()Student משלנו, אשר יורש את כל התכונות מ-()Person ומחיל מתודת ()greeting שונה.

+ +
+

שימו לב: אם אתם נתקלים בבעיות, ראו את הגרסה הסופית או כדף אינטרנט .

+
+ +

סיכום בנושא תכונות האובייקט

+ +

על מנת לסכם, יש לנו בעיקרון שלוש סוגים של property/method לדאוג להם:

+ +
    +
  1. אלו המוגדרים בתוך ה-constructor function וניתנים לאובייקטים שנוצרים באמצעות ה- this. אלו בעיקרון מאוד קלים לאיתור - בתוך הקוד שלנו, אלו התכונות שמוגדרות בתוך ה-constructor באמצעות this.x = x, והם זמינים רק עבור האובייקטים שנוצרים (בדרך כלל נוצרים באמצעות ה-constructor ושימוש במילה השמורה new, לדוגמא: ()var myInstance = new myConstructor.
  2. +
  3. אלו המוגדרים ישרות על ה-constructor עצמם, ואלו זמינות רק על ה-constructor. הם לרוב יהיו זמינים רק אובייקטים מובנים של הדפדפן, ואנחנו יכולים לזהות שהם קשורים באופן ישירות ל-constructor ולא למופע אובייקט. לדוגמא ()Object.keys.
  4. +
  5. ואלו המוגדרים על הפרופ׳ בשם prototype של ה-constructor, אשר נורשים על ידי כל מופעי האובייקט שנוצרים או אובייקטים שיורשים מהם. זה כולל כל תכונה שמוגדרת ב-property בשם prototype של ה-Constructor. לדוגמא: ()myConstructor.prototype.x.
  6. +
+ +

אם אתם לא בטוחים מה זה מה, אל תדאגו, אתם תכירו אותם יותר לעומק במהלך הדרך והמשך הקריירה שלכם ככל שתתמודדו איתם.

+ +

ECMAScript 2015 Classes

+ +

ECMAScript 2015 הציגה מעין סינטקס חדש בשם class syntax ל- JavaScript כדרך חדשה לרשום מחלקות לשימוש חוזר, באמצעות סינטקס פשוט ונקי יותר, אשר דומה יותר ל-classes ב-C++ או ב-Java. בחלק הזה של המאמר אנחנו נמיר את הדוגמאות מלמעלה מ-prototypal inheritance ל-classes, על מנת להראות לכם איך לעשות שימוש-classes.

+ +
+

לתשומת לב: דרך חדשה זו של כתיבת classes נתמכת בכל הדפדפנים המודרניים, אבל זה עדיין חשוב להבין את ה-prototypal inheritance במקרה ותיתקלו בפרוייקט שדורש תמיכה בדפדפן שאינו תומך בסינטקס של classes - הידוע מבין כולם הוא Internet Explorer.

+
+ +

נסתכל על הדוגמא שלנו של Person כתובה בצורת classes:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+
+ +

ההצהרה באמצעות המילה השמורה class מצהירה בעצם בשאנחנו רוצים ליצור class חדשה. בתוך הבלוק הזה שבין {}, אנחנו מגדירים את התכונות של אותה מחלקה:

+ + + +

כעת אנחנו יכולים ליצור מופעי אובייקט חדשים באמצעות שימוש באופרטור new operator, באותה הדרך שעשינו בעבר:

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
+leia.farewell();
+// Leia has left the building. Bye for now
+
+ +
+

לתשומת לב: מאחורי הקלעים, ה-classes שלנו מומרים ל-prototypal Inheritance models - זהו רק syntactic sugar - שזה אומר דרך אחרת מבחינת סינטקס לעשות דבר זהה, אך לא משהו אחר. יחד עם זאת, אין ספק שזו דרך פשוטה יותר להעניק הורשה.

+
+ +

הורשה עם class syntax

+ +

למעלה יצרנו class על מנת לתאר person. יש לנו סט של תכונות שזהות לכל האנשים. בחלק זה אנחנו ניצור את ה-class המיוחד של Teacher, ונגרום לו לירוש מ-Person באמצעות שימוש בסינטקס class החדש. זה נקרא יצירת subclass או ביצוע subclassing.

+ +

על מנת ליצור subclass אנחנו יכולים להשתמש במילה השמורה extends על מנת להגיד ל-JavaScript איזו class אנחנו מתבססים עליה ביצירת ה-class החדשה:

+ +
class Teacher extends Person {
+  constructor(subject, grade) {
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

אך יש קאצ׳ קטן:

+ +

שלא כמו ה-Constructor function, שבהן האופרטור new operator היה עושה את האתחול של המילה this לאובייקט חדש, פה זה לא קורה בצורה אוטומטית בעבור המחלקה שמוגדרת באמצעות המילה extends, כלומר עבור ה-sub-classes.

+ +

ולכן הרצה שלהקוד למעלה יציג לנו שגיאה:

+ +
Uncaught ReferenceError: Must call super constructor in derived class before
+accessing 'this' or returning from derived constructor
+ +

למחלקות משנה, sub-classes, ההגדרה של this בעבור אובייקט חדש, תהיה תמיד תלויה ב-parent class constructor, כלומר ב-constructor function של ה-class שממנה אנחנו מתרחבים (extending).

+ +

כאן, אנחנו מרחיבים את המחלקה Person- המחלקת משנה בשם -Teacher היא בעצם extension - הרחבה - של המחלקה Person. אז עבור Teacher האתחול של this מתבצע על ידי ה-constructor Person.

+ +

על מנת לקרוא ל-constructor ה-parent, אנחנו צריכים להשתמשש באופרטור ()super , כך:

+ +
class Teacher extends Person {
+  constructor(subject, grade) {
+    super();              // Now `this` is initialized by calling the parent constructor.
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

אין שום סיבה שתהיה לנו sub-class אם היא לא יורשת מאפיינים מה-parent class.
+ זה רעיון טוב אז שהאופרטור ()super  גם מקבל ארגומנטים בעבור ה- parent constructor.

+ +

כאשר אנחנו מסתכלים על ה- Person constructor,  אנחנו יכולים לראות שיש לו את הבלוק קוד הבא בתוך ה-constructor שלו:

+ +
 constructor(first, last, age, gender, interests) {
+   this.name = {
+     first,
+     last
+   };
+   this.age = age;
+   this.gender = gender;
+   this.interests = interests;
+} 
+ +

מאחר והאופרטור ()super הוא בעצם ה-parent class constructor, העברה של הארגומנטים הרלוונטיים של המחלקת אם, גם תאתחל את הפרופ׳ במחלקת משנה שלנו, ותירש אותם: 

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // subject and grade are specific to Teacher
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+
+ +

כעת, כשיוצרים מופעי אובייקט של Teacher, אנחנו יכולים לקרוא למתודות ופרופ׳ שהוגדרו גם ב-Teacher וגם ב-Person:

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+
+ +
+

לתשומת לב: אתם יכולים למצוא את הדוגמא ב-GitHub ב- es2015-class-inheritance.html  או כ-דף אינטרנט.

+
+ +

Getters ו- Setters

+ +

יהיו פעמים שאנחנו נרצה לשנות את הערכים של מאפיין בתוך השאנחנו יוצרים או שאנחנו לא נדע מהו הערך הסופי שאותו מאפיין יקבל. אם נסתכל על הדוגמא שלנו Teacher, יכול להיות מצב שאנחנו לא נדע מה הנושא שהמורה מלמד לפני שאנחנו יוצרים אותו או שהנושא יכול להשתנות במהלך התקופה.

+ +

במקרים כאלו אנחנו נוכל להשתמש ב-getters ו-setters.

+ +

נשפר את Teacher class עם  getters ו-setters. המחלקה מתחילה בדיוק כמו שראינו אותה בדוגמא האחרונה.

+ +

 getters ו-setters עובדים בזוגות. getter מחזיר את הערך הנוכחי של משתנה וה-setter הבן זוג שלו משנה את הערך של המשתנה למה שה-setter יגדיר. 

+ +

המחלקה Teacher החדשה תיראה כך:

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // subject and grade are specific to Teacher
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+
+ +

במחלקה למעלה יש לנו getter ו-setter בעבור הפרופ׳ subject. אנחנו משתמשים בסימן _ על מנת ליצור ערך נפרד שבו נאחסכן את השם של הפרופ׳. אם לא נעשה זאת בצורה הזה, אנחנו נקבל שגיאות בכל פעם שנקרא ל-get או ל-set. בנקודה זו: 

+ + + +

הדוגמא למטה מראה את השימוש באפשרויות האלו: 

+ +
// Check the default value
+console.log(snape.subject) // Returns "Dark arts"
+
+// Change the value
+snape.subject="Balloon animals" // Sets _subject to "Balloon animals"
+
+// Check it again and see if it matches the new value
+console.log(snape.subject) // Returns "Balloon animals"
+
+ +
+

לתשומת לב : תוכלו למצוא את es2015-getters-setters.html ב-GitHub, או כדף אינטרנט.

+
+ +

מתי אנחנו נשתמש בהורשה ב-JavaScript?

+ +

רוב הסיכויים שלאחר קריאת המאמר הזה, אתם בטח חושבים לעצמכם ״טוב, זה מסובך מאוד״. אתם צודקים. הורשה ואבי טיפוס הינם חלק מההיבטים המורכבים ביותר של JavaScript, אבל הרבה מעוצמתה של השפה והגמישות שלה מגיע מתוך המבנה של האובייקטים והירושה שלהם, וזה שווה להכיר ולהבין כיצד התהליכים הללו מתרחשים. 

+ +

בדרך מסויימת, אנחנו משתמשים בהורשה כל הזמן. בכל פעם שאנחנו משתמשים במאפיינים שונים של Web API או בפרופ׳/מתודות שהוגדרו באובייקט מובנה של הדפדפן (built-in browser object) על מחרוזות, מערכים וכד׳ אנחנו באופן עקיף משתמשים בירושה. 

+ +

במונחים של שימוש בירושה בקוד שלנו, אנחנו ככל הנראה לא נשתמש בזה באופן תדיר, במיוחד בהתחלה ובפרוייקטים קטנים. זה בזבוז זמן להשתמש באובייקטים וירושה רק לשם השימוש בהם אם אנחנו לא צריכים. אבל ככל שכמות הקוד גדלה, אנחנו ככל הנראה נזהה את הצורך להתחיל להשתמש בכך. םא אנחנו מוצאים את עצמנו מתחילים ליצור מספר אובייקטים שיש להם מאפיינים זהים, אז יצירת אובייקט גנרי אחד אשר יכיל את כל הפונקציונליות המשותפת של אותם אובייקטים ויוריש להם את אותה פונקציונליות תהיה דרך מאוד שימושית ונוחה.

+ +
+

לתשומת לב: לאור הדרך שבה JavaScript עובדת עם שרשרת אבי הטיפוס (prototype chain) וכד׳ - השיתוף של פונקציונליות בין אובייקטים נקרא לרוב delegation - ״האצלה״. אובייקטים מיוחדים ״מאצילים״ פונקציונליות לאובייקטים שנוצרים.  

+
+ +

כאשר אנחנו משתמשים בהורשה, ההמלצה היא שלא יהיו יותר מדי רמות של הורשה, ושתמיד נעקוב איפה אנחנו מגדירים את המתודות והפרופ׳. זה אפשרי להתחיל לכתוב קוד שבאופן זמני משנה את ה-prototypes של האובייקטים המובנים של הדפדפן (built-in browser objects), אבל אין לעשות זאת אלא אם כן יש לנו סיבה מאוד טובה. יותר מדי הורשה יכולה להוביל לבלבול אינסופי ולשגיאות בקוד. 

+ +

באופן אולטמטיבי, אובייקטים הם פשוט תבנית אחרת של שימוש חוזר בקוד, כמו פונקציות ולולאות, עם הכללים והיתרונות שלהם. אם אתם מוצאים את עצמכם יוצרים משתנים ופונקציות הקשורים אחד לשני ואתם רוצים לעקוב ולארוז אותם יחד בצורה מסודרת, אובייקט הוא רעיון טוב. אובייקטים גם שימושיים מאוד כשאנחנו רוצים להעביר ריכוז של מידע ממקום אחד למקום שני. את שני הדברים הללו ניתן להשיג ללא שימוש ב-constructors או ב-inheritance. אם אנחנו צריכים רק מופע אחד של אובייקט, כנראה יהיה עדיף פשוט להשתמש ב-inheritance ואין צורך בירושה.

+ +

אלטרנטיבות להרחבה של שרשרת ההורשה 

+ +

ב-JavaScript, יש מספר דרכים שונות להרחבה של ה-prototype של אובייקט חוץ מאלו שראינו במאמר זה. להרחבה בנושא, ראו את הדף שלנו בנושא Inheritance and the prototype chain.

+ +

לסיכום

+ +

מאמר זה נועד לסקור את יתרת הנושא של OOJS וסינטקס נוסף שאנחנו חושבים שאתם צריכים לדעת. בנקודה זו אתם אמורים להבין את ההיבטים של אובייקטים ב-JavaScript ואת הבסיס של תכנות מונחה עצמים (OOP), אביט טיפוס, שרשרת אבי-טיפוס, הורשה באמצעות אבי-טיפוס, כיצד ליצור מחלקות (classes), מופעי אובייקטים, הוספת מאפיינים למחלקות, יצירת מחלקות משנה שיורשות ממחלקות אחרות ועוד. 

+ +

במאמר הבא אנחנו נגע כיצד לעבוד עם (JavaScript Object Notation (JSON, פורמט מקובל להעברת מידע.

+ +

ראו גם

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/objects/json/index.html b/files/he/learn/javascript/objects/json/index.html new file mode 100644 index 0000000000..5f61b5aa22 --- /dev/null +++ b/files/he/learn/javascript/objects/json/index.html @@ -0,0 +1,346 @@ +--- +title: Working with JSON +slug: Learn/JavaScript/Objects/JSON +translation_of: Learn/JavaScript/Objects/JSON +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects/Object_building_practice", "Learn/JavaScript/Objects")}}
+ +

JavaScript Object Notation (JSON) הוא סטדנרט לפורמט מבוסס-טקסט, המבוסס על הסינטקס של אובייקט ב-ה. הוא לרוב משמש בעבור העברת מידע ביישומי רשת (כלומר העברת מידע משרת לקוח, כך שהמידע יוכל להיות מוצג על גבי דף אינטרנט, או ההפך). אנחנו ניתקל בו לעיתים קרובות מאוד ומאמר זה נועד להעניק לכם כל מה שצריך לדעת על עבודה עם JSON כשאנחנו משתמשים ב-JavaScript, כולל עיבוד שלו כך שאנחנו נוכל לגשת למידע המאוחסן בו וכן ליצור JSON.

+ + + + + + + + + + + + +
ידע מוקדם:Basic computer literacy, a basic understanding of HTML and CSS, familiarity with JavaScript basics (see First steps and Building blocks) and OOJS basics (see Introduction to objects).
מטרה:להבין כיצד לעבוד עם מידע המאוחסן בתוך JSON וכיצד ליצור אובייקטי JSON בעצמנו.
+ +

מה זה JSON?

+ +

{{glossary("JSON")}} הוא פורמט מידע מבוסס-טקסט אשר דומה לסינטקס של אובייקט ב-JavaScript. פורמט זה הוצג על ידי Douglas Crockford. למרות שהוא מאוד דומה ומזכיר סינטקס של object literal ב-JavaScript, ניתן להשתמש בו באופן עצמאי ולא רק ב-JavaScript, וסביבות פיתוח אחרות מכילות אפשרויות לקרוא או לעבד וכן ליצור JSON. 

+ +

JSON מתקיים כמחרוזת - שימושי מאוד כשאנחנו רוצים להעביר מידע ברשת. הוא צריך להיות מומר לאובייקט JavaScript כאשר אנחנו רוצים לגשת לאותו מידע שמאוחסן בתוך ה-JSON. זה לא מורכב או מסובך - JavaScript מעניקה לנו את האובייקט JSON הגלובלי שיש לו מתודות שזמינות עבורו בעבור המרה בין JSON למחרוזת.

+ +
+

לתשומת לב: המרה של מחרוזת לאובייקט נקראת parsing ואילו המרה של אובייקט למחרזות כך שהוא יוכל להיות מועבר ברשת, נקראת stringification.

+
+ +

אובייקט JSON יכול להיות מאוחסן בתוך קובץ נפרד משלו, שהוא בעצם קובץ טקסט עם סיימות של .json ו-{{glossary("MIME type")}} של application/json.

+ +

המבנה של JSON

+ +

כפי שציינו למעלה, JSON הוא מחרוזת, שהמבנה שלה מזכיר מאוד object literal ב-JavaScript. אנחנו יכולים לכלול את אותם סוגי מידע בסיסיים בתוך JSON כפי אנחנו יכולים לכלול אותם בתוך אובייקט של JavaScript - מחרוזות, מספרים, מערכים, בוליאנים ואובייקטים אחרים. זה מאפשר לנו ליצור היררכיית מידע כמו זו לדוגמא:

+ +
{
+  "squadName": "Super hero squad",
+  "homeTown": "Metro City",
+  "formed": 2016,
+  "secretBase": "Super tower",
+  "active": true,
+  "members": [
+    {
+      "name": "Molecule Man",
+      "age": 29,
+      "secretIdentity": "Dan Jukes",
+      "powers": [
+        "Radiation resistance",
+        "Turning tiny",
+        "Radiation blast"
+      ]
+    },
+    {
+      "name": "Madame Uppercut",
+      "age": 39,
+      "secretIdentity": "Jane Wilson",
+      "powers": [
+        "Million tonne punch",
+        "Damage resistance",
+        "Superhuman reflexes"
+      ]
+    },
+    {
+      "name": "Eternal Flame",
+      "age": 1000000,
+      "secretIdentity": "Unknown",
+      "powers": [
+        "Immortality",
+        "Heat Immunity",
+        "Inferno",
+        "Teleportation",
+        "Interdimensional travel"
+      ]
+    }
+  ]
+}
+ +

אם אנחנו נטען את האובייקט הזה לתוך תוכנית JavaScript ונמיר אותו (parse) בתוך משתנה בשם superHeroes לדוגמא, אנחנו אז נוכל לגשת לאותו מידע המאוחסן בו באמצעות שימוש ב-00000 שאנחנו מכירים מהמאמר בנושא אובייקטים - עקרונות יסוד. לדוגמא: 

+ +
superHeroes.homeTown
+superHeroes['active']
+ +

על מנת לגשת למידע בהמשך ההיררכיה, אנחנו פשוט נצטרך ״לקשור״ את שם הפרופ׳ המבוקש והאינדקס הרלוונטי במערך ביחד. לדוגמא, על מנת לגשת ל-9999 השלישי של ה-0000 השני ברשימת ה-9999, נוכל לרשום קוד כזה:

+ +
superHeroes['members'][1]['powers'][2]
+ +
    +
  1. ראשית יש לנו את שם המשתנה  — superHeroes.
  2. +
  3. בתוך המשתנה אנחנו רוצים לגשת לפרופ׳ members, אז אנחנו משתמשים ב- ["members"]
  4. +
  5. members מכיל מערך של אובייקטים. אנחנו רוצים לגשת לאובייקט השני בתוך המערך אז אנחנו נשתמש באינדקס [1]
  6. +
  7. בתוך האובייקט, אנחנו רוצים לגשת לפרופ׳ powers, אז אנחנו נשתמש ב-["powers"]
  8. +
  9. ובתוך הפרופ׳ powers, אנחנו נרצה את הערך השלישי, אז אנחנו נשתמש ב-[2]
  10. +
+ +
+

לתשומת לב: יצרנו את ה-JSON שבדוגמא למעלה בקובץ JSONTest.html וניתן לראותו גם ב-קוד המקור

+ +

מנסו להעלות את הקובץ ולגשת למידע בתוך המשתנה באמצעות הקונסולה בדפדפן. 

+
+ +

מערכים כ-JSON

+ +

למעלה ציינו שטקסט ב-JSON דומה בעקרון לאובייקט ב-JavaScript, וזה דיי נכון. הסיבה שאנחנו אומרים ״דומה״ ולא ״זהה״, מכיוון שמערך ב-JavaScript הוא בעצם JSON תקין:

+ +
[
+  {
+    "name": "Molecule Man",
+    "age": 29,
+    "secretIdentity": "Dan Jukes",
+    "powers": [
+      "Radiation resistance",
+      "Turning tiny",
+      "Radiation blast"
+    ]
+  },
+  {
+    "name": "Madame Uppercut",
+    "age": 39,
+    "secretIdentity": "Jane Wilson",
+    "powers": [
+      "Million tonne punch",
+      "Damage resistance",
+      "Superhuman reflexes"
+    ]
+  }
+]
+ +

הטקסט למעלה הוא JSON תקין לחלוטין. אנחנו פשוט נצטרך לגשת לאיברים במערך (בגרסה המעובדת שלו), באמצעות אינדקס של מערך/

+ +

הערות נוספות

+ + + +

למידה עצמאית: עבודה עם JSON

+ +

כעת נלמד באמצעות דוגמא כיצד ניתן לעשות שימוש במידע JSON באתר אינטרנט.

+ +

התחלה

+ +

על מנת להתחיל, עשו עותקים מקודמיים של heroes.html ושל style.css . הקובץ האחרון מכיל CSS פשוט לעיצוב הדף שלנו והראשון מכיל גוף HTML פשוט:

+ +
<header>
+</header>
+
+<section>
+</section>
+ +

בנוסף, הקובץ הראשון מכיל אלמנט {{HTMLElement("script")}} פשוט שיכיל את קוד ה-JavaScript שנכתוב בתרגיל זה. כעת, קובץ זה מכיל שתי שורות, אשר מכילות הפניה לאלמנט {{HTMLElement("header")}} ולאלמנט {{HTMLElement("section")}} ומאחסן הפניות אלו בתוך משתנים:

+ +
var header = document.querySelector('header');
+var section = document.querySelector('section');
+ +

מידע ה-JSON זמין ב-GitHub בכתובת: https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json.

+ +

בתרגיל אנחנו נעלה את המידע לתוך הדף שלנו, באמצעות שימוש במניפולציות על ה-DOM, כך שהדף שלנו ייראה כך:

+ +

+ +

קבלת ה-JSON

+ +

על מנת להשיג את ה-JSON, אנחנו נשתמש ב-API שנקרא {{domxref("XMLHttpRequest")}} (לרוב נקרא XHR). זהו אובייקט JavaScript שימושי מאוד, אשר מאפשר לנו לבצע בקשות רשת על מנת לקבל משאבים מהשרת באמצעות JavaScript (כמו תמונות, טקסט, JSON, ואפילו קוד HTML), כלומר, אנחנו יכולים לעדכן חלקים מתוך התוכן שלנו, מבלי לטעון מחדש את הדף כולו. אפשרות זו איפשה לדפי אינטרנט להיות הרבה יותר רספונסיביים. אנחנו נלמד בהרחבה על נושא זה בהמשך. 

+ +
    +
  1. על מנת להתחיל, אנחנו הולכים לאחסן את ה-URL של ה-JSON שאנחנו נרצה לקבל בתוך משתנה. הוסיפו את הקוד הבא בתחתית הקוד של JavaScript שברשותכם:
  2. +
  3. +
    var requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
    +
  4. +
  5. על מנת ליצור בקשה, אנחנו צריכים ליצור אובייקט בקשה חדשה מה-constructor בשם XMLHttpRequest, באמצעות שימוש במילה השמורה new. הוסיפות את הקוד הבא מתחת לשורה האחרונה: +
    var request = new XMLHttpRequest();
    +
  6. +
  7. כעת, אנחנו צריכים לפתוח בקשה חדשה לאובייקט בקשה שיצרנו באמצעות המתודה ()open. הוסיפו את הקוד הבא: +
    request.open('GET', requestURL);
    + +

    מתודה זו מקבל לפחות שני פרמטרים - וניתן להזין עוד פרמטרים אופציונליים. הפרמטרים שאנחנו חייבים לציין בעבור הדוגמא הזו הם:

    + +
      +
    • סוג המתודה של HTTP שאנחנו נרצה לבצע בבקשת השרת - במקרה שלנו זו תהיה GET, שכן אנחנו רוצים לקבל מידע כלשהו. 
    • +
    • ה-URL שאנחנו נבצע אליו בקשה - זהו ה-URL שבו מאוחסן קובץ ה-JSON שלנו. 
    • +
    +
  8. +
  9. כעת, הוסיפו את השורות הבאות  - בשורות אלו אנחנו מגדירים את ה-responseType ל-JSON, כך שה-XHR יודע שהשרת יחזיר לנו JSON, ושמאחורי הקלעים תבוצע לו המרה לאובייקט JavaScript. בשורה השנייה אנחנו שולחים את הבקשה באמצעות מתודת ()send: +
    request.responseType = 'json';
    +request.send();
    +
  10. +
  11. החלק האחרון של הקוד הרשום להלן הוא בעצם המתנה לקבלת תגובה מהשרת, וביצוע פעולה איתה. הוסיפו את הקוד הבא מתחת לקוד הנוכחי שלכם: +
    request.onload = function() {
    +  var superHeroes = request.response;
    +  populateHeader(superHeroes);
    +  showHeroes(superHeroes);
    +}
    +
  12. +
+ +

כאן אנחנו מאחסנים את התגובה לבקשה לשלנו (הזמינה בפרופ׳ response ) בתוך משתנה שנקרא superHeroes. המשתנה עכשיו מכיל את האובייקט JavaScript שמבוסס על ה-JSON. לאחר מכן אנחנו מעבירים את אותו אובייקט כארגומנט לשתי קריאות פונקציות - הראשונה תמלא את ה-<header> עם המידע הנכון, ואילו השניה תיצור לנו כרטיס מידע בעבור כל ״גיבור״ בקבוצה ותכניס את הכרטיס הזה לתוך <section>

+ +

עטפנו את הקוד הזה בתוך מטפל אירוע - event handler - שירוץ ברגע שהאירוע load נוצר על האובייקט request (ראו onload) - זה מכיוון שהאירוע load נוצר כאשר התגובה מהשרת הוחזרה בהצלחה. ביצוע של דבר זה מבטיח לנו שה-request.response יהיה זמין בוודאות כשנרצה לעשות משהו איתו.

+ +

מילוי של מידע ב-header

+ +

כעת, כשהשגנו את המידע ב-JSON והמרנו אותו לאובייקט JavaScript, אנחנו יכולים לעשות בו שימוש באמצעות כתיבה של הפונקציות שציינו למעלה. ראשית, הוסיפו את הפונקציות הרשומות מטה מתחת לקוד הנוכחי שלכם: 

+ +
function populateHeader(jsonObj) {
+  var myH1 = document.createElement('h1');
+  myH1.textContent = jsonObj['squadName'];
+  header.appendChild(myH1);
+
+  var myPara = document.createElement('p');
+  myPara.textContent = 'Hometown: ' + jsonObj['homeTown'] + ' // Formed: ' + jsonObj['formed'];
+  header.appendChild(myPara);
+}
+ +

אנחנו קראנו לפרמטר בשם jsonObj, על מנת להזכיר לעצמנו שזהו בעצם אובייקט JavaScript שמקורו ב-JSON. ראשית, יצרנו אלמנט {{HTMLElement("h1")}} עם ()createElement, והשמנו את ה-textContent שיהיה שווה לפרופ׳ squadName של האובייקט, ולאחר מכן שייכנו אותו ל-header באמצעות ()appendChild

+ +

לאחר מכן עשינו פעולה דומה בעבור הפסקה - p: יצרנו אותה, השמנו את ה-textContent שלה ולאחר מכן הוספנו אותה ל-header. ההבדל היחיד הוא שהטקסט שלה הוא שרשור של מחרוזות המכילות את הפרופ׳ homeTown ו-formed של האובייקט jsonObj.

+ +

יצירה של כרטיסי המידע ״hero״

+ +

כעת, הוסיפו את הפונקציה הבאה מתחת לקוד הנוכחי, אשריוצרץ ומצגיה את כרטיסי המידע של ה-hero: 

+ +
function showHeroes(jsonObj) {
+  var heroes = jsonObj['members'];
+
+  for (var i = 0; i < heroes.length; i++) {
+    var myArticle = document.createElement('article');
+    var myH2 = document.createElement('h2');
+    var myPara1 = document.createElement('p');
+    var myPara2 = document.createElement('p');
+    var myPara3 = document.createElement('p');
+    var myList = document.createElement('ul');
+
+    myH2.textContent = heroes[i].name;
+    myPara1.textContent = 'Secret identity: ' + heroes[i].secretIdentity;
+    myPara2.textContent = 'Age: ' + heroes[i].age;
+    myPara3.textContent = 'Superpowers:';
+
+    var superPowers = heroes[i].powers;
+    for (var j = 0; j < superPowers.length; j++) {
+      var listItem = document.createElement('li');
+      listItem.textContent = superPowers[j];
+      myList.appendChild(listItem);
+    }
+
+    myArticle.appendChild(myH2);
+    myArticle.appendChild(myPara1);
+    myArticle.appendChild(myPara2);
+    myArticle.appendChild(myPara3);
+    myArticle.appendChild(myList);
+
+    section.appendChild(myArticle);
+  }
+}
+ +

לשם התחלה, אחסנו את הפרופ׳ members של אובייקט ה-JavaScript בתוך משתנה חדש. מערך זה מכיל מספר אובייקטים שמכילים את המידע בעבור כל hero. 

+ +

לאחר מכן, השתמשנו בלולאת for על מנת לעבור על כל אובייקט בתוך מערך זה. בעבור כל אובייקט במערך, עשינו את הדברים הבאים: 

+ +
    +
  1. יצרנו מספר אלמנטים חדשים: <article><h2>, שלוש <p>, ו-<ul>.
  2. +
  3. קבענו ש- <h2> יכיל את הערך הנוכחי של ה-name של ה-hero.
  4. +
  5. מילאנו את שלושת ה-p עם secretIdentity, age ושורה עם הטקסט "Superpowers:".
  6. +
  7. אחסנו את הפרופ׳ powers בתוך משתנה חדש שנקרא superPowers - זה מכיל מערך שמציג את ה-superpowers הנוכחיים.
  8. +
  9. השתמשנו בלולאת for שוב על מנת לעבור על כל ה-superpowers הנוכחיים של hero - בעבור כל אחד יצרנו אלמנט <li>, והכנסו את ה-superpower לתוכו ולאחר מכן שמנו את ה-listItem בתוך <ul> באמצעות ()appendChild.
  10. +
  11. לבסוף, שייכנו את <h2>, <p> ו-<ul> לתוך <article> (myArticle) ולאחר מכן שייכנו את <article> לתוך <section>. הסדר שבו דברים משוייכים הוא חשוב שכן כך הם יוצגו על גבי הדף.
  12. +
+ +
+

לתשומת לב: אם אתם נתקלים בבבעיה, ראו את הקוד המקור ב-heroes-finished.html או כ-דף אינטרנט

+
+ +
+

לתשומת לב:  אם אתם מתקשים בעניין ה-dot/bracket notation שאנו משתמשים כדי לגשת לאובייקט JavaScript, אולי יעזור לכם לפתוח את הקובץ superheroes.json בלשונית אחרת או בעורך הקוד שלכם, ולהסתכל עליו תוך כדי מעבר על הקוד שלנו. בנוסף, שקלו לעשות חזרה על המאמר שלנו בנושא אובייקטים - עקרונות יסוד.

+
+ +

המרה בין אובייקטים וטקסט

+ +

הדוגמא למעלה הייתה יחסי פשוטה במונחים של לגשת לאובייקט JavaScript, מכיוון שקבענו שבקשת ה-XHR שלנו תמיר את התגובת JSON ישירות לאובייקט JavaScript באמצעות: 

+ +
request.responseType = 'json';
+ +

לפעמים אנחנו נקבל מחרוזת JSON לא מעובדת, ואנחנו נצטרך להמיר אותה לאובייקט בעצמנו. וכאשר אנחנו נרצה לשלוח אובייקט JavaScript ברשת, אנחנו נצטרך להמיר אותו ל-JSON (מחרוזת) לפני השליחה שלו. למזלנו, שתי בעיות אלו כל כך נפוצות, שקיים אובייקט בשם JSON הזמין בכל הדפדפנים, המכיל שתי מתודות: 

+ + + +

אתם יכולים לראות את הראשון בפעולה בקובץ -heroes-finished-json-parse.html - הוא עושה בדיוק את אותו הדבר כמו בדוגמא שבנינו קודם, רק שאנחנו קבענו שה-XHR יחזיר טקסט JSON גולמ, ואז השתמשנו ב-()parse על מנת להמיר אותו לאובייקט JavaScript זה הקוד הרלוונטי:

+ +
request.open('GET', requestURL);
+request.responseType = 'text'; // now we're getting a string!
+request.send();
+
+request.onload = function() {
+  var superHeroesText = request.response; // get the string from the response
+  var superHeroes = JSON.parse(superHeroesText); // convert it to an object
+  populateHeader(superHeroes);
+  showHeroes(superHeroes);
+}
+ +

כפי שאתם יכולים לנחש, ()stringify עובד בדרך ההפוכה. נסו להזין את השורות הבאות לקונסולה:

+ +
var myJSON = { "name": "Chris", "age": "38" };
+myJSON
+var myString = JSON.stringify(myJSON);
+myString
+ +

כאן יצרנו אובייקט JavaScript ואז המרנו אותו למחרוזת JSON באמצעות ()stringify - ושמרנו את הערך שחזר במשתנה חדש. 

+ +

לסיכום

+ +

במאמר זה, ניסינו להעניק לכם מדריך פשוט כיצד להשתמש ב-JSON בתוכנויות שלנו, כולל כיצד ליצור ולהמיר JSON, וכיצד לגשת למידע המאוחסן בתוכו. במאמר הבא אנחנו נסתכל על פרקטיקות ביצירת אובייקטים ב-JavaScript.

+ +

ראו גם

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects/Object_building_practice", "Learn/JavaScript/Objects")}}

+ +

In this module

+ + diff --git a/files/he/learn/javascript/objects/object-oriented_js/index.html b/files/he/learn/javascript/objects/object-oriented_js/index.html new file mode 100644 index 0000000000..63f83aa94d --- /dev/null +++ b/files/he/learn/javascript/objects/object-oriented_js/index.html @@ -0,0 +1,275 @@ +--- +title: Object-oriented JavaScript for beginners +slug: Learn/JavaScript/Objects/Object-oriented_JS +translation_of: Learn/JavaScript/Objects/Object-oriented_JS +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
+ +

לאחר שסיימנו עם עקרונות היסוד, כעת אנחנו נתמקד על- object-oriented JavaScript (OOJS) — מאמר זה נועד להעניק מבט בסיסי של תיאוריית object-oriented programming (OOP) ולאחר מכן נחקור כיצד JavaScript מחקה (מלשון חיקוי) מחלקות אובייקטים באמצעות פנקציה בנאית (constructor functions), וכיצד אנחנו יכולים ליצור ״מופעי״ אובייקטים

+ + + + + + + + + + + + +
ידע מוקדם:Basic computer literacy, a basic understanding of HTML and CSS, familiarity with JavaScript basics (see First steps and Building blocks) and OOJS basics (see Introduction to objects).
מטרה:להבין את בסיס התיאוריה שעומדת מאחורי תכנות מונחה עצמים, וכיצד זה מתקשר ל- JavaScript ("everything is an object"), ולהבין כיצד ליצור constructors ו- object instances.
+ +

Object-oriented programming — הבסיס

+ +

לשם ההצלחה, ננסה ללהעניק לכם נקודת מבט מופשטת, מלמעלה, של מה זה בעצם תכנות מונחה עצמים - Object-oriented programming (OOP). אנחנו אומרים מופשט מכיוון ש-0000 יכול להיות מורכב מאוד ופירוט של כל התיאוריה רוב הסיכויים תבלבל אתכם. הרעיון הבסיסי של OOP הוא שאנחנו משתמשים באובייקטים על מנת לדמות דברים אמיתיים מהעולם האמיתי שנרצה לכלול בתוך התוכנית שלנו, ו/או להעניק דרך פשוטה לגשת לפונקציונליות מסויימת, שבדרכים אחרות היה פשוט בלתי אפשרי או מסובך מאוד.

+ +

אובייקטים יכולים להכיל מידע וקוד הקשורים זה לזה, אשר מייצדים מידע אודות הדבר שנאחנו מנסים לדמות, ופונקציונליות או התנהגות שאנחנו רוצים שיהיה לאובייקט שלנו. המידע של האובייקט, ולרוב גם הפונקציות שלו, יכולות להיות מאוחסות בצורה מסודרת - (המילה המקצועית היא encapsulated - בתוך האובייקט (וניתן לתת להם שמות שיהוו הפניה לאותם ערכים, ולפעמים זה נקרא namespace), וכך נהיה פשוט לבנות ולגשת אליהם. אובייקטים גם לרוב משמשים כמאחסני מידע שאנחנו יכולים לשלוח בקלות ברשת.

+ +

הגדרה של טמפלייט של אובייקט

+ +

ננסה לחשוב על תוכנית פשוטה אשר מציגה מידע על תלמידים ועל מורים בבית ספר. אנחנו נסתכל על דוגמא זו ועל תיאוריית oop באופן כללי, מבלי להתייחס לשפת תכנות מסויימת.

+ +

על מנת להתחיל, אנחנו נחזור לאובייקט Person מהמאמר הקוד אובייקטים - עקרונות יסוד, אשר מגדיר את המידע והפונקציונליות הגנריות של בן אדם, של person. יש הרבה דברים שאנחנו יכולים לדעת על אדם (כתובת, גובה, מידת נעליים וכד׳), אבל במקרה שלנו אנחנו מעונינים להראות את השם, גיל, מין תחומי עניין שלהם, ואנחנו רוצים שתאפשר לנו לכתוב הקדמה קצרה עליהם, בהתבסס על המידע שיש לנו ולאפשר לאותו אובייקט להגיד שלום בצורה כלשהי. זה בעצם נקרה abstraction — יצירת מודל פשוט של משהו מורכב יותר אשר מייצג את האספקטיים החשובים ביותר בדרך כזו שניתן לעבוד איתה בתוכנית שלנו ולצרכי התוכנית שלנו.

+ +

+ +

יצירת אובייקטים

+ +

מהמחלקה שלנו אנחנו יכולים ליצור מופעי אובייקט - object instances — אלו בעצם אובייקטים המכילים את המידע והפונקציונליות שהוגדרו ב-מחלקה. מהמחלקה Person שלנו, אנחנו יכולים ליצור כמה אנשים בפועל:

+ +

+ +

כאשר נוצר מופע אובייקט ממחלקה, ה- constructor function של אותה מחלקה מורץ על מנת ליצור את האובייקט החדש. תהליך זה של יצירת מופע אובייקט ממחלקה נקרא instantiation — ומופע האובייקט שנוצר, האובייקט שנוצר, אנו אומרים שהוא instantiated מהמחלקה

+ +

Specialist classes

+ +

במקרה שלנו אנחנו לא רוצים אנשים שנראים אותו דבר - אנחנו רוצים מורים וסטודנטים, אשר הם בעלי מאפיינים ספיצפיים של בני אדם. ב-OOP, אנחנו יכולים ליצור מחלקות חדשות בהתבסס על מחלקות קיימות - אלו נקראים - child classes והם יכולים לרשת, ירושה  של מידע ומאפיינים של  קלאס האב שלהם, כך שאנחנו יכולים להשתמש בפונקציונליות שמשותפת לכל האובייקטים באמצעות אותה הורשה, מבלי שנצטרף לכתוב אותה מחדש בכל פעם. כאשר אנחנו צריכים להעניק תכונה או פונקציונליות פרטנית לאובייקט, אנחנו יכולים להגדיר אותם על אותו אובייקט באופן פרטני.

+ +

+ +

זהו מאוד שימושי - מורים וסטודנטים חולקים מאפיינים בסיסיים כמו שם, גיל, מין כך שזה יהיה נוח אם נוכל להגדיר את המאפיינים הללו פעם אחת בלבד. אנחנו גם יכולים להגדיר מאפיינים דומים באופן נפרד, במחלקות שונות, כך שכל מאפיין שכזה יהיה ב-namespace אחר. לדוגמא,הברה של סטודנט יכולה להיות שונה מהברכה של מורה. כך לדוגמא, הברכה של סטודנט תהיה "Yo, I'm [firstName]" (e.g Yo, I'm Sam), בעוד הברכה של מורה תהיה "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (e.g Hello, My name is Mr Griffiths, and I teach Chemistry).

+ +
+

לתשומת לב: המילה הרשמית לאפשרות לשכפל אובייקטים ולהחיל עליהם את אותה פונקציונליות נקראת polymorphism. רק למקרה ותהיתם

+
+ +

אנחנו יכולים ליצור מופעי אובייקטים חדשים מה-child classes. לדוגמא:

+ +

+ +

בהמשך המאמר, אנחנו נסתכל כיצד תיאורית oop באה לידי ביטוי ב-JavaScript.

+ +

Constructors ו- object instances

+ +

JavaScript  משתמש בפונקציות מיוחדות שנקראות constructor functions על מנת להגדיר ול initialize אובייקטים ואת המאפיינים שלהם. זה שימושי מאוד מכיוון שאנחנו ניתקל בסיטואציות שבהן אנחנו לא יודעים כמה אובייקטים אנחנו נרצה או נצטרף ליצור. constructors מאפשרים לנו ליצור אובייקטים כמה שנרצה בדרך אפקטיבית, וך צירוף המידע והפונקציות שלהם ככל שנרצה.

+ +

נחקור כעת יצירה של מחלקות באמצעות constructors ויצירה של מופעי אובייקט מהם ב-JavaScript. ראשית, אנא צרו עותק של oojs.html

+ +

דוגמא פשוטה

+ +
    +
  1. נתחיל בכך שנסתכל כיצד אנחנו יכולים להגדיר person באמצעות פונקציה רגילה. הוסיפו את הפונקציה הזו לתוך אלמנט script : + +
    function createNewPerson(name) {
    +  var obj = {};
    +  obj.name = name;
    +  obj.greeting = function() {
    +    alert('Hi! I\'m ' + obj.name + '.');
    +  };
    +  return obj;
    +}
    +
  2. +
  3. כעת אנחנו יכולים ליצור person חדש באמצעות קריאה לפונקציה זו - נסו להזין אתהקוד הבא בקונסולה: +
    var salva = createNewPerson('Salva');
    +salva.name;
    +salva.greeting();
    + זה עובד בצורה טובה, אבל קצת מפותלת. אם אנחנו יודעים שאנחנו רוצים ליצור אובייקט, מדוע אנחנו צריכים ליצור במפורש אובייקט ריק בהתחלה ולהחזיר אותו? למרבה המזל, javascript מעניקה לנו קיצור שימושי פונקציה בנאית- constructor functions - בואו ניצור אחת יחד:
  4. +
  5. החליפו את הפונקציה הקודמת עם הפונקציה הנוכחית: +
    function Person(name) {
    +  this.name = name;
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  };
    +}
    +
  6. +
+ +

ה-constructor function היא הגרסה של javascript למחלקות. שימו לב שיש לה את כל המאפיינים שאנחנו נצפה לראות בפונקציה, למרות שהיא לא מחזירה שום דבר או יוצרת אובייקט באופן מפורש - היא בסך הכל מגדירה מפתחות ומתודות. אנו משתמשים במילה השמורה this — זה בעיקרון אומר שבכל פעם שנוצר מופע אובייקט, ה- name property יהיה שווה לערך של name value שהועבר לתוך ה- constructor, והמתודה greeting() תשתמש באותם ערכים שהועברו ל- constructor.

+ +
+

שימו לב:שם של constructor function בדרך כלל מתחיל עם אות גדולה - זו מוסכמה מקובלת על מנת לזהות בקלותconstructor functions קוד שלנו.

+
+ +

אז כיצד אנחנו קוראים ל-constructor function על מנת ליצור אובייקטים?

+ +
    +
  1. הוסיפו את השורות הבאות מתחת לקוד הקודם שהוספתם: +
    var person1 = new Person('Bob');
    +var person2 = new Person('Sarah');
    +
  2. +
  3. שמרו ורעננו את הדף ולאחר מכן הזינו את השורות הבאות בקונסולה: +
    person1.name
    +person1.greeting()
    +person2.name
    +person2.greeting()
    +
  4. +
+ +

אתם תראו שאנחנו יצרנו שני אובייקטים חדשים, כאשר כל אחד מהם נשמר תחת שם אחר — כאשר אנחנו רוצים לגשת למפתחות ולמתודות שלהם, אנחנו צריכים להתחיל עם קריאה ל person1 או ל- person2; הפונקציונליות המאוחסנת בהם ארוזה בצורה מסודרת כך שהיא תתנגש עם פונקציונליות אחרת. יחד עם זאת, יש להם את אותו מפתח name  ואת אותה מתודת greeting() שזמינה עבורה. שימו לב שהם משתמשים בערך של ה - name שניתן להם כאשר הם נוצרו. זו אחת מהסיבות מדוע זה מאוד חשוב להתשמש במילה this, כך שהם ישתמשו בערכים שלהם ולא בערכים אחרים.

+ +

נסתכל שוב על הקריאות ל- constructor שוב:

+ +
var person1 = new Person('Bob');
+var person2 = new Person('Sarah');
+ +

בכל אחת מהקריאות, המילה השמורה new משמשת על מנת להגיד לדפדפן שאנחנו רוצים ליצוא מופע אובייקט חדש, ולאחר מכן יש את שם הפונקציה עם הפרמטרים הרלוונטיים בתוך הסוגריים והתוצאה של אותה פונקציה, של אותה constructor function, מאוחסנת בתוך משתנה - זה דומה מאוד לאיך שפונקציה רגילה מופעלת או לאיך שקוראים לפונקציה רגילה. כל מופע אובייקט שכזה נוצר בהתאם להגדרה של ה-constructor function:

+ +
function Person(name) {
+  this.name = name;
+  this.greeting = function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  };
+}
+ +

לאחר שהאובייקטים החדשים נוצרו, המשתנים person1 ו- person2 מכילים את האובייקטים הבאים:

+ +
{
+  name: 'Bob',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+{
+  name: 'Sarah',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

שימו לב שכאשר אנחנו קוראים ל-constructor function שלנו, אנחנו מגדירים את המתודה greeting() בכל פעם - משהו שאינו אידיאלי. על מנת להימנע מכך, אנחנו נגדיר פונקציות שהוא בתבנית האב - אנו נגע בנושא זה בהמשך.

+ +

יצירת ה-constructor הסופי שלנו

+ +

הדוגמא שהסברנו למעלה הייתה דוגמא פשוטה שנועדה רק לסבר לנו את האוזן. כעת, ניצור את ה-constructor function code>Person() הסופי שלנו.

+ +
    +
  1. הסירו את הקוד שהכנסתם עד עכשיו, והוסיפו את ה - constructor הזה - הוא בעצם אותו דבר כמו בדוגמא למעלה, רק קצת יותר מורכב: : +
    function Person(first, last, age, gender, interests) {
    +  this.name = {
    +     first : first,
    +     last : last
    +  };
    +  this.age = age;
    +  this.gender = gender;
    +  this.interests = interests;
    +  this.bio = function() {
    +    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    +  };
    +  this.greeting = function() {
    +    alert('Hi! I\'m ' + this.name.first + '.');
    +  };
    +}
    +
  2. +
  3. כעת, הוסיפו את הקוד הבא מתחת, על מנת ליצור מופעי אובייקט מ-constructor: +
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    +
  4. +
+ +

אתם תראו שאנחנו יכולים לגשת לכל אחד מהמפתחות והמתודות בדיוק כפי שיכלנו לפני כן - נסו להזין את הקוד הבא בקונסולה:

+ +
person1['age']
+person1.interests[1]
+person1.bio()
+// etc.
+ +
+

לתשומת לב: אם אתם נתקלים בבעיות, נסו להשוות את הקוד שלכם לקוד שלנו - oojs-class-finished.html או ראו את דף האינטרנט.

+
+ +

תרגולים נוספים

+ +

על מנת להתחיל, הוסיפו כמה אובייקטים נוספים משלכם, ונסו לקבל או לקבוע את המידע שלהם.

+ +

בנוסף, יש מספר בעיות עם המתודה bio() — הפלט שלה תמיד יציג פלט בלשון זכר, למרות ש-person הוא נקבה. והמתמודה גם תציג רק שני תחומי עניין, אפילו אם בתוך המערך interests יש יותר תחומי עניין. האם אתם יכולים לתקן את זה ב-constructor שלנו? אם יכולים לשים כל קוד בתוך הconstructor (אתם כנראה תצרו משפטי תנאי ולולאה). חשבו כיצד המשפטים אמורים להיראות בצורה שונה כאשר מדובר במין שונה ומה עושים כאשר מספר תחומי העניין הוא 1, 2 או יותר מ-2.

+ +
+

לתשומת לב: אם אתם נתקעים, ראו את התשובה ב- GitHub repo או בדף היאנטרנט) — אך נסו בעצמכם לפני כן

+
+ +

דרכים נוספות ליצירת מופעי אובייקט - ליצירת אובייקטים חדשים

+ +

עד עכשיו ראינו שתי דרכים ליצירת מופעי אובייקט — הצהרה על אובייקט ליטראלי, ובאמצעות שימוש ב - constructor function (כפי שראינו למעלה).

+ +

ישנם דרכים נוספים ואנחנו נרצה להציג לכם אותם על מנת שתכירו אותם במידה ותיתקלו בהם בהמשך הדרך

+ +

The Object() constructor

+ +

ראשית, אתם יכולים לעשות שימוש ב constructor Object() על מנת ליצור אובייקט חדש. כן, גם אובייקטים גנריים יש להם constructor, אשר יוצר אובייקט ריק.

+ +
    +
  1. נסו להזין את הקוד הבא לתוך הקונסולה: +
    var person1 = new Object();
    +
  2. +
  3. קוד זה מאחסן בעצם אובייקט ריק בתוך המשתנה person1 .אנחנו יכולים להוסיף מפתחות ומתודות לאובייקט זה באמצעות שימוש ב- dot או dot; נסו להזין את הדוגמאות הבאות בקונסולה: +
    person1.name = 'Chris';
    +person1['age'] = 38;
    +person1.greeting = function() {
    +  alert('Hi! I\'m ' + this.name + '.');
    +};
    +
  4. +
  5. אתם גם יכולים להעביר object literal ל- Object() constructor כפרמטר, על מנת למלא מראש את הפרופ׳ והמתודות: נסו להזין את הקוד הבא בקונסולה: +
    var person1 = new Object({
    +  name: 'Chris',
    +  age: 38,
    +  greeting: function() {
    +    alert('Hi! I\'m ' + this.name + '.');
    +  }
    +});
    +
  6. +
+ +

שימוש במתודת create()

+ +

Constructors יכולים לעזור להעניק סדר לקוד שלנו - אנחנו יכולים ליצור את הconstructors במקום אחד ואז ליצור מופעים שלהם כמה שנרצה, במקום אחר, וזה יהיה ברור מאיפה הם נוצרוץ

+ +

יחד עם זאת, ישנם מפתחים אשר מעדיפים ליצור מופעי אובייקט מבלי ליצור תחילה constructors, במיוחד אם אתם יוצרים רק מספר קטם של מופעי אובייקט. ל-JavaScript יש מתודה מובנת שנקראת create() אשר מאפשרת לנו לעשות זאת. עם מתודה זו, אנחנו יכולים ליצור אובייקט חדש, בהתבסס על אובייקט קיים.

+ +
    +
  1. העלו את התרגיל שסיימתם בתרגול הקודם לדפדפן והוסיפו את הקוד הבא לקונסולה: : +
    var person2 = Object.create(person1);
    +
  2. +
  3. כעת נסו להזין את הקוד הבא בקונסולה: +
    person2.name
    +person2.greeting()
    +
  4. +
+ +

אתם תראו ש-person2 נוצר בהתבסס על person1 - יש לו את אותם פרופ׳ ומתודות שזמינות עבורו.

+ +

הגבלה אחת שיש בשימוש עם מתודת create() היא שדפדפן IE8 does לא תומך בה. אז constructors יכולים להיות דרך רלוונטם במידה ואתם צריכים לתמוך בגרסה זו.

+ +

אנו נחקור את המתודה create() בפרטי פרטים בהמשך הקורס.

+ +

לסיכום

+ +

מאמר זה העניק לכם מבט מופשט על תיאוריית פיתוח מונחה עצמים - זה לא הסיפור המלא כמובן, אבל זה נועד לתת לכם את הרעיון שאיתו אנחנו ממשיכים. בנוסף, התחלנו לגלות כיצד אנחנו יכולים ליצור מופעי אובייקט, או אובייקטים חדשים, בדרכים שונות.

+ +

במאמר הבא אנחנו נחקור את נושא JavaScript object prototypes.

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

+ +

במודול זה

+ + diff --git a/files/he/learn/javascript/objects/object_prototypes/index.html b/files/he/learn/javascript/objects/object_prototypes/index.html new file mode 100644 index 0000000000..0afe0ef67d --- /dev/null +++ b/files/he/learn/javascript/objects/object_prototypes/index.html @@ -0,0 +1,283 @@ +--- +title: Object prototypes +slug: Learn/JavaScript/Objects/Object_prototypes +translation_of: Learn/JavaScript/Objects/Object_prototypes +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects")}}
+ +

Prototypes - אבי טיפוס, זה המנגנון שבאמצעותו אובייקטים של javascript יורשים מאפיינים אחד מהשני. במאמר זה אנחנו נסביר כיצד שרשראות אבי טיפוס - (prototype chains) עובדות ונסתכל כיצד מאפיין (property) בשם prototype יכול לשמש עבור הוספת מתודות ל-constructors קיימים.

+ + + + + + + + + + + + +
ידע מוקדם: +

הבנה של פונקציות ב-JavaScript, הכרות עם הבסיס של JavaScript (ראו את המאמר צעדים ראשונים ב-צעדים ראשונים ב-JavaScript ו-אבני הבניין של JavaScript) וכן הבסיס של תכנות מונחה עצמית ב-JavaScript (ראו אובייקטים - עקרונות יסוד).

+
מטרה:הבנה של JavaScript object prototypes, כיצד שרשראות prototype עובדות, וכיצד להוסיף מתודות חדשות לתוך פרופ׳ בשם prototype .
+ +

שפה מבוססת אב-טיפוס?

+ +

JavaScript לעיתים קרובות מתוארת כשפה מונחית אבי-טיפוס (prototype-based language) - על מנת לאפשר הורשה, לאובייקטים יכול להיות אובייקט אב-טיפוס (prototype object), אשר מתפקד כאובייקט תבנית - אובייקט אב טיפוס אשר ממנו האובייקטים יכולים לירוש מתודות (methods) ומאפיינים (properties).

+ +

לאובייקט אב-הטיפוס (prototype), יכול להיות גם אובייקט אב-טיפוס משל עצמו, אשר ממנו הוא יורש מתודות (methods) ומאפיינים (properties) וכך הלאה. תהליך זה לרוב מוגדר כשרשרת אבי הטיפוס (prototype chain), אשר מסבירה מדוע לאובייקטים שונים יש מאפיינים ומתודות שזמינים עבורם, כאשר אלו בכלל הוגדרו באובייקטים אחרים. 

+ +

ליתר דיוק, המתודות והמאפיינים מוגדרים במאפיין (property) בשם prototype ב-constructor functions ולא באובייקטים עצמם שנוצרו (object instance).

+ +

ב-JavaScript, נוצר קשר בין האובייקט שנוצר (object instance) לבין אב הטיפוס/prototype שלו באמצעות ״הליכה״ על שרשרת אבי הטיפוס. אנו נוכל לראות את אב הטיפוס של אובייקט ב-property של האובייקט שנוצר בשם __proto__ . שם זה נגזר מ-prototype שמוגדר בכלל ב-constructor.  

+ +
+

לתשומת לב:  חשוב להבין שיש אבחנה בין אב הטיפוס של האובייקט  (object' s prototype) אשר זמין באמצעות (Object.getPrototypeOf(obj או באמצעות מאפיין __proto__לבין מאפיין (property) בשם אב-טיפוס (prototype) אשר נמצא ב-constructor functions. 

+ +

הראשון הוא מאפיין שיופיע על כל אובייקט שייווצר ויעיד מיהו אב הטיפוס של אותו אובייקט ואילו השני, אשר נמצא ב-constructor הוא בעצם מאפיין של אותו constructor. 

+
+ +

נסתכל על דוגמא על מנת להבין זאת לעומק.

+ +

הבנה של אובייקטים מסוג אב-טיפוס

+ +

נחזור לדוגמא הקודמת שבה סיימנו לכתוב את ה-constructor שלנו ()Person:

+ +

העלו את הדוגמא בדפדפן שלכם. אם אין לכם את הדוגמא עצמה או שהיא אינה עובדת, אנא השתמשו בדוגמא שלנו אשר נמצאת בקישור זה או בקוד המקור.

+ +

בדוגמא זו, אנחנו מגדירים את ה -constructor function שלנו כך:

+ +
function Person(first, last, age, gender, interests) {
+
+  // property and method definitions
+  this.first = first;
+  this.last = last;
+//...
+}
+ +

אנו יוצרים אובייקטים מה-constructor function שהגדרנו כך:

+ +
var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
+ +

אם תקלידו .person1 בקונסולה, אתם אמורים לראות שהדפדפן מנסה להשלים באופן אוטומטי אפשרויות הזמינות עבור אובייקט זה: 

+ +

+ +

ברשימה זו, אתם יכולים לראות את הפרופ׳ ומתודות שהוגדרו ב-constructor Person()name, age, gender, interests, bio, ו- greeting. בנוסף, אנחנו גם נראה פרופ׳ ומתודות נוספות - watch, valueOf וכד׳ - אלו מוגדרים בתבנית אב הטיפוס (prototype object) של ()Person, שזה  Object.

+ +

+ +

מה קורה כשאנחנו רוצים לקרוא למתודה, להפעיל מתודה על person1, אשר מוגדרת ב-Object?:

+ +
person1.valueOf()
+ +

המתודה הזו - ()Object.valueOf נורשת על ידי person1 בגלל שתבנית אב הטיפוס של האובייקט (הפונקציה ()Person) שיצר אותו היא  ()Object. המתודה ()valueOf מחזירה את הערך של האובייקט שהיא נקראה עליו: 

+ + + +
+

לתשומת לב: אנחנו רוצים להדגיש את העבודה שמתודות ופרופ׳ לא מועתקים מאובייקט אחד לשני ב-prototype chain - הם זמינים עבורם באצעות הליכה על prototype chain כפי שהסברנו למעלה..

+
+ +
+

לתשומת לב: אין דרך רשמית להיכנס לאובייקט אב הטיפוס של אובייקט בצורה ישירה  הקשרים שבין הפריטים בשרשרת מוגדר בתוך פרופ׳ פנימי - שנקרא [[prototype]] במסמכים של השפה - ראו גם {{glossary("ECMAScript")}}. 

+ +

מרבית הדפדפנים המודרניים מכילים פרופ׳ שזמין שנקרא בשם __proto__, אשר מכיל את האובייקט שהוא תבנית האב של האובייקט הרלוונטי. לדוגמא, נסו להזין person1.__proto__ ו- person1.__proto__.__proto__ לראות איך זה נראה. החל מ-ECMAScript 2015 אנחנו יכולים לגשת לאובייקט אב הטיפוס של אובייקט באמצעות Object.getPrototypeOf(obj).

+
+ +

The prototype property: היכן שמגדירים מה מורישים

+ +

אז, היכן הפרופ׳ והמתודות שמורישים מוגדרים? אם אנחנו מסתכלים על הדף Object, אנחנו נראה רשימה מצד של שמאל של פרופ׳ ומתודות - הרבה יותר מאלו שראינו שזמינות עבור person1. למה? מכיוון שחלקם הורשו ל-person1 וחלקם לא. 

+ +

כפי שהוסבר למעלה, אלו שנורשו הם אלו שמוגדרים במאפיין (property) בשם prototype - כלומר אלו הם שמתחילים עם .Object.prototype ולא אלו שרק מתחילים עם Object. הערך של המאפיין prototype הוא אובייקט, אשר בעצם הוא מאחסן את כל הפרופ׳ והמתודות שאנחנו רוצים להוריש לאובייקטים בהמשך ה-prototype chain.

+ +

כך לדוגמא, ()Object.prototype.watch ו- ()Object.prototype.valueOf זמינות עבור כל אובייקט שיירש מ-Object.prototype, כולל מופעים חדשים של אובייקטים מה-()constructor Person

+ +

לעומת זאת, ()Object.is ו-()Object.keys לדוגמא, לא מוגדרים בתוך () ולכן לא נוריש אותם לאובייקטים אשר יירשו מ-Object.prototype. הם מתודות ופרופ׳ אשר זמינים רק עבור ה-constructor Object עצמו. 

+ +
+

לתשומת לב:  זה נראה מוזר - כיצד יכולה להיות מתודה שמוגדרת על ה-constructor, שהיא בעצמה פונקציה? פונקציות הם גם סוג של אובייקט - ראו הדף בנושא ()Function להמשך פרטים.

+
+ +
    +
  1. אתם יכולים לבדוק את הפרופ׳ בשם ה-prototype של האובייקטים הנוכחיים שלנו - חזרו לדוגמא הקודמת והקלידו את הקוד הבא בקונסולה: +
    Person.prototype
    +
  2. +
  3. אתם לא תראו יותר מדי אפשרויות מכיוון שלא הגדרנו כלום בפרופ׳ בשם prototype של ה-constructor. כברירת מחדל, הפרופ׳ בשם prototype של ה-constructor תמיד מתחיל ריק.
  4. +
  5. כעת, נסו להזין את הקוד הבא: +
    Object.prototype
    +
  6. +
+ +

אתם תראו מספר גדול של מתודות שמוגדרות ב-property בשם prototype של Object, אשר זמינות עבור אובייקטים שירשו מ-Object כפי שהסברנו למעלה.

+ +

אתם תראו דוגמאות כאלו של שרשרת ההורשה - prototype chain inheritance בכל JavaScript - נסו לחפש אחר מתודות ופרופ׳ אשר מוגדרים בפרופ׳ של האובייקטים הגלובליים String, Date, Number, ו-Array. לכולם יש מס׳ מתודות ופרופ׳ שמוגדרות באותו prototype. זו הסיבה לדוגמא שכשאר אנחנו יוצרים מחרוזת בצורה הבאה:

+ +
var myString = 'This is my string.';
+ +

ל-myString ישר יש מספר מתודות שימושיות שזמינות עבורו כמו () split(), indexOf(), replace וכד׳. 

+ +
+

לתשומת לב: אנו ממליצים לקרוא את המדריך המעמיק שלנו בנושא - Using prototypes in JavaScript לאחר שהבנתם את החלק הזה. החלק הזה בכוונה נכתב בצורה פשוטה (יחסית) על מנת להסביר את הרעיון שעומד מאחורי הנושא.

+
+ +
+

חשוב: המאפיין (prototype (property הוא אחד מהחלקים המבלבלים ב-JavaScript. אתם עלולים לחשוב ש-this מצביע על האובייקט אב טיפוס של האובייקט הנוכחי, אבל הוא לא (זה אובייקט פנימי שניתן לגשת אליו באמצעות __proto__, זוכרים? ).

+ +

המאפיין prototype הוא בעצם property שמכיל אובייקט, אשר על אובייקט זה אנחנו מגדירים את המתודות והפרופ׳ שנרצו שיורשו.

+
+ +

שימוש במתודה ()create

+ +

מוקדם יותר ראינו כיצד מתודת ()Object.create יכולה לשמש על מנת ליצור אובייקט חדש:

+ +
    +
  1. לדוגמא, נסו להזין בקונסולה של הדוגמא הקודמת את הקוד הבא: +
    var person2 = Object.create(person1);
    +
  2. +
  3. מה ש-()create עושה בפועל היא ליצור אובייקט חדש מ-prototype מוגדר. כאן person2 מיוצר כאובייקט חדש באמצעות שימוש ב-person1 כאובייקט ה-prototype שלו, כאובייקט אב הטיפוס שלו. אתם יכולים לראות זאת באמצעות הזנת הקוד הבא בקונסולה:
  4. +
  5. +
    person2.__proto__
    +
  6. +
+ +

זה יחזיר לנו person1.

+ +

המאפיין (property) בשם constructor

+ +

לכל constructor function יש מאפיין (property) בשם prototype אשר הערך שלו הוא אובייקט. אותו אובייקט מכיל מאפיין (property) בשם constructor

+ +

המאפיין constructor הזה, מצביע ל-constructor function המקורית. כפי שתראו בהמשך, כאשר properties שמוגדרים על Person.prototype property, או באופן כללי, על המאפיין prototype של constructor function, שהוא בעצמו הוא אובייקט, הם נהיים זמינים עבור כל האובייקטים שייווצרו באמצעות ה-constructor בשם ()Person. המאפיין constructor זמין גם עבור האובייקט person1 וגם עבור האובייקט person2

+ +
    +
  1. לדוגמא, נסו להזין את השורות הבאות בקונסולה: +
    person1.constructor
    +person2.constructor
    + +

    שתיהן אמורות להחזיר לנו ה-Person() constructor, שכן היא מכילה את ה-״הגדרה״ המקורית של אובייקטים אלו. טריק חכם הוא שניתן לשים סוגריים רגילות () בסוף המאפיין constructor (ובתוך הסוגריים להכניס פרמרטים הנדרשים ל-constructor, ככל ונדרשים), וואז נוצר לנו אובייקט חדש מאותו constructor. ה-constructor הוא בעצם פונקציה אחרי הכל, אז אפשר לקרוא לפונקציה באמצעות שימוש ב-() כמו שאנחנו יודעים. רק חשוב לשים את המילה השמורה new לפני, על מנת להגדיר שאנחנו רוצים שיווצר אובייקט חדש ולהשתמש בפונקציה הזו כ-constructor של אותו אובייקט. 

    +
  2. +
  3. נסו להזין את הקוד הבא בקונסולה: +
    var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);
    +
  4. +
  5. כעת, נסו לגשת למתודות ולפרופ׳ של האובייקט החדש: +
    person3.name.first
    +person3.age
    +person3.bio()
    +
  6. +
+ +

זה עובד מצויין. אנחנו בדרך לא נשתמש באופציה שכזו, אבל זה יכול להיות שימושי כאשר אנחנו רוצים ליצור מופע אובייקט חדש ואין לנו הפנייה לקונסטרקטור המקורי בצורה פשוטה מכל סיבה שהיא. 

+ +

ה-property בשם constructor שימושי גם לדברים נוספים. לדוגמא, אם יש לנו אובייקט ואני רוצה להחזיר את שם ה-constructor שהוא מופע שלו, כלומר שבנה אותו, אנחנו יכולים להזין את הקוד הבא:

+ +
instanceName.constructor.name
+ +

נסו לדוגמא להזין את הקוד הבא:

+ +
person1.constructor.name
+
+ +
+

לתשומת לב:  הערך של constructor.name יכול להשתנות (כתוצאה מ-prototypical inheritance, binding, preprocessors, transpilers, etc. ועד), אז לדוגמאות מורכבות יותר, אנחנו נרצה להתשמש באופרטור instanceof במקום. 

+
+ +
    +
+ +

שינוי של הפרופ׳ בשם prototype - הוספה/הסרה של מתודות

+ +

נסתכל כעת על דוגמא לשינוי של ה-property בשם prototype שנמצא ב-constructor function - מתודות שיתווספו ל-prototype יהיו זמינות עבור כל האובייקטים שנוצרו מאותה constructor function. בנקודה הזו אנחנו נוסיף מתודות ל-property בשם prototypeשל constructor function שלנו, כך שכל מה שנגדיר במאפיין prototype יהיה זמין עבור האובייקטים שייווצרו ממנו. 

+ +
    +
  1. חזרו לדוגמא המופיע בקישור oojs-class-further-exercises.htm וצרו עותק מקומי של קוד המקור source code. מתחת לקוד הנוכחי של של JavaScript, הוסיפו את הקוד הבא, אשר יוצר מתודה חדשה במאפיין prototype של constructor function:
  2. +
  3. +
    Person.prototype.farewell = function() {
    +  alert(this.name.first + ' has left the building. Bye for now!');
    +};
    +
  4. +
  5. שמרו את הקוד והעלו את הדף בדפדפן, ונסו להזין את הקוד הבא : +
    person1.farewell();
    +
  6. +
+ +

אתם אמורים לקבל הודעה קופצת מסוג alert, המכילה את שם ה-person שהוגדר ב-constructor. זה מאוד שימוש, אבל מה שיותר שימושי זה שכל שרשרת ההורשה עודכנה באופן דינאמי ואוטומטי והפכה את המתודה הזו לזמינה עבור כל האובייקטים שנוצרו באמצעות אותה constructor function. 

+ +

חשבו על זה לרגע, בקוד שלנו אנחנו הגדרנו את ה-constructor function, ואז יצרנו אובייקט מאותה constructor function, ואז הוספנו מתודה נוספות ל-prototype של אותה constructor function: 

+ +
function Person(first, last, age, gender, interests) {
+
+  // property and method definitions
+
+}
+
+var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);
+
+Person.prototype.farewell = function() {
+  alert(this.name.first + ' has left the building. Bye for now!');
+};
+ +

ומה שקרה זה שהמתודה שהגדרנו ()farewell נהייתה זמינה עבור person1 - והוא יכול לעשות בה שימוש, למרות שהוא הוגדר לפני שהגדרנו את המתודה החדשה. מגניב, לא?

+ +
+

לתשומת לב: אם אתם נתקלים בבעיות, ראו את הדוגמא פה או כדף אינטרנט.

+
+ +

יחד עם זאת, לעיתים נדירות נראה properties שמוגדרים ב-property הprototype מכיוון שהם לא ממש גמישים כשהם מוגדרים בצורה הזו. לדוגמא, ניתן להוסיף property בצורה הזו: 

+ +
Person.prototype.fullName = 'Bob Smith';
+ +

זה לא ממש מאפשר גמישות (שכן יכול להיות שקוראים לאובייקט person החדש בשם אחר), ולכן יהיה עדיף לבנות את המאפיין החדש בצורה הבאה: 

+ +
Person.prototype.fullName = this.name.first + ' ' + this.name.last;
+ +

יחד עם זאת, זה לא יעבוד שכן this יפנה לסקופ הגלובלי במקרה הנוכחי ולא לסקופ של הפונקציה. קריאה למאפיין זה תחזיר ערך של undefined undefined.

+ +

זה עובד מצוין על מתודה שהגדרנו למעלה ב-prototype מכיוון שהיא יושבת בתוך הסקופ של ה-function, והמתודה מועברת בתורה לסקופ של האובייקט שנוצר באמצעות constructor function. אז אולי נגדיר constant properties ב- prototype, כאלו שלא נשנה לעולם, אבל בכללי, זה עובד טוב יותר להגדיר properties בתוך ה-constructor. 

+ +

בעקרון, השימוש המקובל להגדרת אובייקטים הוא להגדיר את המאפיינים (properties) בתוך ה-constructor ואת המתודות בתוך ה-prototype. 

+ +

זה הופך הקוד לקל יותר לקריאה, שכן ה-constractor מכיל רק  את ההגדרות של properties ואילו המתודות מחולקות לבלוקים נפרדים. לדוגמא:

+ +
// Constructor with property definitions
+
+function Test(a, b, c, d) {
+  // property definitions
+}
+
+// First method definition
+
+Test.prototype.x = function() { ... };
+
+// Second method definition
+
+Test.prototype.y = function() { ... };
+
+// etc.
+ +

ניתן לראות שימוש בפועל בדוגמא school plan app של Piotr Zalewa.

+ +

לסיכום

+ +

מאמר זה כיסה את הנושא של JavaScript object prototypes, כולל כיצד prototype object chains מאפשרת לאובייקטים לירוש מתודות ופרופ׳ אחד מהשני, ראינו את ה-property בשם prototype וכיצד הוא יכול לשמש על מנת להוסיף מתודות ל-constructors וכן נושאים נוספים.

+ +

במאמר הבא אנחנו נראה כיצד ניתן להחיל הורשה של פונצקיונליות בין שני אובייקטים שניצור. 

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects")}}

+ +

במודול זה

+ + -- cgit v1.2.3-54-g00ecf