הפשטות של ג’אווהסקריפט, הפכה אותה לנגישה מאוד – ולכן גם מאוד נפוצה. מכאן הוגדר חוק אטווד הטוען: “כל תוכנה שיכולה להיכתב בג’אווהסקריפט – תכתב לבסוף בג’אווהסקריפט“.
חשוב להזכיר שג’אווהסקריפט היא לא שפה שתוכננה היטב: יש בתכנון שלה ליקויים רבים – מה שגם העמיד לה לאורך השנים ביקורת רבה. השפה תוכננה בחופזה (ע”פ סיפורים, גרסה ראשונית שלה עם מפרשן הועמדה בעשרה ימים) והייתה חלק ממוצר מאוד נקודתי: גרסה 2.0 של דפדפן נטסקייפ. בד”כ משקיעים בשפות תכנות חשיבה ובקרה רבים בהרבה – וזה ניכר.
![]() |
חוסר עקביות מעורר השתאות של ג’אווהסקריפט, בפעולות החיבור בין אובייקטים. מקור |
קהילת הג’אווהסקריפט, שהיא כיום גדולה ורצינית – לא נשארה אדישה, ולאורך השנים היא משקעיה מאמצים בכדי לשפר את השפה. מיליוני אתרי-אינטרנט שנכתבו על בסיס ג’אווהקריפט בחתך מסוים – הם מגבלה נוקשה על החשיבות בשפה לתאימות-לאחור.
![]() |
רמת התמיכה ב ES6 ע”י מנועי-ההרצה השונים. מקור. |
אם עוד לפני שנה-שנתיים התמיכה בדפדפנים עדיין לא הייתה טובה מספיק – והשימוש העיקרי ב ES6 היה בסביבות בהן אנו שולטים על הגרסה (כמו NodeJs), היום המצב כבר השתנה ובגרסאות הדפדפנים האחרונות התמיכה כבר טובה למדי!
התמונה למעלה מציגה את התמונה עבור ES6, אך המצב גם כבר דיי טוב עבור ES7 ו ES8.
לפני שאתם משתמשים בפיצ’ר מתקדם כדאי לבדוק את רמת התמיכה שלו באתר CanIUse.
לצורך הפוסט אשתמש בשם ES6 בכדי להתייחס ל ES6+ES7+ES8 – נראה לי שהם מספיק קרובים בכדי שיהיה אפשר להתייחס אליהם כמקשה אחת. לכל מה שהגיע לפני ES6 אקרא בפוסט ES5 (למרות שיש מגוון גרסאות שונות).
אני מניח שרבים מאיתנו, המכירים ג’אווהסקריפט בצורה סבירה או טובה – לא הספיקו להתעדכן באמת בעיקרי השינויים בשפה.
כשהתקן ה ES6 יצא – עדיין היה לו תמיכה מזערית בסביבות הריצה השונות. הוא לא היה רלוונטי עדיין לעבודה. אז מה עושים? ממתינים עוד קצת. זה מה שאני עשיתי.
הנחת הפוסט היא שאתם מכירים JavaScript בצורה טובה, אך בגרסאות הישנות שלה – ושלא הסתקתם להתעדכן לגבי ES6.
אם זה המצב, כנראה שאם תפתחו קוד חדש שנכתב על בסיס ES6 – תתקשו לקרוא אותו. חלקים חוזרים בתחביר – לא יהיו מובנים.
מטרת הפוסט היא לא לכסות את כל פינות ES6 וכל הפיצ׳רים – אלא בעיקר לכסות את התחביר החדש – ומשמעויותיו. לאפשר לכם לקרוא ולהבין קוד ES6. את התשובות לשאולת בנוסח ״איך עושים …. ב ES6״ – אני אשאיר לגוגל ו Stack Overflow.
let ו const מחליפים את var
- אם שכחנו להשתמש במילה השמורה var בהגדרת משתנה – אין בעיה! המשתנה יוגדר על המרחב הגלובלי (או אובייקט שמייצג אותו, למשל window בדפדפן).
- אם הגדרנו משתנה פעמיים – אין בעיה! הוא יוגדר מחדש (על חשבון הקודם). הגדרה כפולה של משתנה היא כנראה באג ולא כוונת המתכנת הסביר.
- ה scope של הגדרת var הוא scope הפונקציה – ולאו דווקא הבלוק העוטף (כלומר: {}), זה גם מבלבל (שונה משפות תחביר-C האחרות) – וגם פחות שימושי: משתנים שאורך החיים שלהם מתאים יותר ל block ״זולגים״ החוצה ל scope של הפונקציה.
- קוד שבא לפני הגדרה של משתנה שהוגדר כ var – עדיין יכול להשתמש במשתנה. זו מן התנהגות של מנגנון שנקרא hoisting בו כל הגדרות ה var (וגם function או class) מקודמות לתחילת ה scope שבהן הוגדרו לפני שהקוד מבוצע במפרשן.
- עצה נפוצה ב ES5 היא לבצע את כל ההגדרות בתחילת ה scope – בכדי להימנע מהתנהגות לא-צפויה של הקוד. כלומר: לכתוב את הקוד כפי שאכן ירוץ.
const יגן רק על הרפרנס עצמו. אם משתמשים בו על אובייקט, הוא רק יגן בפני החלפת המצביע לאובייקט – אך לא יגן בפני שינויים על האובייקט עצמו:
כלי להגנה על פרמטרים באובייקט הוא הפונקציה (…)Object.freeze. חשוב להזכיר שהיא לא רקורסיבית, קרי לא תגן על שינוי תכונות של אובייקטים שנשמרים על האובייקט – מה שהופך אותה לפחות שימושית.
- מוגדר תמיד strict mode בתוך מודולים – אלמנט חדש בשפה (פוסט הבא?), שהוא דיי נפוץ. בשל תאימות לאחור לא החילו strict mode על המרחב הגובאלי / פונקציות רגילות – וההמלצה היא להמשיך ולהגדיר בהם ״use strict״.
- שימוש ב let / const לא מאפשר להגדיר מחדש משתנה שכבר הוגדר.
- ה scope של let / const הוא הבלוק {} בו הם הוגדרו – ולא רק הפונקציה. זה כנראה השיפור המורגש ביותר.
- לכאורה let / const לא עוברים תהליך של Hoisting ולא ניתן לגשת אליהם לפני שהוגדרו.
- למען הדיוק, כן מתרחש Hoisting (מגבלות טכניות?) – אבל המפרשן מוסיף גם בדיקה בעת הגישה, ואם יש גישה למשתנה לפני שאותחל – הוא יזרוק Reference Error:
- מדפיסה ״global x״ מכיוון ש x לא c ב scope הפונקציה, הולכים ל scope החיצוני – ומוצאים אותו שם. זו התנהגות ES5.
- השורה השנייה תזרוק ReferenceError בעת הפענוח.המשתנה y לא אותחל – זו הבדיקה שדיברנו עליה. היה hoisting ולכן המפרשן יודע על קיומו, אבל לא ניתן לגשת אליו.
הבלוק שאתם רואים (סוגריים מסולסלים צהובים) הוא התחליף המקובל ב ES6 ל Immediately Invoked Function Expressions – הגדרה של פונקציה שמיד מפעילים אותה. זה בעצם היה תרגיל לצורך “סגירת” משתנים מסוימים ב scope מצומצם יוצר, מה שאנו מקבלים ב ES6 מבלוק רגיל – כאשר אנחנו משתמשים ב let/const.
Arrow Functions
על פניו, Arrow Functions (בקיצור: AF) הם דרך מינימלית יותר להעביר פונקציה כארגומנט.
- התחביר הקלאסי (הפונקציה אנונימית ומצביע אליה מושם למשתנה).
- תחביר AF כאשר יש פרמטרים.
- תחביר AF ללא פרמטרים.
כפי שאתם בוודאי זוכרים, this בג’אווהסקריפט מתייחס ל context הקורא לפונקציה, ולא ל scope החיצוני – כפי שמקובל ברוב שפות התכנות. זה מבלבל (כתבתי על זה פוסט שלם, בזמנו) – ותרגיל מקובל הוא להוסיף ב scope החיצוני את השורה:
var that = this; // Store the context of this
על מנת להיות מסוגלים לגשת ל this של ה scope העוטף.
ב AF – זו ההתנהגות הטבעית. הפרקטיקה היום ב ES6 היא להשתדל ולהשתמש ב Arrow functions ככל האפשר.
כמה תכונות חדשות של פונקציות (ומסביב) – ששינו את תחביר השפה
Rest Parameter
Rest Parameter הוא המקביל של varargs של ג’אווה / params של #C:
כמו בשפות אחרות – על ה rest param להיות אחרון ברשימת הפרמטרים (הגיוני).
ברוכה הבאה, ג’אווהסקריפט, למשפחת השפות המודרניות!
Spread Operator
Spread Operator (בקיצור: SO) הוא כלי חדש וחשוב בשפה. התחביר שלו זהה ל Rest Parameter (שלוש נקודות) – מה שהזכיר לי אותו בהקשר לאייטם הקודם.
ה SO מקבל אובייקט iterable (כמו מערך או מחרוזת) – ו”מפזר” את הערכים שלו. הקונספט הזה קיים גם בשפות אחרות.
חשוב לציין שאי אפשר להשתמש בו סתם כך, למשל: להציב את התוצאה שלו למשתנה. יש להשתמש בו בהקשר שמוכן לקבל iterable מהסוג הנכון.
הדוגמה הפשוטה ביותר היא לפזר פרמטרים לפונקציה :
- בצורה הזו x מקבל את הרשימה, בעוד y ו z – לא מקבלים ערכים, ולכן הם undefined.
- אם ״פיזרנו״ את הרשימה – כל הפרמטרים מקבלים ערכים מהרשימה (כי הרשימה ארוכה דיה).
- אי אפשר להשתמש ב SO להשמה פשוטה. זה לא הגיוני. אפשר להשתמש ב SO רק בהקשרים המוכנים לקבל iterable.
- ה SO מאפשר לנו לייצר בקלות עותק חדש של המערך – ורק עליו נבצע מיון.
- אנחנו יכולים להשתמש ב SO גם בכדי להרכיב בקלות ובצורה דינאמית – מערכים חדשים.
- SO עובד גם על אובייקטים, ומאפשר מן תחביר חדש וקל “להרחיב” אותם.
- אפשר גם הפוך – אפשר לצמצם עותק של האובייקט. כמובן, אבל חשבתי שיהיה שימושי להזכיר.
Deconstructing
אם אתם עובדים עם שפות מודרניות, סיכוי טוב שה Spread Operator הזכיר לכם פעולת Deconstruction, המפרקת איברים ממערך או אובייקט – בהשמה. גם פעולה זו נוספה לשפה:
- בדוגמה הפשוטה ביותר, אנו מציבים כמה ערכים, במקרה שלנו a ו b – בפעולה אחת, מתוך מערך.
- אפשר להשתמש ב deconstruction בכדי לבצע swap, למשל.
- אפשר לשלב Rest Operator בתוך Deconstruction ולהציב את כל הערכים הנותרים בתוך המשתנה שקראנו לו rest.
- שימו לב שאם אני לא זקוק לערך מסוים, אני יכול לדלג עליו עם פסיק ללא משתנה. נחמד.
- זהו התחביר. אנחנו שולפים לתוך משתנה בשם x את הערך למפתח x מתוך האובייקט.
- מה עושים כאשר המשתנה כבר מוגדר, אך אנו רוצים להציב בו שוב?
- תקלה: אסור להגדיר מחדש משתנה בעזרת let.
- תקלה: ג׳אווהסקריפט לא יודע לזהות שמדובר בפעולת deconstruction.
- הפתרון התחבירי: לעטוף את השמת ה deconstruction בסוגריים. אני מקווה שעכשיו זה נראה הגיוני.
- השימוש הנפוץ ל deconstruction, מן הסתם – הוא בהשמה לריבוי ערכים. מה קורה כאשר אין התאמה בין שם המשתנה למפתח באובייקט? – אנו מקבלים undefined.
- מה עושים אם לאובייקט יש מפתחות בשמות שלא מתאימים לנו? – אנחנו יכולים להשתמש בתחביר הזה בכדי לבחור באלו שמות משתנים להציב אותם. אנו רוצים שערך המפתח x יכנס למשתנה a, וערך המפתח y למשתנה b.
- האם אפשר לספק שמות שונים רק לחלק מהאיברים? אפשר.
אני מסתכל על השורה ותוהה הזו מה הסיכוי לנחש מה היא עושה אותה מבלי להכיר את הכללים?!
- גם כאן אפשר להשתמש ב rest operator (כרגע פיצ׳ר בהרצה), rest הפעם הוא מטיפוס אובייקט (ולא מערך).
ערך ברירת מחדל
כן! אנחנו יכולים לקבוע ערכי ברירת מחדל לפרמטרים בפונקציות (ובעוד כמה מקרים).
- ערך ברירת מחדל לפרמטר בפונקציה – שימושי להרחבת פונקציה בצורה תואמת-לאחור או צמצום החתימה שלה – עבור השימושים הנפוצים.
- ערך ברירת המחדל הוא תחליף מרכזי תחביר ה x = x || 10 שמאוד היה מקובל בשפה. היום – כמעט ולא תראו אותו.
- ערך ברירת המחדל הוא תחליף מסוים ל function overloading – יכולת שלא קיימת בשפה.
- שימוש פשוט בערכי ברירת מחדל.
- אם מעבירים undefined לפרמטר עם ערך ברירת-מחדל, אזי יתקבל ערך ברירת המחדל – ולא undefined. ערך null יעבור כרגיל.
- הנה, אפשר להשתמש בערך ברירת-מחדל גם בהשמת deconstruction.
- גם כאן, כללי ה null וה undefined – תקפים.
אחלה פוסט, כרגיל תקציר מעולה של החידושים האחרונים. שווה להזכיר את Babel ואת הדחיפה שלו לקפיצה של JavaScript בשנים האחרונות. הכלי מאפשר לתרגם (to transpile) קוד שנכתב ב-ES6 לקוד שנכתב ב-ES5, ובכך לאפשר למפתחים להשתמש בתכונות מתקדמות של השפה בלי חשש לבעיות תאימות בדפדפנים. הכלי (או כלים אחרים דומים כמו המפרשן של TypeScript) הינו כלי מרכזי ובסיסי בכל פיתוח מודרני שמתבצע היום בשפה.כמו כן לגבי מודולים (ES Modules), שווה להזכיר שהם עדיין לא חלק מהתקן באופן רשמי, ולכן לא ניתן להשתמש בהם ללא כלים כמו Babel.
התגובה הוסרה על ידי המשתמש שכתב אותה.
תודה אלון על התוספות 👍👍babel באמת היה כלי חשוב מאוד לגישור ES6 למשך תקופה – אבל אני רוצה רק לציין שיש כמה פי׳צרים שכנראה מאוד קשה לעשות בעזרת transpiling לבד, וגם babel לא תומך בהם – אלא רק מנועי ג׳אווהסקריפט שמריצים ES6 ישירות.
פוסט מעולה וקל לצריכהתודה!
תודה רבה! שמח לשמוע! 👍
לא deconstructing אלא destructuring
צודק. תודה!
shalom 🙂
פשוט מעולה, ליאור שאפו על הכתיבה (במאמר הזה ובכלל ) אני רוצה לציין שלפני 7 שנים בערך ניתקלתי לראשונה בבלוג שלך (שהיה מעוצב אחרת 🙂 ) ומאז אפשר להגיד שהפכתי ממתכנת javascript ג׳וניור לסיניור, תודה רבה!!
תודה רבה! ☺️