2 |
3 | # להבין ECMAScript 6
4 |
5 | מאת ניקולאס סי. זאקאס
6 |
7 | [](https://creativecommons.org/licenses/by-nc-nd/3.0/)
8 |
9 | ECMAScript 6
10 | מהווה את השינוי הגדול ביותר שעבר על מה שידוע לנו כ
11 | JavaScript
12 | מאז שנוצרה השפה. המהדורה השישית
13 | הוסיפה לא רק משתנים חדשים אלא גם תחביר חדש ויכולות חדשות.
14 | המהדורה השישית הושלמה בשנת 2014
15 | לאחר שנים של דיון
16 | ועבודת הכנה מעמיקה.
17 | למרות שיקח זמן מה עד שכל סביבות הג׳אווהסקריפט הקיימות יתמכו בה תמיכה מלאה,
18 | רצוי וחשוב לדעת מה קיים כעת ומה עתיד לבוא.
19 |
20 | הספר נועד לשמש כמדריך עבור המעבר בין מהדורה 5 ל-6 של השפה. הוא איננו מיועד לשמש עבור סביבת ריצה ספציפית, כך שהוא שימושי באותה המידה עבור מפתחים שעובדים בסביבת דפדפן
21 | (מפתחי ווב)
22 | או מפתחי
23 | Node.js
24 |
25 | מה מכיל הספר:
26 | * כל השינויים שחלו בשפה מאז מהדורה 5
27 | * כיצד תחביר המחלקה החדש מתקשר למושגים מוכרים בשפה
28 | * התועלת שבגנרטורים ואיטרטורים
29 | * ההבדל בין פונקציות חץ לפונקציות רגילות
30 | * אפשרויות חדשות לאחסון מידע בעזרת סטים, מפות ועוד.
31 | * הכוח הטמון בירושה ממשתנים בסיסיים
32 | * מדוע אנשים כה מתרגשים ממשתנה הפרומיס בנוגע לתכנות אסינכרוני
33 | * כיצד מודולים ישפיעו על כתיבת הקוד שלכם
34 |
35 | ## היכן לקרוא
36 |
37 | [גרסת המוציא לאור](https://leanpub.com/understandinges6/read/)
38 | זמינה בחינם ומכילה את הגרסה העדכנית ביותר.
39 | התוכן לא בהכרח סופי אבל אמור להיות נכון.
40 | גרסאות חדשות מפורסמות מספר פעמים בחודש.
41 |
42 | תוכן שאינו מופיע בגרסת המו״ל נחשב לתוכן לא מושלם וייתכן שיכיל חסרים או שגיאות.
43 |
44 | המו״ל הינו חברת לינפאב
45 | (Leanpub)
46 |
47 | ## רכישת עותק דיגיטלי
48 |
49 | ניתן לרכוש עותק דיגיטלי של הספר דרך חברת
50 | [Leanpub](https://leanpub.com/understandinges6).
51 | במידה ותרכשו עותק לפני שהספר יושלם, תקבלו את כל העדכונים לספר בזמן העדכון כולל הגרסה הסופית.
52 |
53 | ## רכישת עותק מודפס
54 |
55 | הספר מודפס על ידי חברת
56 | No Starch Press
57 | וניתן
58 | [לרכוש עותק באמזון](http://amzn.to/22YQOer).
59 | גרסת הדפוס מופצת לאחר עריכה מקצועית
60 |
61 | ## תמיכה במחבר
62 |
63 | אם נהניתם מהספר ותרצו לתמוך במאמציי ליצירת תוכן חדש, קיימות מספר דרכים:
64 |
65 | * [השאר טיפ](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EFQLNGT3QEL6J) בפייפאל
66 | * [הפוך לפטרון](https://patreon.com/nzakas) ב Patreon
67 |
68 | תרומה קטנה תעזור מאוד להמשיך בעבודתי.
69 |
70 | ## השתתפות בתוכן
71 |
72 | למרות שהספר נכתב בדרך של שיתוף תוכן, הפרויקט פועל תחת תנאים שונים מפרויקט קוד פתוח
73 | נא לקרוא את ההמשך לפני הוספת תוכן משלכם.
74 |
75 | ### תחביר של Leanpub
76 |
77 | Leanpub
78 | עובד בסגנון מיוחד עבור קבצי המקור. יש לעיין
79 | [בתיעוד](https://leanpub.com/help/manual#leanpub-auto-styling-text)
80 | לגבי המותר והאסור.
81 |
82 | **שים לב:**
83 | Leanpub
84 | לא נותנים תמיכה לכל האפשרויות הקיימות בסגנון כתיבה רגיל המצוי בגיטהאב
85 |
86 | ### בקשות שינוי
87 | בקשות שינוי
88 | PR (Pull Requests)
89 | יתקבלו רק עבור *תיקונים*
90 | ולא עבור *יצירת תוכן*
91 |
92 | בקשות שינוי שיתקבלו:
93 |
94 | * שגיאות כתיב
95 | * שגיאות תחביר
96 | * תיאורים אלטרנטיביים
97 | * הבהרות
98 |
99 | עקרונית, ניתן להגיש בקשות שינוי כדי לתקן את מה שקיים בקוד אבל לא להוסיף אליו
100 |
101 | **הערה:** בקשת שינוי שהתקבלה משמעותה ששמך יתווסף לרשימת התורמים
102 | תורמים עדיין כפופים לתנאי הרשיון, כלומר אינך נחשב ליוצר או בעלים של התוכן לאחר שהוכנס לקוד. הדבר נחשב לתרומה של מאמץ ליצירת הספר.
103 |
104 | ### סוגיות (Issues)
105 | באמצעות סוגיות ניתן להצביע על שגיאות כמו גם להציע שינויים.
106 | ניתן להשתמש בסוגיות בשביל:
107 |
108 | * שאלות בנוגע לתוכן
109 | * הערה על שגיאה או בעיה בתוכן
110 | * בקשה ליותר אינפורמציה על חלק מהתוכן
111 |
112 | כל סוגיה פתוחה תקבל מענה. סוגיות ייסגרו לאחר שקיבלו מענה או במידה והסוגיה לא תקבל מענה.
113 |
114 | אין לפתוח סוגיות עבור:
115 |
116 | * שאלות מתי תוכן מסויים יושלם
117 | * כל דבר שמסומן בקבצים בתור ״ TODO״
118 |
119 | ## זכויות יוצרים ורשיון
120 | זכויות יוצרים שייכות לניקולאס סי. זאקאס 2014-2016
121 |
122 | יצירה זו מופיעה תחת רשיון מסוג
123 | [Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License](http://creativecommons.org/licenses/by-nc-nd/3.0/).
124 |
125 | ## שאלות
126 |
127 | ### מתי יושלם הספר?
128 |
129 | הספר הושלם מבחינת תוכן מתאריך 10 ביוני 2016.
130 |
131 | ### אם ארכוש את הספר עכשיו, האם אקבל את הגרסא הסופית?
132 |
133 | כן. כאשר תרכוש עותק אלקטרוני של Leanpub , תקבל גם את כל העדכונים לספר. עדכונים יתקבלו באמצעות דואר אלקטרוני לאחר הרכישה.
134 |
135 | ### באיזו תדירות מפורסם הספר?
136 |
137 | בערך כל שבוע עד שבועיים השינויים ב Github מפורסמים תחת Leanpub
138 |
139 | ### כיצד להתייחס לתוכן בגיטהאב?
140 |
141 | כל מה שמופיע בגיטהאב נחשב לתוכן בתהליך יצירה.
142 | החלקים שנחשבים לנכונים ומסודרים מפורסמים תחת
143 | Leanpub.
144 | זו הסיבה שקיים יותר תוכן בגיטהאב מאשר בלינפאב,
145 | מאחר וגיטהאב מכיל את התוכן כולו, כולל תוכן שעוד לא פורסם.
146 |
147 | ### מה ההבדל בין הגרסא המודפסת לבין גרסת לינפאב?
148 |
149 | הנושאים והסדר שבו הם מופיעים בספר זהים בין הגרסאות. ההבדל הוא במספר מילים וביטויים ששונו על ידי עורכים למען בהירות או על מנת לשמור על סגנון הכתיבה של חברת
150 | No Starch.
151 | השינויים האלו לא מועברים ללינפאב מכיוון שהם מתרחשים בתוך קבצים בפורמט
152 | PDF
153 | ולפעמים בצורה לא ברורה, כך שקשה לשמור על שתי הגרסאות מסונכרנות זו עם זו.
154 |
155 | ### האם ניתן לרכוש זכויות תרגום
156 |
157 | כן. יש ליצור קשר עם חברת
158 | [No Starch Press](https://www.nostarch.com/)
159 | למען רכישת זכויות תרגום
160 |
161 | ## תורמים לפרויקט התרגום
162 |
163 | * [Ronen Elster](https://github.com/ronen-e)
164 | * [yanai101](https://github.com/yanai101)
165 | * [dekelb](https://github.com/dekelb)
166 | * [Maya Liberman](https://github.com/mayaliberman)
167 | * [Eli Orr](https://github.com/eliorr)
168 | * [Oded Dugma](https://github.com/odeddugma)
169 |
170 |
171 |
2 |
3 | # להבין ECMAScript 6
4 |
5 | מאת ניקולאס סי. זאקאס
6 |
7 | [](https://creativecommons.org/licenses/by-nc-nd/3.0/)
8 |
9 | ECMAScript 6
10 | מהווה את השינוי הגדול ביותר שעבר על מה שידוע לנו כ
11 | JavaScript
12 | מאז שנוצרה השפה. המהדורה השישית
13 | הוסיפה לא רק משתנים חדשים אלא גם תחביר חדש ויכולות חדשות.
14 | המהדורה השישית הושלמה בשנת 2014
15 | לאחר שנים של דיון
16 | ועבודת הכנה מעמיקה.
17 | למרות שיקח זמן מה עד שכל סביבות הג׳אווהסקריפט הקיימות יתמכו בה תמיכה מלאה,
18 | רצוי וחשוב לדעת מה קיים כעת ומה עתיד לבוא.
19 |
20 | הספר נועד לשמש כמדריך עבור המעבר בין מהדורה 5 ל-6 של השפה. הוא איננו מיועד לשמש עבור סביבת ריצה ספציפית, כך שהוא שימושי באותה המידה עבור מפתחים שעובדים בסביבת דפדפן
21 | (מפתחי ווב)
22 | או מפתחי
23 | Node.js
24 |
25 | מה מכיל הספר:
26 | * כל השינויים שחלו בשפה מאז מהדורה 5
27 | * כיצד תחביר המחלקה החדש מתקשר למושגים מוכרים בשפה
28 | * התועלת שבגנרטורים ואיטרטורים
29 | * ההבדל בין פונקציות חץ לפונקציות רגילות
30 | * אפשרויות חדשות לאחסון מידע בעזרת סטים, מפות ועוד.
31 | * הכוח הטמון בירושה ממשתנים בסיסיים
32 | * מדוע אנשים כה מתרגשים ממשתנה הפרומיס בנוגע לתכנות אסינכרוני
33 | * כיצד מודולים ישפיעו על כתיבת הקוד שלכם
34 |
35 | ## היכן לקרוא
36 |
37 | [גרסת המוציא לאור](https://leanpub.com/understandinges6/read/)
38 | זמינה בחינם ומכילה את הגרסה העדכנית ביותר.
39 | התוכן לא בהכרח סופי אבל אמור להיות נכון.
40 | גרסאות חדשות מפורסמות מספר פעמים בחודש.
41 |
42 | תוכן שאינו מופיע בגרסת המו״ל נחשב לתוכן לא מושלם וייתכן שיכיל חסרים או שגיאות.
43 |
44 | המו״ל הינו חברת לינפאב
45 | (Leanpub)
46 |
47 | ## רכישת עותק דיגיטלי
48 |
49 | ניתן לרכוש עותק דיגיטלי של הספר דרך חברת
50 | [Leanpub](https://leanpub.com/understandinges6).
51 | במידה ותרכשו עותק לפני שהספר יושלם, תקבלו את כל העדכונים לספר בזמן העדכון כולל הגרסה הסופית.
52 |
53 | ## רכישת עותק מודפס
54 |
55 | הספר מודפס על ידי חברת
56 | No Starch Press
57 | וניתן
58 | [לרכוש עותק באמזון](http://amzn.to/22YQOer).
59 | גרסת הדפוס מופצת לאחר עריכה מקצועית
60 |
61 | ## תמיכה במחבר
62 |
63 | אם נהניתם מהספר ותרצו לתמוך במאמציי ליצירת תוכן חדש, קיימות מספר דרכים:
64 |
65 | * [השאר טיפ](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EFQLNGT3QEL6J) בפייפאל
66 | * [הפוך לפטרון](https://patreon.com/nzakas) ב Patreon
67 |
68 | תרומה קטנה תעזור מאוד להמשיך בעבודתי.
69 |
70 | ## השתתפות בתוכן
71 |
72 | למרות שהספר נכתב בדרך של שיתוף תוכן, הפרויקט פועל תחת תנאים שונים מפרויקט קוד פתוח
73 | נא לקרוא את ההמשך לפני הוספת תוכן משלכם.
74 |
75 | ### תחביר של Leanpub
76 |
77 | Leanpub
78 | עובד בסגנון מיוחד עבור קבצי המקור. יש לעיין
79 | [בתיעוד](https://leanpub.com/help/manual#leanpub-auto-styling-text)
80 | לגבי המותר והאסור.
81 |
82 | **שים לב:**
83 | Leanpub
84 | לא נותנים תמיכה לכל האפשרויות הקיימות בסגנון כתיבה רגיל המצוי בגיטהאב
85 |
86 | ### בקשות שינוי
87 | בקשות שינוי
88 | PR (Pull Requests)
89 | יתקבלו רק עבור *תיקונים*
90 | ולא עבור *יצירת תוכן*
91 |
92 | בקשות שינוי שיתקבלו:
93 |
94 | * שגיאות כתיב
95 | * שגיאות תחביר
96 | * תיאורים אלטרנטיביים
97 | * הבהרות
98 |
99 | עקרונית, ניתן להגיש בקשות שינוי כדי לתקן את מה שקיים בקוד אבל לא להוסיף אליו
100 |
101 | **הערה:** בקשת שינוי שהתקבלה משמעותה ששמך יתווסף לרשימת התורמים
102 | תורמים עדיין כפופים לתנאי הרשיון, כלומר אינך נחשב ליוצר או בעלים של התוכן לאחר שהוכנס לקוד. הדבר נחשב לתרומה של מאמץ ליצירת הספר.
103 |
104 | ### סוגיות (Issues)
105 | באמצעות סוגיות ניתן להצביע על שגיאות כמו גם להציע שינויים.
106 | ניתן להשתמש בסוגיות בשביל:
107 |
108 | * שאלות בנוגע לתוכן
109 | * הערה על שגיאה או בעיה בתוכן
110 | * בקשה ליותר אינפורמציה על חלק מהתוכן
111 |
112 | כל סוגיה פתוחה תקבל מענה. סוגיות ייסגרו לאחר שקיבלו מענה או במידה והסוגיה לא תקבל מענה.
113 |
114 | אין לפתוח סוגיות עבור:
115 |
116 | * שאלות מתי תוכן מסויים יושלם
117 | * כל דבר שמסומן בקבצים בתור ״ TODO״
118 |
119 | ## זכויות יוצרים ורשיון
120 | זכויות יוצרים שייכות לניקולאס סי. זאקאס 2014-2016
121 |
122 | יצירה זו מופיעה תחת רשיון מסוג
123 | [Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License](http://creativecommons.org/licenses/by-nc-nd/3.0/).
124 |
125 | ## שאלות
126 |
127 | ### מתי יושלם הספר?
128 |
129 | הספר הושלם מבחינת תוכן מתאריך 10 ביוני 2016.
130 |
131 | ### אם ארכוש את הספר עכשיו, האם אקבל את הגרסא הסופית?
132 |
133 | כן. כאשר תרכוש עותק אלקטרוני של Leanpub , תקבל גם את כל העדכונים לספר. עדכונים יתקבלו באמצעות דואר אלקטרוני לאחר הרכישה.
134 |
135 | ### באיזו תדירות מפורסם הספר?
136 |
137 | בערך כל שבוע עד שבועיים השינויים ב Github מפורסמים תחת Leanpub
138 |
139 | ### כיצד להתייחס לתוכן בגיטהאב?
140 |
141 | כל מה שמופיע בגיטהאב נחשב לתוכן בתהליך יצירה.
142 | החלקים שנחשבים לנכונים ומסודרים מפורסמים תחת
143 | Leanpub.
144 | זו הסיבה שקיים יותר תוכן בגיטהאב מאשר בלינפאב,
145 | מאחר וגיטהאב מכיל את התוכן כולו, כולל תוכן שעוד לא פורסם.
146 |
147 | ### מה ההבדל בין הגרסא המודפסת לבין גרסת לינפאב?
148 |
149 | הנושאים והסדר שבו הם מופיעים בספר זהים בין הגרסאות. ההבדל הוא במספר מילים וביטויים ששונו על ידי עורכים למען בהירות או על מנת לשמור על סגנון הכתיבה של חברת
150 | No Starch.
151 | השינויים האלו לא מועברים ללינפאב מכיוון שהם מתרחשים בתוך קבצים בפורמט
152 | PDF
153 | ולפעמים בצורה לא ברורה, כך שקשה לשמור על שתי הגרסאות מסונכרנות זו עם זו.
154 |
155 | ### האם ניתן לרכוש זכויות תרגום
156 |
157 | כן. יש ליצור קשר עם חברת
158 | [No Starch Press](https://www.nostarch.com/)
159 | למען רכישת זכויות תרגום
160 |
161 | ## תורמים לפרויקט התרגום
162 |
163 | * [Ronen Elster](https://github.com/ronen-e)
164 | * [yanai101](https://github.com/yanai101)
165 | * [dekelb](https://github.com/dekelb)
166 | * [Maya Liberman](https://github.com/mayaliberman)
167 | * [Eli Orr](https://github.com/eliorr)
168 | * [Oded Dugma](https://github.com/odeddugma)
169 |
170 |
171 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | להבין ES6
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
116 |
117 |
118 |
125 |
126 |
127 |
128 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/README_en.md:
--------------------------------------------------------------------------------
1 | # Understanding ECMAScript 6
2 |
3 | by Nicholas C. Zakas
4 |
5 | 
6 |
7 | ECMAScript 6 represents the biggest change to the core of JavaScript in the history of the language. Not only does the sixth edition add new object types, but also new syntax and exciting new capabilities. The result of years of study and debate, ECMAScript 6 reached feature complete status in 2014. While it will take a bit of time before all JavaScript environments support ECMAScript 6, it's still useful to understand what's coming and which features are available already.
8 |
9 | This book is a guide for the transition between ECMAScript 5 and 6. It is not specific to any JavaScript environment, so it is equally useful to web developers as it is Node.js developers.
10 |
11 | What you'll learn:
12 |
13 | * All of the changes to the language since ECMAScript 5
14 | * How the new class syntax relates to more familiar JavaScript concepts
15 | * Why iterators and generators are useful
16 | * How arrow functions differ from regular functions
17 | * Additional options for storing data using sets, maps, and more
18 | * The power of inheriting from native types
19 | * Why people are so excited about promises for asynchronous programming
20 | * How modules will change the way you organize code
21 |
22 | ## Where to Read
23 |
24 | The [published version](https://leanpub.com/understandinges6/read/) is available for free and contains the latest "blessed" version. The content may be incomplete but should be correct. New releases are published several times a month.
25 |
26 | Anything that is not present in the published version is considered a work-in-progress and may be incomplete or incorrect.
27 |
28 | ## Purchasing an Ebook Copy
29 |
30 | You can purchase a copy of this ebook through [Leanpub](https://leanpub.com/understandinges6). If you purchase a copy before the ebook is finished, you will receive all updates to the ebook as they are released, up to and including the finished version.
31 |
32 | ## Purchasing a Print Copy
33 |
34 | Understanding ECMAScript 6 is being printed by No Starch Press and you can [purchase a print copy on Amazon](http://amzn.to/22YQOer). The print version is professionally copyedited and laid out.
35 |
36 | ## Supporting the Author
37 |
38 | If you enjoy this book and would like to support my efforts to create more content, there are a couple ways to do so:
39 |
40 | * [Leave a tip](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EFQLNGT3QEL6J) on PayPal
41 | * [Become a patron](https://patreon.com/nzakas) on Patreon
42 |
43 | A small donation is greatly appreciated and will allow me more time to continue my work.
44 |
45 | ## Contributions
46 |
47 | Even though this book is being developed in the open, the project works differently than open source software projects. Please read the following thoroughly before attempting to contribute.
48 |
49 | ### Leanpub Markdown
50 |
51 | Leanpub uses a specific flavor of Markdown for its source files. Please be sure to read [their documentation](https://leanpub.com/help/manual#leanpub-auto-styling-text) about what is allowable and what is not.
52 |
53 | **Note:** It may be frustrating, Leanpub does not support all of GitHub-flavored markdown.
54 |
55 | ### Pull requests
56 |
57 | Pull requests will be accepted only for *correction of content* and not for *creation of content*. Acceptable pull requests:
58 |
59 | * Typo fixes
60 | * Grammar/spelling errors
61 | * Alternate descriptions
62 | * Clarifying phrases
63 |
64 | Pull requests that will not be considered:
65 |
66 | * New sections of content
67 | * New chapters
68 |
69 | Basically, you can use pull requests to fix what is already in the repository but not to add what is not in the repository.
70 |
71 | **Note:** An accepted pull request means that your name will be added to a list of contributors. You are still bound by the conditions of the license, meaning that you are not considered an author or owner of the content once it has been merged in. It is considered a donation of your effort to this work.
72 |
73 | ### Issues
74 |
75 | Issues can be used both to point out errors as well as to make suggestions. Use issues for:
76 |
77 | * Asking questions about the content
78 | * Pointing out an error or problem with the content
79 | * Requesting more information about a section
80 | * Suggesting a new topic for inclusion
81 |
82 | Any issue that remains open will be addressed. Issues will be closed either when addressed or if the issue will not be addressed.
83 |
84 | Issues should not be used for:
85 |
86 | * Asking when a particular section or chapter will be complete
87 | * Anything already marked as "TODO" in the files
88 |
89 | These types of issues will simply be marked as invalid and closed without comment.
90 |
91 | ## Copyright and License
92 |
93 | Copyright 2014-2016 Nicholas C. Zakas.
94 |
95 | This work is licensed under a [Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License](http://creativecommons.org/licenses/by-nc-nd/3.0/).
96 |
97 | ## Frequently Asked Questions
98 |
99 | ### When will the book be finished?
100 |
101 | The book is content-complete as of June 10, 2016.
102 |
103 | ### If I buy a copy now, do I get the finished one?
104 |
105 | Yes. When you buy the Leanpub ebook today, you will automatically get all updates for the ebook. You'll be notified of updated versions via email after purchase.
106 |
107 | ### How often is the book published?
108 |
109 | Roughly every week or two the changes in GitHub are published to Leanpub.
110 |
111 | ### What is the state of content in GitHub?
112 |
113 | Everything in GitHub is a work in progress. Those parts that are known to be correct and fully-formed enough to be valuable are published to Leanpub. That's why there's more content in GitHub than is available on Leanpub, as GitHub contains everything, including content that's not quite ready for the book.
114 |
115 | ### What is the difference between the print version and the Leanpub version?
116 |
117 | The topics covered, and the order in which those topics are covered, are exactly the same between the print and Leanpub versions. The only real differences are in some words and phrases that have been altered by the copyeditors either for clarity or to follow No Starch's style guide. These changes are not backported into Leanpub because they happen on PDFs and sometimes opaquely, making it difficult to keep the two versions in sync.
118 |
119 | ### Can I acquire translation rights?
120 |
121 | Yes. Please contact [No Starch Press](https://www.nostarch.com/) for acquiring translation rights.
122 |
--------------------------------------------------------------------------------
/manuscript/B-ECMAScript-7.md:
--------------------------------------------------------------------------------
1 | # Appendix B: Understanding ECMAScript 7 (2016)
2 |
3 | The development of ECMAScript 6 took about four years, and after that, TC-39 decided that such a long development process was unsustainable. Instead, they moved to a yearly release cycle to ensure new language features would make it into development sooner.
4 |
5 | More frequent releases mean that each new edition of ECMAScript should have fewer new features than ECMAScript 6. To signify this change, new versions of the specification no longer prominently feature the edition number, and instead refer to the year in which the specification was published. As a result, ECMAScript 6 is also known as ECMAScript 2015, and ECMAScript 7 is formally known as ECMAScript 2016. TC-39 expects to use the year-based naming system for all future ECMAScript editions.
6 |
7 | ECMAScript 2016 was finalized in March 2016 and contained only three additions to the language: a new mathematical operator, a new array method, and a new syntax error. Both are covered in this appendix.
8 |
9 | ## The Exponentiation Operator
10 |
11 | The only change to JavaScript syntax introduced in ECMAScript 2016 is the *exponentiation operator*, which is a mathematical operation that applies an exponent to a base. JavaScript already had the `Math.pow()` method to perform exponentiation, but JavaScript was also one of the only languages that required a method rather than a formal operator. (And some developers argue an operator is easier to read and reason about.)
12 |
13 | The exponentiation operator is two asterisks (`**`) where the left operand is the base and the right operand is the exponent. For example:
14 |
15 | ```js
16 | let result = 5 ** 2;
17 |
18 | console.log(result); // 25
19 | console.log(result === Math.pow(5, 2)); // true
20 | ```
21 |
22 | This example calculates 5^2^, which is equal to 25. You can still use `Math.pow()` to achieve the same result.
23 |
24 | ### Order of Operations
25 |
26 | The exponentiation operator has the highest precedence of all binary operators in JavaScript (unary operators have higher precedence than `**`). That means it is applied first to any compound operation, as in this example:
27 |
28 | ```js
29 | let result = 2 * 5 ** 2;
30 | console.log(result); // 50
31 | ```
32 |
33 | The calculation of 5^2^ happens first. The resulting value is then multiplied by 2 for a final result of 50.
34 |
35 | ### Operand Restriction
36 |
37 | The exponentiation operator does have a somewhat unusual restriction that isn't present for other operators. The left side of an exponentiation operation cannot be a unary expression other than `++` or `--`. For example, this is invalid syntax:
38 |
39 | ```js
40 | // syntax error
41 | let result = -5 ** 2;
42 | ```
43 |
44 | The `-5` in this example is a syntax error because the order of operations is ambiguous. Does the `-` apply just to `5` or the result of the `5 ** 2` expression? Disallowing unary expressions on the left side of the exponentiation operator eliminates that ambiguity. In order to clearly specify intent, you need to include parentheses either around `-5` or around `5 ** 2` as follows:
45 |
46 | ```js
47 | // ok
48 | let result1 = -(5 ** 2); // equal to -25
49 |
50 | // also ok
51 | let result2 = (-5) ** 2; // equal to 25
52 | ```
53 | If you put the parentheses around the expression, the `-` is applied to the whole thing. When the parentheses surround `-5`, it's clear that you want to raise -5 to the second power.
54 |
55 | You don't need parentheses to use `++` and `--` on the left side of the exponentiation operator because both operators have clearly-defined behavior on their operands. A prefix `++` or `--` changes the operand before any other operations take place, and the postfix versions don't apply any changes until after the entire expression has been evaluated. Both use cases are safe on the left side of this operator, as this code demonstrates:
56 |
57 | ```js
58 | let num1 = 2,
59 | num2 = 2;
60 |
61 | console.log(++num1 ** 2); // 9
62 | console.log(num1); // 3
63 |
64 | console.log(num2-- ** 2); // 4
65 | console.log(num2); // 1
66 | ```
67 |
68 | In this example, `num1` is incremented before the exponentiation operator is applied, so `num1` becomes 3 and the result of the operation is 9. For `num2`, the value remains 2 for the exponentiation operation and then is decremented to 1.
69 |
70 | ## The Array.prototype.includes() Method
71 |
72 | You might recall that ECMAScript 6 added `String.prototype.includes()` in order to check whether certain substrings exist within a given string. Originally, ECMAScript 6 was also going to introduce an `Array.prototype.includes()` method to continue the trend of treating strings and arrays similarly. But the specification for `Array.prototype.includes()` was incomplete by the ECMAScript 6 deadline, and so `Array.prototype.includes()` ended up in ECMAScript 2016 instead.
73 |
74 | ### How to Use Array.prototype.includes()
75 |
76 | The `Array.prototype.includes()` method accepts two arguments: the value to search for and an optional index from which to start the search. When the second argument is provided, `includes()` starts the match from that index. (The default starting index is `0`.) The return value is `true` if the value is found inside the array and `false` if not. For example:
77 |
78 | ```js
79 | let values = [1, 2, 3];
80 |
81 | console.log(values.includes(1)); // true
82 | console.log(values.includes(0)); // false
83 |
84 | // start the search from index 2
85 | console.log(values.includes(1, 2)); // false
86 | ```
87 |
88 | Here, calling `values.includes()` returns `true` for the value of `1` and `false` for the value of `0` because `0` isn't in the array. When the second argument is used to start the search at index 2 (which contains the value `3`), the `values.includes()` method returns `false` because the number `1` is not found between index 2 and the end of the array.
89 |
90 | ### Value Comparison
91 |
92 | The value comparison performed by the `includes()` method uses the `===` operator with one exception: `NaN` is considered equal to `NaN` even though `NaN === NaN` evaluates to `false`. This is different than the behavior of the `indexOf()` method, which strictly uses `===` for comparison. To see the difference, consider this code:
93 |
94 | ```js
95 | let values = [1, NaN, 2];
96 |
97 | console.log(values.indexOf(NaN)); // -1
98 | console.log(values.includes(NaN)); // true
99 | ```
100 |
101 | The `values.indexOf()` method returns `-1` for `NaN` even though `NaN` is contained in the `values` array. On the other hand, `values.includes()` returns `true` for `NaN` because it uses a different value comparison operator.
102 |
103 | W> When you want to check just for the existence of a value in an array and don't need to know the index , I recommend using `includes()` because of the difference in how `NaN` is treated by the `includes()` and `indexOf()` methods. If you do need to know where in the array a value exists, then you have to use the `indexOf()` method.
104 |
105 | Another quirk of this implementation is that `+0` and `-0` are considered to be equal. In this case, the behavior of `indexOf()` and `includes()` is the same:
106 |
107 | ```js
108 | let values = [1, +0, 2];
109 |
110 | console.log(values.indexOf(-0)); // 1
111 | console.log(values.includes(-0)); // true
112 | ```
113 |
114 | Here, both `indexOf()` and `includes()` find `+0` when `-0` is passed because the two values are considered equal. Note that this is different than the behavior of the `Object.is()` method, which considers `+0` and `-0` to be different values.
115 |
116 | ## Change to Function-Scoped Strict Mode
117 |
118 | When strict mode was introduced in ECMAScript 5, the language was quite a bit simpler than it became in ECMAScript 6. Despite that, ECMAScript 6 still allowed you to specify strict mode using the `"use strict"` directive either in the global scope (which would make all code run in strict mode) or in a function scope (so only the function would run in strict mode). The latter ended up being a problem in ECMAScript 6 due to the more complex ways that parameters could be defined, specifically, with destructuring and default parameter values. To understand the problem, consider the following code:
119 |
120 | ```js
121 | function doSomething(first = this) {
122 | "use strict";
123 |
124 | return first;
125 | }
126 | ```
127 |
128 | Here, the named parameter `first` is assigned a default value of `this`. What would you expect the value of `first` to be? The ECMAScript 6 specification instructed JavaScript engines to treat the parameters as being run in strict mode in this case, so `this` should be equal to `undefined`. However, implementing parameters running in strict mode when `"use strict"` is present inside the function turned out to be quite difficult because parameter default values can be functions as well. This difficulty led to most JavaScript engines not implementing this feature (so `this` would be equal to the global object).
129 |
130 | As a result of the implementation difficulty, ECMAScript 2016 makes it illegal to have a `"use strict"` directive inside of a function whose parameters are either destructured or have default values. Only *simple parameter lists*, those that don't contain destructuring or default values, are allowed when `"use strict"` is present in the body of a function. Here are some examples:
131 |
132 | ```js
133 | // okay - using simple parameter list
134 | function okay(first, second) {
135 | "use strict";
136 |
137 | return first;
138 | }
139 |
140 | // syntax error
141 | function notOkay1(first, second=first) {
142 | "use strict";
143 |
144 | return first;
145 | }
146 |
147 | // syntax error
148 | function notOkay2({ first, second }) {
149 | "use strict";
150 |
151 | return first;
152 | }
153 | ```
154 |
155 | You can still use `"use strict"` with simple parameter lists, which is why `okay()` works as you would expect (the same as it would in ECMAScript 5). The `notOkay1()` function is a syntax error because you can no longer use `"use strict"` in functions with default parameter values. Similarly, the `notOkay2()` function is a syntax error because you can't use `"use strict"` in a function with destructured parameters.
156 |
157 | Overall, this change removes both a point of confusion for JavaScript developers and an implementation problem for JavaScript engines.
158 |
--------------------------------------------------------------------------------
/docs/00-Introduction.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # הקדמה
4 |
5 | אפיון השפה הידועה בשם
6 | JavaScript
7 | מוגדרות בתקן בעל הסימון
8 | ECMA-262.
9 | שפת התכנות המוגדרת בתקן זה נקראת
10 | ECMAScript.
11 | מה שמוכר לנו בתור
12 | JavaScript
13 | בדפדפנים
14 | וסביבת
15 | Node.js
16 | הוא למעשה הרחבה של
17 | ECMAScript.
18 |
19 | דפדפנים וסביבת
20 | Node.js
21 | מוסיפים פונקציונאליות בעזרת אובייקטים נוספים ומתודות, אבל עדיין הבסיס של השפה נשאר
22 | ECMAScript.
23 | ההתפתחות של תקן
24 | ECMA-262
25 | חיונית להצלחתה של שפת
26 | JavaScript.
27 | ספר זה סוקר את השינויים שנעשו בעדכון החשוב האחרון לשפה:
28 | ECMAScript 6.
29 |
30 | ## הדרך אל ECMAScript 6
31 |
32 | בשנת 2007,
33 | JavaScript
34 | היתה בצומת דרכים חשובה.
35 | הפופולאריות של
36 | Ajax
37 | גברה בעידן של אפליקציות ווב דינאמיות, בעוד ש
38 | JavaScript
39 | לא השתנתה מאז הגרסה השלישית של תקן
40 | ECMA-262
41 | שפורסמה בשנת
42 | 1999.
43 |
44 | TC-39 -
45 | הוועדה האחראית לקידום התפתחות
46 | ECMAScript
47 | הכינה טיוטה גדולה לאפיון
48 | ECMAScript 4.
49 | ECMAScript 4
50 | עשתה מספר עצום של שינויים, והוסיפה שינויים קטנים כמו גם גדולים בשפה.
51 | העדכונים כללו תחביר חדש, מודולים, מחלקות והורשה ממחלקות, איברים פרטיים באובייקטים, סוגים חדשים ועוד.
52 |
53 | ההיקף הנרחב של
54 | השינויים בגרסת
55 | ECMAScript 4
56 | יצר קרע בוועדה
57 | TC-39,
58 | כאשר מספר חברי ועדה טענו שהמהדורה הרביעית ניסתה להשיג יותר מדי.
59 | קבוצה של מפתחים מובילים מגוגל, יאהו ומייקרוסופט יצרו טיוטה אלטרנטיבית עבור
60 | ECMAScript
61 | שבמקור כונתה
62 | ECMAScript 3.1.
63 | המספר
64 | ״3.1״
65 | נועד לציין שמדובר בשיפור לתקן קיים.
66 |
67 | ECMAScript 3.1
68 | הכילה מעט שינויים תחביריים והתרכזה ב
69 | מאפייני תכונות אובייקטים
70 | תמיכה מובנית בפורמט
71 | JSON
72 | והוספת מתודות לאובייקטים קיימים.
73 |
74 | למרות שהיה ניסיון מסויים לחבר בין
75 | ECMAScript 3.1
76 | ו-
77 | ECMAScript 4
78 | ניסיון זה כשל בקרב תומכי שני המחנות והיה קושי רב לגשר בין שתי הגישות לגבי הדרך שבה השפה צריכה להתפתח.
79 |
80 | בשנת 2008,
81 | ברנדון איק,
82 | היוצר של
83 | JavaScript
84 | הודיע שוועדת
85 | TC-39
86 | תרכז את מאמציה בגרסה
87 | ECMAScript 3.1.
88 | הם יידחו את השינויים הגדולים בתחביר וביכולות השפה שתוכננו לגרסת
89 | ECMAScript 4
90 | לשלב מאוחר יותר, לאחר שתוסדר הגרסה הבאה של
91 | ECMAScript
92 |
93 | ולאחר מכן, כל חברי הוועדה יעסקו בהבאת החלקים הטובים ביותר של
94 | ECMAScript 3.1
95 | ו-4
96 | באפיון שקיבל את הכינוי
97 | ECMAScript Harmony.
98 |
99 | בסופו של דבר, תקן
100 | ECMAScript 3.1
101 | הוגדר בתור הגרסה החמישית של תקן
102 | ECMA-262
103 | או בכינוי הידוע יותר
104 | ECMAScript 5.
105 | גרסה
106 | ECMAScript 4
107 | מעולם לא הפכה לתקנית בכדי לא לגרום לבלבול
108 | לאחר הוצאת גרסה 5, החלה הוועדה את העבודה על מה שנקרא
109 | ECMAScript Harmony,
110 | כאשר
111 | ECMAScript 6
112 | הייתה הגרסה הראשונה ששוחררה ברוח ״הרמוניה״ זו.
113 |
114 | ECMAScript 6
115 | הייתה מוכנה סופית
116 | במהלך שנת 2015 ופורסמה באופן רשמי בשם
117 | "ECMAScript 2015"
118 | (ספר זה מתייחס אליה בתור
119 | ECMAScript 6 -
120 | השם המקובל בקרב מתכנתים בעולם).
121 |
122 | העדכונים בגרסה זו
123 | כוללים מנעד רחב של שינויים החל מאובייקטים חדשים, תבניות חדשות, שינויים תחביריים ומתודות חדשות על אובייקטים קיימים.
124 | החלק המלהיב ב-
125 | ECMAScript 6
126 | הוא שהשינויים בשפה מוכוונים לפתרון בעיות שמפתחים נתקלים בהם.
127 |
128 | ## על ספר זה
129 |
130 | הבנה טובה של התכונות והיכולות של
131 | ECMAScript 6
132 | הינה חיונית לכל מפתח
133 | JavaScript
134 | מעתה ואילך.
135 | העדכונים בשפה שהוכנסו לגרסת
136 | ECMAScript 6
137 | מהוות את הבסיס שעליו יתבסססו אפליקציות
138 | JavaScript
139 | בעתיד.
140 | למטרה זו נכתב ספר זה. תקוותי היא שתקראו ספר זה על מנת ללמוד על התכונות והיכולות של
141 | ECMAScript 6
142 | כך שתהיו מוכנים להתחיל להשתמש בתכונות והיכולות החדשות בכל עת שתזדקקו להם.
143 |
144 | ### תאימות לדפדפנים ול Node.js
145 |
146 | סביבות רבות שמריצות
147 | JavaScript -
148 | כמו דפדפנים ו
149 | Node.js ,
150 | עובדות על יישום בפועל של
151 | ECMAScript 6.
152 | ספר זה אינו עוסק בבעיות יישום בסביבות השונות אלא מתרכז בהגדרות התקן והתנהגות נכונה.
153 | לכן - ייתכן שבסביבות מסויימות תתקלו בבעיות תאימות לתקן, אשר אינן נכללות בספר זה.
154 |
155 | ### למי מיועד ספר זה
156 |
157 | ספר זה נועד להדריך את אלו המכירים
158 | JavaScript
159 | ו-
160 | ECMAScript 5.
161 | בעוד שהבנת השפה
162 | לעומק אינה הכרחית לשימוש בספר זה, היא תסייע לכם להבין את ההבדלים בין
163 | ECMAScript 5
164 | ובין
165 | ECMAScript 6.
166 | ספר זה נועד בעיקר למפתחים ברמה בינונית עד מתקדמת שמפתחים בסביבת דפדפן או
167 | Node.JS
168 | אשר מעוניינים ללמוד על התפתחויות עדכניות בשפה.
169 |
170 | ספר זה אינו למתחילים שמעולם לא תכנתו ב
171 | JavaScript.
172 | הנכם אמורים להיות בעלי הבנה בסיסית טובה בשפה בכדי להשתמש בספר זה.
173 |
174 | ### סקירה כללית
175 |
176 | כל אחד מ-13 הפרקים בספר מרחיב על היבט אחר של
177 | ECMAScript 6.
178 | פרקים רבים מתחילים בדיון על הבעיות שהשינויים שהופיעו ב
179 | ECMAScript 6
180 | נועדו לפתור, על מנת לספק הקשר רחב יותר לסיבות לשינויים אלו.
181 | כל פרק מכיל דוגמאות קוד על מנת לסייע לך ללמוד את התחביר החדש בשפה ומושגים חדשים.
182 |
183 | **פרק 1: כיצד עובדים משתנים במרחב בלוק**
184 | מדבר על
185 | `let`
186 | ו-
187 | `const`
188 | המחליפים ברמת בלוק של קוד עבור
189 | `var`
190 |
191 | **פרק 2: מחרוזות וביטויים רגולריים**
192 | סוקר יכולת נוספת חדשה לעיבוד על
193 | מחרוזות וכן מציג את הקונספט החדש של
194 | template strings.
195 |
196 | **פרק 3: פונקציות באקמהסקריפט 6**
197 | עוסק בשינויים הקשורים בפונקציות, שינויים הכוללים את המבנה של פונקציות חץ, ערך ברירת מחדל לפרמטרים של פונקציה ונושאים נוספים.
198 |
199 | **פרק 4: הרחבת פונקציונאליות של אובייקטים**
200 | מסביר את השינוי כיצד אובייקטים נוצרים, עוברים שינוי וכיצד משתמשים בהם. הנושאים כוללים שינוי בתחביר עבור אובייקט ליטראלס, ושיטות שיקוף
201 | (reflection)
202 | חדשות.
203 |
204 | **פרק 5: פירוק לשם גישה פשוטה יותר לנתונים**
205 | מציג פירוק באובייקטים ומערכים, דרך חדשה למשוך נתונים אובייקטים ומערכים בצורת קצרה ונוחה יותר.
206 |
207 | **פרק 6: סימבולים ותכונות מסוג סימבול**
208 | מציג את הקונספט של סימבולים, דרך חדשה להגדיר תכונות אובייקט.
209 | סימבול הוא סוג חדש של משתנה פרימיטיבי שניתן להשתמש בו על מנת להקשות על הגישה
210 | אל תכונות ומתודות של אובייקט
211 | (אבל לא למנוע אותה לחלוטין).
212 |
213 | **פרק 7: סט ומפה**
214 | מפרט את הסוגים החדשים של
215 | `Set`, `WeakSet`, `Map`,
216 | ו-
217 | `WeakMap`.
218 | סוגים חדשים אלו מרחיבים את השימושיות של מערך, מבחינת סמנטיקה וניהול זיכרון שהותאם במיוחד ל-
219 | JavaScript.
220 |
221 | **פרק 8: איטרטורים וגנרטורים**
222 | עוסק בתוספת של
223 | איטרטורים וגנרטורים
224 | לשפה. תכונות אלו מאפשרות עבודה עם אוסף של נתונים בצורה יעילה ועוצמתית שלא התאפשרה בגרסה הקודמת של JavaScript.
225 |
226 | **פרק 9: מחלקות**
227 | מציג את המימוש הרשמי הראשון של
228 | מחלקות ב
229 | JavaScript.
230 | הנושא של מחלקות היווה לעיתים תכופות לגורם מבלבל עבור מפתחים שהגיעו מרקע תכנותי בשפות אחרות
231 | לעיתים נקודת בילבול בין מה שקיים בשפות תכנות אחרות, התוספת של מחלקות לתחביר השפה
232 | מאפשר שימוש בשפה בצורה שהיא נוחה ומקוצרת יותר.
233 |
234 | **פרק 10: יכולות משופרות של מערך**
235 | מפרט את השינויים שהוכנסו למערכים רגילים ודרכים מעניינות חדשות בהם מערכים ניתנים לשימוש ב
236 | JavaScript.
237 |
238 | **פרק 11: פרומיס ותכנות אסינכרוני**
239 | מציג את אובייקט פרומיס
240 | (Promise)
241 | כחלק חדש בשפה.
242 | פרומיסים החלו בתור רעיון לשינוי שבסוף הצליחו להיות פופולריים בזכות תמיכה מאסיבית בספריות שונות.
243 | ECMAScript 6
244 | הכניסה פרומיסים לשפה בצורה רשמית והופכת אותם לזמינים בסביבת הפיתוח כברירת מחדל.
245 |
246 | **פרק 12: פרוקסי וממשק השיקוף**
247 | (Reflection API)
248 | מציג את הצורה הפורמאלית של
249 | ממשק השיקוף
250 | בשפת
251 | JavaScript
252 | אובייקט הפרוקסי
253 | החדש שמאפשר למפתח לתפוס כל אירוע של פעולה שמבוצעת על אובייקטים.
254 | פרוקסי
255 | מאפשר למפתח שליטה מוחלטת על האובייקט, וכך מציע לנו אפשרויות רבות לנהל אינטרקציה עם האובייקטים שלנו.
256 |
257 | **פרק 13: עטיפת קוד במודולים**
258 | מפרט את צורת כתיבת המודול
259 | הרשמית ב
260 | JavaScript.
261 | הכוונה מאחורי השינוי הייתה לתת אלטרנטיבה טובה לשיטות הגדרת המודול הלא רשמיות
262 | שהופיעו ברבות השנים.
263 |
264 | **נספח א: שינויים קטנים באקמהסקריפט 6**
265 | סוקר שינויים אחרים בגרסת
266 | ECMAScript 6.
267 | כאלו שלא נמצאים בשימוש שוטף
268 | או שלא התאימו לנושאים הנרחבים שנסקרו בכל פרק.
269 |
270 | **נספח ב: להבין אקמהסקריפט 7**
271 | מתאר את שתי התוספות לשפה בגרסת
272 | ECMAScript 7,
273 | שינויים שלא היו בעלי השפעה נרחבת על
274 | השפה כמו השינויים בגרסת
275 | ECMAScript 6.
276 |
277 | ### סימונים מוסכמים שבשימוש בספר זה
278 |
279 | להלן הסימונים הטיפוגראפיים שנמצאים בשימוש בספר זה
280 |
281 | * *איטליקס*
282 | מסמן הגדרה חדשה
283 | * `רוחב קבוע`
284 | מסמן קטע קוד או שם קובץ
285 |
286 | בנוסף, קטעי קוד ארוכים מוכלים במסגרות קוד כדוגמת:
287 |
288 |
2 |
3 | # הקדמה
4 |
5 | אפיון השפה הידועה בשם
6 | JavaScript
7 | מוגדרות בתקן בעל הסימון
8 | ECMA-262.
9 | שפת התכנות המוגדרת בתקן זה נקראת
10 | ECMAScript.
11 | מה שמוכר לנו בתור
12 | JavaScript
13 | בדפדפנים
14 | וסביבת
15 | Node.js
16 | הוא למעשה הרחבה של
17 | ECMAScript.
18 |
19 | דפדפנים וסביבת
20 | Node.js
21 | מוסיפים פונקציונאליות בעזרת אובייקטים נוספים ומתודות, אבל עדיין הבסיס של השפה נשאר
22 | ECMAScript.
23 | ההתפתחות של תקן
24 | ECMA-262
25 | חיונית להצלחתה של שפת
26 | JavaScript.
27 | ספר זה סוקר את השינויים שנעשו בעדכון החשוב האחרון לשפה:
28 | ECMAScript 6.
29 |
30 | ## הדרך אל ECMAScript 6
31 |
32 | בשנת 2007,
33 | JavaScript
34 | היתה בצומת דרכים חשובה.
35 | הפופולאריות של
36 | Ajax
37 | גברה בעידן של אפליקציות ווב דינאמיות, בעוד ש
38 | JavaScript
39 | לא השתנתה מאז הגרסה השלישית של תקן
40 | ECMA-262
41 | שפורסמה בשנת
42 | 1999.
43 |
44 | TC-39 -
45 | הוועדה האחראית לקידום התפתחות
46 | ECMAScript
47 | הכינה טיוטה גדולה לאפיון
48 | ECMAScript 4.
49 | ECMAScript 4
50 | עשתה מספר עצום של שינויים, והוסיפה שינויים קטנים כמו גם גדולים בשפה.
51 | העדכונים כללו תחביר חדש, מודולים, מחלקות והורשה ממחלקות, איברים פרטיים באובייקטים, סוגים חדשים ועוד.
52 |
53 | ההיקף הנרחב של
54 | השינויים בגרסת
55 | ECMAScript 4
56 | יצר קרע בוועדה
57 | TC-39,
58 | כאשר מספר חברי ועדה טענו שהמהדורה הרביעית ניסתה להשיג יותר מדי.
59 | קבוצה של מפתחים מובילים מגוגל, יאהו ומייקרוסופט יצרו טיוטה אלטרנטיבית עבור
60 | ECMAScript
61 | שבמקור כונתה
62 | ECMAScript 3.1.
63 | המספר
64 | ״3.1״
65 | נועד לציין שמדובר בשיפור לתקן קיים.
66 |
67 | ECMAScript 3.1
68 | הכילה מעט שינויים תחביריים והתרכזה ב
69 | מאפייני תכונות אובייקטים
70 | תמיכה מובנית בפורמט
71 | JSON
72 | והוספת מתודות לאובייקטים קיימים.
73 |
74 | למרות שהיה ניסיון מסויים לחבר בין
75 | ECMAScript 3.1
76 | ו-
77 | ECMAScript 4
78 | ניסיון זה כשל בקרב תומכי שני המחנות והיה קושי רב לגשר בין שתי הגישות לגבי הדרך שבה השפה צריכה להתפתח.
79 |
80 | בשנת 2008,
81 | ברנדון איק,
82 | היוצר של
83 | JavaScript
84 | הודיע שוועדת
85 | TC-39
86 | תרכז את מאמציה בגרסה
87 | ECMAScript 3.1.
88 | הם יידחו את השינויים הגדולים בתחביר וביכולות השפה שתוכננו לגרסת
89 | ECMAScript 4
90 | לשלב מאוחר יותר, לאחר שתוסדר הגרסה הבאה של
91 | ECMAScript
92 |
93 | ולאחר מכן, כל חברי הוועדה יעסקו בהבאת החלקים הטובים ביותר של
94 | ECMAScript 3.1
95 | ו-4
96 | באפיון שקיבל את הכינוי
97 | ECMAScript Harmony.
98 |
99 | בסופו של דבר, תקן
100 | ECMAScript 3.1
101 | הוגדר בתור הגרסה החמישית של תקן
102 | ECMA-262
103 | או בכינוי הידוע יותר
104 | ECMAScript 5.
105 | גרסה
106 | ECMAScript 4
107 | מעולם לא הפכה לתקנית בכדי לא לגרום לבלבול
108 | לאחר הוצאת גרסה 5, החלה הוועדה את העבודה על מה שנקרא
109 | ECMAScript Harmony,
110 | כאשר
111 | ECMAScript 6
112 | הייתה הגרסה הראשונה ששוחררה ברוח ״הרמוניה״ זו.
113 |
114 | ECMAScript 6
115 | הייתה מוכנה סופית
116 | במהלך שנת 2015 ופורסמה באופן רשמי בשם
117 | "ECMAScript 2015"
118 | (ספר זה מתייחס אליה בתור
119 | ECMAScript 6 -
120 | השם המקובל בקרב מתכנתים בעולם).
121 |
122 | העדכונים בגרסה זו
123 | כוללים מנעד רחב של שינויים החל מאובייקטים חדשים, תבניות חדשות, שינויים תחביריים ומתודות חדשות על אובייקטים קיימים.
124 | החלק המלהיב ב-
125 | ECMAScript 6
126 | הוא שהשינויים בשפה מוכוונים לפתרון בעיות שמפתחים נתקלים בהם.
127 |
128 | ## על ספר זה
129 |
130 | הבנה טובה של התכונות והיכולות של
131 | ECMAScript 6
132 | הינה חיונית לכל מפתח
133 | JavaScript
134 | מעתה ואילך.
135 | העדכונים בשפה שהוכנסו לגרסת
136 | ECMAScript 6
137 | מהוות את הבסיס שעליו יתבסססו אפליקציות
138 | JavaScript
139 | בעתיד.
140 | למטרה זו נכתב ספר זה. תקוותי היא שתקראו ספר זה על מנת ללמוד על התכונות והיכולות של
141 | ECMAScript 6
142 | כך שתהיו מוכנים להתחיל להשתמש בתכונות והיכולות החדשות בכל עת שתזדקקו להם.
143 |
144 | ### תאימות לדפדפנים ול Node.js
145 |
146 | סביבות רבות שמריצות
147 | JavaScript -
148 | כמו דפדפנים ו
149 | Node.js ,
150 | עובדות על יישום בפועל של
151 | ECMAScript 6.
152 | ספר זה אינו עוסק בבעיות יישום בסביבות השונות אלא מתרכז בהגדרות התקן והתנהגות נכונה.
153 | לכן - ייתכן שבסביבות מסויימות תתקלו בבעיות תאימות לתקן, אשר אינן נכללות בספר זה.
154 |
155 | ### למי מיועד ספר זה
156 |
157 | ספר זה נועד להדריך את אלו המכירים
158 | JavaScript
159 | ו-
160 | ECMAScript 5.
161 | בעוד שהבנת השפה
162 | לעומק אינה הכרחית לשימוש בספר זה, היא תסייע לכם להבין את ההבדלים בין
163 | ECMAScript 5
164 | ובין
165 | ECMAScript 6.
166 | ספר זה נועד בעיקר למפתחים ברמה בינונית עד מתקדמת שמפתחים בסביבת דפדפן או
167 | Node.JS
168 | אשר מעוניינים ללמוד על התפתחויות עדכניות בשפה.
169 |
170 | ספר זה אינו למתחילים שמעולם לא תכנתו ב
171 | JavaScript.
172 | הנכם אמורים להיות בעלי הבנה בסיסית טובה בשפה בכדי להשתמש בספר זה.
173 |
174 | ### סקירה כללית
175 |
176 | כל אחד מ-13 הפרקים בספר מרחיב על היבט אחר של
177 | ECMAScript 6.
178 | פרקים רבים מתחילים בדיון על הבעיות שהשינויים שהופיעו ב
179 | ECMAScript 6
180 | נועדו לפתור, על מנת לספק הקשר רחב יותר לסיבות לשינויים אלו.
181 | כל פרק מכיל דוגמאות קוד על מנת לסייע לך ללמוד את התחביר החדש בשפה ומושגים חדשים.
182 |
183 | **פרק 1: כיצד עובדים משתנים במרחב בלוק**
184 | מדבר על
185 | `let`
186 | ו-
187 | `const`
188 | המחליפים ברמת בלוק של קוד עבור
189 | `var`
190 |
191 | **פרק 2: מחרוזות וביטויים רגולריים**
192 | סוקר יכולת נוספת חדשה לעיבוד על
193 | מחרוזות וכן מציג את הקונספט החדש של
194 | template strings.
195 |
196 | **פרק 3: פונקציות באקמהסקריפט 6**
197 | עוסק בשינויים הקשורים בפונקציות, שינויים הכוללים את המבנה של פונקציות חץ, ערך ברירת מחדל לפרמטרים של פונקציה ונושאים נוספים.
198 |
199 | **פרק 4: הרחבת פונקציונאליות של אובייקטים**
200 | מסביר את השינוי כיצד אובייקטים נוצרים, עוברים שינוי וכיצד משתמשים בהם. הנושאים כוללים שינוי בתחביר עבור אובייקט ליטראלס, ושיטות שיקוף
201 | (reflection)
202 | חדשות.
203 |
204 | **פרק 5: פירוק לשם גישה פשוטה יותר לנתונים**
205 | מציג פירוק באובייקטים ומערכים, דרך חדשה למשוך נתונים אובייקטים ומערכים בצורת קצרה ונוחה יותר.
206 |
207 | **פרק 6: סימבולים ותכונות מסוג סימבול**
208 | מציג את הקונספט של סימבולים, דרך חדשה להגדיר תכונות אובייקט.
209 | סימבול הוא סוג חדש של משתנה פרימיטיבי שניתן להשתמש בו על מנת להקשות על הגישה
210 | אל תכונות ומתודות של אובייקט
211 | (אבל לא למנוע אותה לחלוטין).
212 |
213 | **פרק 7: סט ומפה**
214 | מפרט את הסוגים החדשים של
215 | `Set`, `WeakSet`, `Map`,
216 | ו-
217 | `WeakMap`.
218 | סוגים חדשים אלו מרחיבים את השימושיות של מערך, מבחינת סמנטיקה וניהול זיכרון שהותאם במיוחד ל-
219 | JavaScript.
220 |
221 | **פרק 8: איטרטורים וגנרטורים**
222 | עוסק בתוספת של
223 | איטרטורים וגנרטורים
224 | לשפה. תכונות אלו מאפשרות עבודה עם אוסף של נתונים בצורה יעילה ועוצמתית שלא התאפשרה בגרסה הקודמת של JavaScript.
225 |
226 | **פרק 9: מחלקות**
227 | מציג את המימוש הרשמי הראשון של
228 | מחלקות ב
229 | JavaScript.
230 | הנושא של מחלקות היווה לעיתים תכופות לגורם מבלבל עבור מפתחים שהגיעו מרקע תכנותי בשפות אחרות
231 | לעיתים נקודת בילבול בין מה שקיים בשפות תכנות אחרות, התוספת של מחלקות לתחביר השפה
232 | מאפשר שימוש בשפה בצורה שהיא נוחה ומקוצרת יותר.
233 |
234 | **פרק 10: יכולות משופרות של מערך**
235 | מפרט את השינויים שהוכנסו למערכים רגילים ודרכים מעניינות חדשות בהם מערכים ניתנים לשימוש ב
236 | JavaScript.
237 |
238 | **פרק 11: פרומיס ותכנות אסינכרוני**
239 | מציג את אובייקט פרומיס
240 | (Promise)
241 | כחלק חדש בשפה.
242 | פרומיסים החלו בתור רעיון לשינוי שבסוף הצליחו להיות פופולריים בזכות תמיכה מאסיבית בספריות שונות.
243 | ECMAScript 6
244 | הכניסה פרומיסים לשפה בצורה רשמית והופכת אותם לזמינים בסביבת הפיתוח כברירת מחדל.
245 |
246 | **פרק 12: פרוקסי וממשק השיקוף**
247 | (Reflection API)
248 | מציג את הצורה הפורמאלית של
249 | ממשק השיקוף
250 | בשפת
251 | JavaScript
252 | אובייקט הפרוקסי
253 | החדש שמאפשר למפתח לתפוס כל אירוע של פעולה שמבוצעת על אובייקטים.
254 | פרוקסי
255 | מאפשר למפתח שליטה מוחלטת על האובייקט, וכך מציע לנו אפשרויות רבות לנהל אינטרקציה עם האובייקטים שלנו.
256 |
257 | **פרק 13: עטיפת קוד במודולים**
258 | מפרט את צורת כתיבת המודול
259 | הרשמית ב
260 | JavaScript.
261 | הכוונה מאחורי השינוי הייתה לתת אלטרנטיבה טובה לשיטות הגדרת המודול הלא רשמיות
262 | שהופיעו ברבות השנים.
263 |
264 | **נספח א: שינויים קטנים באקמהסקריפט 6**
265 | סוקר שינויים אחרים בגרסת
266 | ECMAScript 6.
267 | כאלו שלא נמצאים בשימוש שוטף
268 | או שלא התאימו לנושאים הנרחבים שנסקרו בכל פרק.
269 |
270 | **נספח ב: להבין אקמהסקריפט 7**
271 | מתאר את שתי התוספות לשפה בגרסת
272 | ECMAScript 7,
273 | שינויים שלא היו בעלי השפעה נרחבת על
274 | השפה כמו השינויים בגרסת
275 | ECMAScript 6.
276 |
277 | ### סימונים מוסכמים שבשימוש בספר זה
278 |
279 | להלן הסימונים הטיפוגראפיים שנמצאים בשימוש בספר זה
280 |
281 | * *איטליקס*
282 | מסמן הגדרה חדשה
283 | * `רוחב קבוע`
284 | מסמן קטע קוד או שם קובץ
285 |
286 | בנוסף, קטעי קוד ארוכים מוכלים במסגרות קוד כדוגמת:
287 |
288 |
320 |
321 | ### עזרה ותמיכה
322 |
323 | תוכלו להעלות נושאים, הצעות שיפור או פול רקוואסט לספר זה על ידי בכתובת:
324 |
325 | [https://github.com/ronen-e/understandinges6](https://github.com/ronen-e/understandinges6)
326 |
327 | ### הוקרה
328 |
329 | תודה לג׳ניפר גריפית-דלגדו, אליסון לו, לכולם בחברת
330 | No Starch Press
331 | על תמיכתם והעזרה שנתנו בכתיבת ספר זה. ההבנה והסבלנות מצידם בעת שהפרודקטיביות האטה לקצב זחילה במהלך תקופת מחלתי הממושכת לא תישכח לעולם.
332 |
333 | אני אסיר תודה לעינו הבוחנת של יורי זייצב בתור עורך טכני ולד״ר אקסל רושמאייר עבור המשוב שנתן ומספר שיחות שעזרו להבהיר מספר מושגים שנדונו בספר זה.
334 |
335 | תודה לכל מי שהגיש תיקונים לגרסה של הספר שמופיעה בגיטהאב:
336 | ShMcK,
337 | רונן אלסטר, ריק וולדרון, בלקטייל, פול סאלטס, לוניביז, איגור סקוהאר,
338 | jakub-g,
339 | דייויד צ׳אנג, קוון סוויני, קייל סימפסון, פיטר באקונדי, פיליפ בוריסוב, שון היקסון, סטיבן פוט,
340 | kavun,
341 | דן קילפ, דארן האסקי, ג׳ייקוב נאברסקי, ג׳אמונד פרגוסון, ג׳וש לובאווי, מריאן רוסנאק, ניקולס פונירוס, רובין פוקורני, רומאן לו, יאנג סו,
342 | alexyans,
343 | robertd, 404,
344 | אהרון דנדי, עבדאלפתח פופולה, אדם ריצ׳מר, אחמד עלי, אלכסנדר דינדיק,
345 | Arjunkumar,
346 | בן רגנספן, קרלו קונסטנטיני, דמיטרי סובורוב, קייל פולוק,
347 | Mallory,
348 | אריק סנדהאל, איתן בראון, יוג׳ין זובארב, פרנצ׳סקו פונגילופי, ג׳ייק צ׳אמפיון, ג׳רמי קאני, ג׳ו איימס, יורי זייצב, קייל וורסלי, קווין לוזאנדייר, לואיס אליס, מוחסן עזימי, נאבאניט׳ קסאבאן, ניק בוטומלי, נילס דקר, פאהלבי פיקרי אוליה, פראיאג ורמה, ראג׳ אנאנד, רוס ג׳רבאסי, רוי לינג, סארבוטאם באנדיופאדהיאי
349 | ו
350 | Shidhin.
351 |
352 | כמו כן תודה לכל מי שתמך בספר זה בפטראון:
353 | קייסי ויסקו
354 |
355 |
356 |
--------------------------------------------------------------------------------
/manuscript/00-Introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | The JavaScript core language features are defined in a standard called ECMA-262. The language defined in this standard is called ECMAScript. What you know as JavaScript in browsers and Node.js is actually a superset of ECMAScript. Browsers and Node.js add more functionality through additional objects and methods, but the core of the language remains as defined in ECMAScript. The ongoing development of ECMA-262 is vital to the success of JavaScript as a whole, and this book covers the changes brought about by the most recent major update to the language: ECMAScript 6.
4 |
5 | ## The Road to ECMAScript 6
6 |
7 | In 2007, JavaScript was at a crossroads. The popularity of Ajax was ushering in a new age of dynamic web applications, while JavaScript hadn't changed since the third edition of ECMA-262 was published in 1999. TC-39, the committee responsible for driving the ECMAScript development process, put together a large draft specification for ECMAScript 4. ECMAScript 4 was massive in scope, introducing changes both small and large to the language. Updated features included new syntax, modules, classes, classical inheritance, private object members, optional type annotations, and more.
8 |
9 | The scope of the ECMAScript 4 changes caused a rift to form in TC-39, with some members feeling that the fourth edition was trying to accomplish too much. A group of leaders from Yahoo, Google, and Microsoft created an alternate proposal for the next version of ECMAScript that they initially called ECMAScript 3.1. The "3.1" was intended to show that this was an incremental change to the existing standard.
10 |
11 | ECMAScript 3.1 introduced very few syntax changes, instead focusing on property attributes, native JSON support, and adding methods to already-existing objects. Although there was an early attempt to reconcile ECMAScript 3.1 and ECMAScript 4, this ultimately failed as the two camps had difficulty with the very different perspectives on how the language should grow.
12 |
13 | In 2008, Brendan Eich, the creator of JavaScript, announced that TC-39 would focus its efforts on standardizing ECMAScript 3.1. They would table the major syntax and feature changes of ECMAScript 4 until after the next version of ECMAScript was standardized, and all members of the committee would work to bring the best pieces of ECMAScript 3.1 and 4 together after that point into an effort initially nicknamed ECMAScript Harmony.
14 |
15 | ECMAScript 3.1 was eventually standardized as the fifth edition of ECMA-262, also described as ECMAScript 5. The committee never released an ECMAScript 4 standard to avoid confusion with the now-defunct effort of the same name. Work then began on ECMAScript Harmony, with ECMAScript 6 being the first standard released in this new "harmonious" spirit.
16 |
17 | ECMAScript 6 reached feature complete status in 2015 and was formally dubbed "ECMAScript 2015." (But this text still refers to it as ECMAScript 6, the name most familiar to developers.) The features vary widely from completely new objects and patterns to syntax changes to new methods on existing objects. The exciting thing about ECMAScript 6 is that all of its changes are geared toward solving problems that developers actually face.
18 |
19 | ## About This Book
20 |
21 | A good understanding of ECMAScript 6 features is key for all JavaScript developers going forward. The language features introduced in ECMAScript 6 represent the foundation upon which JavaScript applications will be built for the foreseeable future. That's where this book comes in. My hope is that you'll read this book to learn about ECMAScript 6 features so that you'll be ready to start using them as soon as you need to.
22 |
23 | ### Browser and Node.js Compatibility
24 |
25 | Many JavaScript environments, such as web browsers and Node.js, are actively working on implementing ECMAScript 6. This book doesn't attempt to address the inconsistencies between implementations and instead focuses on what the specification defines as the correct behavior. As such, it's possible that your JavaScript environment may not conform to the behavior described in this book.
26 |
27 | ### Who This Book is For
28 |
29 | This book is intended as a guide for those who are already familiar with JavaScript and ECMAScript 5. While a deep understanding of the language isn't necessary to use this book, it will help you understand the differences between ECMAScript 5 and 6. In particular, this book is aimed at intermediate-to-advanced JavaScript developers programming for a browser or Node.js environment who want to learn about the latest developments in the language.
30 |
31 | This book is not for beginners who have never written JavaScript. You will need to have a good basic understanding of the language to make use of this book.
32 |
33 | ### Overview
34 |
35 | Each of this book's thirteen chapters covers a different aspect of ECMAScript 6. Many chapters start by discussing problems that ECMAScript 6 changes were made to solve, to give you a broader context for those changes, and all chapters include code examples to help you learn new syntax and concepts.
36 |
37 | **Chapter 1: How Block Bindings Work** talks about `let` and `const`, the block-level replacement for `var`.
38 |
39 | **Chapter 2: Strings and Regular Expressions** covers additional functionality for string manipulation and inspection as well as the introduction of template strings.
40 |
41 | **Chapter 3: Functions in ECMAScript 6** discusses the various changes to functions. This includes the arrow function form, default parameters, rest parameters, and more.
42 |
43 | **Chapter 4: Expanded Object Functionality** explains the changes to how objects are created, modified, and used. Topics include changes to object literal syntax, and new reflection methods.
44 |
45 | **Chapter 5: Destructuring for Easier Data Access** introduces object and array destructuring, which allow you to decompose objects and arrays using a concise syntax.
46 |
47 | **Chapter 6: Symbols and Symbol Properties** introduces the concept of symbols, a new way to define properties. Symbols are a new primitive type that can be used to obscure (but not hide) object properties and methods.
48 |
49 | **Chapter 7: Sets and Maps** details the new collection types of `Set`, `WeakSet`, `Map`, and `WeakMap`. These types expand on the usefulness of arrays by adding semantics, de-duping, and memory management designed specifically for JavaScript.
50 |
51 | **Chapter 8: Iterators and Generators** discusses the addition of iterators and generators to the language. These features allow you to work with collections of data in powerful ways that were not possible in previous versions of JavaScript.
52 |
53 | **Chapter 9: Introducing JavaScript Classes** introduces the first formal concept of classes in JavaScript. Often a point of confusion for those coming from other languages, the addition of class syntax in JavaScript makes the language more approachable to others and more concise for enthusiasts.
54 |
55 | **Chapter 10: Improved Array Capabilities** details the changes to native arrays and the interesting new ways they can be used in JavaScript.
56 |
57 | **Chapter 11: Promises and Asynchronous Programming** introduces promises as a new part of the language. Promises were a grassroots effort that eventually took off and gained in popularity due to extensive library support. ECMAScript 6 formalizes promises and makes them available by default.
58 |
59 | **Chapter 12: Proxies and the Reflection API** introduces the formalized reflection API for JavaScript and the new proxy object that allows you to intercept every operation performed on an object. Proxies give developers unprecedented control over objects and, as such, unlimited possibilities for defining new interaction patterns.
60 |
61 | **Chapter 13: Encapsulating Code with Modules** details the official module format for JavaScript. The intent is that these modules can replace the numerous ad-hoc module definition formats that have appeared over the years.
62 |
63 | **Appendix A: Smaller ECMAScript 6 Changes** covers other changes implemented in ECMAScript 6 that you'll use less frequently or that didn't quite fit into the broader major topics covered in each chapter.
64 |
65 | **Appendix B: Understanding ECMAScript 7 (2016)** describes the two additions to the standard that were implemented for ECMAScript 7, which didn't impact JavaScript nearly as much as ECMAScript 6.
66 |
67 | ### Conventions Used
68 |
69 | The following typographical conventions are used in this book:
70 |
71 | * *Italics* introduces new terms
72 | * `Constant width` indicates a piece of code or filename
73 |
74 | Additionally, longer code examples are contained in constant width code blocks such as:
75 |
76 | ```js
77 | function doSomething() {
78 | // empty
79 | }
80 | ```
81 |
82 | Within a code block, comments to the right of a `console.log()` statement indicate the output you'll see in the browser or Node.js console when the code is executed, for example:
83 |
84 | ```js
85 | console.log("Hi"); // "Hi"
86 | ```
87 |
88 | If a line of code in a code block throws an error, this is also indicated to the right of the code:
89 |
90 | ```js
91 | doSomething(); // error!
92 | ```
93 |
94 | ### Help and Support
95 |
96 | You can file issues, suggest changes, and open pull requests against this book by visiting: [https://github.com/nzakas/understandinges6](https://github.com/nzakas/understandinges6)
97 |
98 |
100 |
101 | If you have questions as you read this book, please send a message to my mailing list: [http://groups.google.com/group/zakasbooks](http://groups.google.com/group/zakasbooks).
102 |
103 | ## Acknowledgments
104 |
105 | Thanks to Jennifer Griffith-Delgado, Alison Law, and everyone at No Starch Press for their support and help with this book. Their understanding and patience as my productivity slowed to a crawl during my extended illness is something I will never forget.
106 |
107 | I'm grateful for the watchful eye of Juriy Zaytsev as tech editor and to Dr. Axel Rauschmayer for his feedback and several conversations that helped to clarify some of the concepts discussed in this book.
108 |
109 | Thanks to everyone who submitted fixes to the version of this book that is hosted on GitHub: ShMcK, Ronen Elster, Rick Waldron, blacktail, Paul Salaets, Lonniebiz, Igor Skuhar, jakub-g, David Chang, Kevin Sweeney, Kyle Simpson, Peter Bakondy, Philip Borisov, Shaun Hickson, Steven Foote, kavun, Dan Kielp, Darren Huskie, Jakub Narębski, Jamund Ferguson, Josh Lubaway, Marián Rusnák, Nikolas Poniros, Robin Pokorný, Roman Lo, Yang Su, alexyans, robertd, 404, Aaron Dandy, AbdulFattah Popoola, Adam Richeimer, Ahmad Ali, Aleksandar Djindjic, Arjunkumar, Ben Regenspan, Carlo Costantini, Dmitri Suvorov, Kyle Pollock, Mallory, Erik Sundahl, Ethan Brown, Eugene Zubarev, Francesco Pongiluppi, Jake Champion, Jeremy Caney, Joe Eames, Juriy Zaytsev, Kale Worsley, Kevin Lozandier, Lewis Ellis, Mohsen Azimi, Navaneeth Kesavan, Nick Bottomley, Niels Dequeker, Pahlevi Fikri Auliya, Prayag Verma, Raj Anand, Ross Gerbasi, Roy Ling, Sarbbottam Bandyopadhyay, and Shidhin.
110 |
111 | Also, thanks to everyone who supported this book on Patreon: Casey Visco.
112 |
--------------------------------------------------------------------------------
/manuscript/A-Other-Changes.md:
--------------------------------------------------------------------------------
1 | # Appendix A: Smaller Changes
2 |
3 | Along with the major changes this book has already covered, ECMAScript 6 made several other changes that are smaller but still helpful in improving JavaScript. Those changes include making integers easier to use, adding new methods for calculations, a tweak to Unicode identifiers, and formalizing the`__proto__` property. I describe all of those in this appendix.
4 |
5 | ## Working with Integers
6 |
7 | JavaScript uses the IEEE 754 encoding system to represent both integers and floats, which has caused a lot of confusion over the years. The language takes great pains to ensure that developers don't need to worry about the details of number encoding, but problems still leak through from time to time. ECMAScript 6 seeks to address this by making integers easier to identify and work with.
8 |
9 | ### Identifying Integers
10 |
11 | First, ECMAScript 6 added the `Number.isInteger()` method, which can determine whether a value represents an integer in JavaScript. While JavaScript uses IEEE 754 to represent both types of numbers, floats and integers are stored differently. The `Number.isInteger()` method takes advantage of that, and when the method is called on a value, the JavaScript engine looks at the underlying representation of the value to determine whether that value is an integer. That means numbers that look like floats might actually be stored as integers and cause `Number.isInteger()` to return `true`. For example:
12 |
13 | ```js
14 | console.log(Number.isInteger(25)); // true
15 | console.log(Number.isInteger(25.0)); // true
16 | console.log(Number.isInteger(25.1)); // false
17 | ```
18 |
19 | In this code, `Number.isInteger()` returns `true` for both `25` and `25.0` even though the latter looks like a float. Simply adding a decimal point to a number doesn't automatically make it a float in JavaScript. Since `25.0` is really just `25`, it is stored as an integer. The number `25.1`, however, is stored as a float because there is a fraction value.
20 |
21 | ### Safe Integers
22 |
23 | IEEE 754 can only accurately represent integers between -2^53^ and 2^53^, and outside this "safe" range, binary representations end up reused for multiple numeric values. That means JavaScript can only safely represent integers within the IEEE 754 range before problems become apparent. For instance, consider this code:
24 |
25 | ```js
26 | console.log(Math.pow(2, 53)); // 9007199254740992
27 | console.log(Math.pow(2, 53) + 1); // 9007199254740992
28 | ```
29 |
30 | This example doesn't contain a typo, yet two different numbers are represented by the same JavaScript integer. The effect becomes more prevalent the further the value falls outside the safe range.
31 |
32 | ECMAScript 6 introduced the `Number.isSafeInteger()` method to better identify integers that the language can accurately represent. It also added the `Number.MAX_SAFE_INTEGER` and `Number.MIN_SAFE_INTEGER` properties to represent the upper and lower bounds of the integer range, respectively. The `Number.isSafeInteger()` method ensures that a value is an integer and falls within the safe range of integer values, as in this example:
33 |
34 | ```js
35 | var inside = Number.MAX_SAFE_INTEGER,
36 | outside = inside + 1;
37 |
38 | console.log(Number.isInteger(inside)); // true
39 | console.log(Number.isSafeInteger(inside)); // true
40 |
41 | console.log(Number.isInteger(outside)); // true
42 | console.log(Number.isSafeInteger(outside)); // false
43 | ```
44 |
45 | The number `inside` is the largest safe integer, so it returns `true` for both the `Number.isInteger()` and `Number.isSafeInteger()` methods. The number `outside` is the first questionable integer value, and it isn't considered safe even though it's still an integer.
46 |
47 | Most of the time, you only want to deal with safe integers when doing integer arithmetic or comparisons in JavaScript, so using `Number.isSafeInteger()` as part of input validation is a good idea.
48 |
49 | ## New Math Methods
50 |
51 | The new emphasis on gaming and graphics that led ECMAScript 6 to include typed arrays in JavaScript also led to the realization that a JavaScript engine could do many mathematical calculations more efficiently. But optimization strategies like asm.js, which works on a subset of JavaScript to improve performance, need more information to perform calculations in the fastest way possible. For instance, knowing whether the numbers should be treated as 32-bit integers or as 64-bit floats is important for hardware-based operations, which are much faster than software-based operations.
52 |
53 | As a result, ECMAScript 6 added several methods to the `Math` object to improve the speed of common mathematical calculations. Improving the speed of common calculations also improves the overall speed of applications that perform many calculations, such as graphics programs. The new methods are listed below:
54 |
55 | * `Math.acosh(x)` Returns the inverse hyperbolic cosine of `x`.
56 | * `Math.asinh(x)` Returns the inverse hyperbolic sine of `x`.
57 | * `Math.atanh(x)` Returns the inverse hyperbolic tangent of `x`
58 | * `Math.cbrt(x)` Returns the cubed root of `x`.
59 | * `Math.clz32(x)` Returns the number of leading zero bits in the 32-bit integer representation of `x`.
60 | * `Math.cosh(x)` Returns the hyperbolic cosine of `x`.
61 | * `Math.expm1(x)` Returns the result of subtracting 1 from the exponential function of `x`
62 | * `Math.fround(x)` Returns the nearest single-precision float of `x`.
63 | * `Math.hypot(...values)` Returns the square root of the sum of the squares of each argument.
64 | * `Math.imul(x, y)` Returns the result of performing true 32-bit multiplication of the two arguments.
65 | * `Math.log1p(x)` Returns the natural logarithm of `1 + x`.
66 | * `Math.log10(x)` Returns the base 10 logarithm of `x`.
67 | * `Math.log2(x)` Returns the base 2 logarithm of `x`.
68 | * `Math.sign(x)` Returns -1 if the `x` is negative, 0 if `x` is +0 or -0, or 1 if `x` is positive.
69 | * `Math.sinh(x)` Returns the hyperbolic sine of `x`.
70 | * `Math.tanh(x)` Returns the hyperbolic tangent of `x`.
71 | * `Math.trunc(x)` Removes fraction digits from a float and returns an integer.
72 |
73 | It's beyond the scope of this book to explain each new method and what it does in detail. But if your application needs to do a reasonably common calculation, be sure to check the new `Math` methods before implementing it yourself.
74 |
75 | ## Unicode Identifiers
76 |
77 | ECMAScript 6 offers better Unicode support than previous versions of JavaScript, and it also changes what characters may be used as identifiers. In ECMAScript 5, it was already possible to use Unicode escape sequences for identifiers. For example:
78 |
79 | ```js
80 | // Valid in ECMAScript 5 and 6
81 | var \u0061 = "abc";
82 |
83 | console.log(\u0061); // "abc"
84 |
85 | // equivalent to:
86 | console.log(a); // "abc"
87 | ```
88 |
89 | After the `var` statement in this example, you can use either `\u0061` or `a` to access the variable. In ECMAScript 6, you can also use Unicode code point escape sequences as identifiers, like this:
90 |
91 | ```js
92 | // Valid in ECMAScript 5 and 6
93 | var \u{61} = "abc";
94 |
95 | console.log(\u{61}); // "abc"
96 |
97 | // equivalent to:
98 | console.log(a); // "abc"
99 | ```
100 |
101 | This example just replaces `\u0061` with its code point equivalent. Otherwise, it does exactly the same thing as the previous example.
102 |
103 | Additionally, ECMAScript 6 formally specifies valid identifiers in terms of [Unicode Standard Annex #31: Unicode Identifier and Pattern Syntax](http://unicode.org/reports/tr31/), which gives the following rules:
104 |
105 | 1. The first character must be `$`, `_`, or any Unicode symbol with a derived core property of `ID_Start`.
106 | 1. Each subsequent character must be `$`, `_`, `\u200c` (a zero-width non-joiner), `\u200d` (a zero-width joiner), or any Unicode symbol with a derived core property of `ID_Continue`.
107 |
108 | The `ID_Start` and `ID_Continue` derived core properties are defined in Unicode Identifier and Pattern Syntax as a way to identify symbols that are appropriate for use in identifiers such as variables and domain names. The specification is not specific to JavaScript.
109 |
110 | ## Formalizing the `__proto__` Property
111 |
112 | Even before ECMAScript 5 was finished, several JavaScript engines already implemented a custom property called `__proto__` that could be used to both get and set the `[[Prototype]]` property. Effectively, `__proto__` was an early precursor to both the `Object.getPrototypeOf()` and `Object.setPrototypeOf()` methods. Expecting all JavaScript engines to remove this property is unrealistic (there were popular JavaScript libraries making use of `__proto__`), so ECMAScript 6 also formalized the `__proto__` behavior. But the formalization appears in Appendix B of ECMA-262 along with this warning:
113 |
114 | > These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the
115 | implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.
116 |
117 | The ECMAScript specification recommends using `Object.getPrototypeOf()` and `Object.setPrototypeOf()` instead because `__proto__` has the following characteristics:
118 |
119 | 1. You can only specify `__proto__` once in an object literal. If you specify two `__proto__` properties, then an error is thrown. This is the only object literal property with that restriction.
120 | 1. The computed form `["__proto__"]` acts like a regular property and doesn't set or return the current object's prototype. All rules related to object literal properties apply in this form, as opposed to the non-computed form, which has exceptions.
121 |
122 | While you should avoid using the `__proto__` property, the way the specification defined it is interesting. In ECMAScript 6 engines, `Object.prototype.__proto__` is defined as an accessor property whose `get` method calls `Object.getPrototypeOf()` and whose `set` method calls the `Object.setPrototypeOf()` method. This leaves no real difference between using `__proto__` and `Object.getPrototypeOf()`/`Object.setPrototypeOf()`, except that `__proto__` allows you to set the prototype of an object literal directly. Here's how that works:
123 |
124 | ```js
125 | let person = {
126 | getGreeting() {
127 | return "Hello";
128 | }
129 | };
130 |
131 | let dog = {
132 | getGreeting() {
133 | return "Woof";
134 | }
135 | };
136 |
137 | // prototype is person
138 | let friend = {
139 | __proto__: person
140 | };
141 | console.log(friend.getGreeting()); // "Hello"
142 | console.log(Object.getPrototypeOf(friend) === person); // true
143 | console.log(friend.__proto__ === person); // true
144 |
145 | // set prototype to dog
146 | friend.__proto__ = dog;
147 | console.log(friend.getGreeting()); // "Woof"
148 | console.log(friend.__proto__ === dog); // true
149 | console.log(Object.getPrototypeOf(friend) === dog); // true
150 | ```
151 |
152 | Instead of calling `Object.create()` to make the `friend` object, this example creates a standard object literal that assigns a value to the `__proto__` property. When creating an object with the `Object.create()` method, on the other hand, you'd have to specify full property descriptors for any additional object properties.
153 |
--------------------------------------------------------------------------------
/docs/B-ECMAScript-7.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # נספח ב: להבין אקמהסקריפט 7 (2016)
4 |
5 | לקח כ-4 שנים לפתח את אקמהסקריפט 6. ולאחר מכן
6 | TC-39
7 | הגיעה לכלל החלטה שתהליך פיתוח ארוך כל כך אינו בר קיימא. תחת זאת, הוחלט לעבור למחזור שחרור גרסאות שנתי כדי לאפשר לתכונות חדשות של השפה להיכנס לאפיון מוקדם יותר.
8 |
9 | שחרורים בתדירות גבוהה יותר משמעותם שכל מהדורה חדשה של אקמהסקריפט תכיל פחות שינויים יחסית לאקמהסקריפט 6. כדי לתת משמעות לשינוי זה, גרסאות חדשות של השפה לא יציגו את מספר המהדורה ובמקום זאת יתייחסו לשנת הפרסום. כתוצאה מכך, אקמהסקריפט 6 ידועה גם בתור אקמהסקריפט 2015 ואקמהסקריפט 7 ידועה באופן רשמי בתור אקמהסקריפט 2016.
10 | TC-39
11 | מצפה להשתמש בשיטת השמות על בסיס השנה עבור כל גרסאות אקמהסקריפט העתידיות.
12 |
13 | אקמהסקריפט 2016 פורסמה במרץ 2016 והכילה רק 3 תוספות לשפה:
14 | אופרטור מתמטי חדש, מתודת מערך חדשה, ושגיאת תחביר חדשה.
15 | נספח זה ירחיב על כך.
16 |
17 | ## האופרטור המעריכי
18 |
19 | השינוי היחיד לתחביר בג׳אווהסקריפט שהוצג בגרסת אקמהסקריפט 2016 הינו
20 | *האופרטור המעריכי*
21 | (*exponentiation operator*),
22 | שמבטא פעולה מתמטית שמגדילה בסיס במעריך.
23 | בג׳אווהסקריפט כבר הייתה קיימת המתודה
24 | `Math.pow()`
25 | שביצעה הגדלה במעריך, אך ג׳אווהסקריפט הייתה אחת מהשפות הבודדות שדרשה מתודה לשם ביצוע הפעולה ולא היה לה אופרטור רשמי לכך
26 | (וישנם מפתחים שטוענים שאופרטור הינו קל יותר לקריאה והבנה).
27 |
28 | האופרטור המעריכי מורכב משתי כוכביות
29 | (`**`)
30 | כאשר האופרנד השמאלי הינו הבסיס והאופרנד הימני הינו המעריך.
31 | לדוגמה:
32 |
33 |
43 |
44 | הדוגמה לעיל מחשבת את הערך עבור
45 | 5^2^,
46 | שנותן את התוצאה 25.
47 | ניתן עדיין להשתמש במתודה
48 | `Math.pow()`
49 | על מנת להשיג את אותה התוצאה.
50 |
51 | ### סדר הפעולות
52 |
53 | האופרטור המעריכי בעל העדיפות הגבוהה ביותר מכל האופרטורים הבינאריים בג׳אווהסקריפט
54 | (לאופרטורים אונאריים יש עדיפות גבוהה יותר מאשר
55 | `**`).
56 | המשמעות היא שהוא מופעל קודם כל עבור כל פעולה מורכבת,
57 | כמו בדוגמה הבאה:
58 |
59 |
67 |
68 | ראשית כל מתבצע החישוב עבור
69 | 5^2^.
70 | התוצאה מוכפלת ב-2 ונקבל תוצאה סופית של 50.
71 |
72 | ### הגבלת אופרנדים
73 |
74 | עבור האופרטור המעריכי קיימת מגבלה שאינה קיימת עבור אופרטורים אחרים. הצד השמאלי של האופרטור לא יכול להיות ביטוי אונארי מלבד
75 | `++`
76 | או
77 | `--`.
78 | הדוגמה הבאה מציגה תחביר לא תקני:
79 |
80 |
81 |
89 |
90 | המספר
91 | `-5`
92 | בדוגמה זו נחשב לשגיאה תחבירית מכיוון שסדר הפעולות אינו ברור.
93 | האם הסימן
94 | `-`
95 | מופעל רק עבור הערך
96 | `5`
97 | או עבור תוצאת הביטוי
98 | `5 ** 2` ?
99 | הטלת איסור על ביטויים אונאריים בצידו השמאלי של האופרטור מבטלת את חוסר הבהירות.
100 | על מנת להבהיר את הכוונה, יש להוסיף סוגריים מסביב למספר
101 | `-5`
102 | או מסביב לביטוי
103 | `5 ** 2`
104 | כמו בדוגמה הבאה:
105 |
106 |
117 |
118 | אם נשים את הסוגריים מסביב לביטוי, הסימן
119 | `-`
120 | פועל על כל הביטוי.
121 | כאשר הסוגריים מופיעים מסביב לערך
122 | `-5`,
123 | ברור שהכוונה היא להגדיל את
124 | `-5`
125 | בריבוע.
126 |
127 | אין צורך להשתמש בסוגריים עבור האופרטורים
128 | `++`
129 | ו-
130 | `--`
131 | בצד השמאלי של האופרטור המעריכי מכיוון שלשני האופרטורים יש התנהגות מוגדרת היטב על האופרנדים שלהם. מקדם של
132 | `++`
133 | או
134 | `--`
135 | משנה את האופרנד לפני ביצוע כל פעולה אחרת והגרסה של האופרטורים שמופיעה מצד ימין של האופרנד לא מבצעת שינוי עד לאחר קריאת כל הביטוי כולו. שני הסוגים בטוחים לשימוש בצד השמאלי של האופרטור המעריכי, כפי שרואים בדוגמה הבאה:
136 |
137 |
151 |
152 | בדוגמה זו,
153 | `num1`
154 | מועלה בערכו לפני שמופעל האופרטור המעריכי,
155 | לכן
156 | `num1`
157 | הופך למספר 3
158 | ותוצאת האופרציה היא
159 | 9.
160 | עבור
161 | `num2`,
162 | הערך נשאר 2 עבור פעולת האופרטור המעריכי ואז משתנה ערכה ל 1.
163 |
164 | ## Array.prototype.includes()
165 |
166 | ייתכן שתזכרו שאקמהסקריפט 6 הוסיפה את המתודה
167 | `String.prototype.includes()`
168 | על מנת לבדוק האם תת מחרוזת מסוימת קיימת בתוך מחרוזת נתונה.
169 | אקמהסקריפט 6 הייתה אמורה להוסיף את המתודה
170 | `Array.prototype.includes()`
171 | כדי להמשיך המנהג של התייחסות למחרוזות ומערכים בצורה דומה. אך האפיון עבור
172 | `Array.prototype.includes()`
173 | לא הושלם עד מועד הסיום שנקבע עבור סיום הגשת אקמהסקריפט 6
174 | ולכן
175 | `Array.prototype.includes()`
176 | הועבר לאקמהסקריפט 2016.
177 |
178 | ## כיצד להשתמש ב Array.prototype.includes()
179 |
180 | המתודה
181 | `Array.prototype.includes()`
182 | מקבלת שני ארגומנטים:
183 | הערך לחיפוש ואינדקס אופציונלי שממנו להתחיל את החיפוש.
184 | כאשר נתון הארגומנט השני
185 |
186 | מתחילה לחפש החל מאותו אינדקס.
187 | (
188 | אינדקס ברירת המחדל לחיפוש הוא
189 | `0`.
190 | ).
191 | הערך שיוחזר יהיה
192 | `true`
193 | אם הערך מופיע במערך או
194 | `false`
195 | אם אינו מופיע במערך.
196 |
197 |
209 |
210 | בדוגמה זו, קריאה לקוד
211 | `values.includes()`
212 | מחזירה
213 | `true`
214 | עבור הערך
215 | `1`
216 | ותחזיר
217 | `false`
218 | עבור הערך
219 | `0`
220 | מכיוון שהערך
221 | `0`
222 | אינו מופיע במערך.
223 | כאשר הארגומנט השני משמש כדי להתחיל את החיפוש מאינדקס 2
224 | (שמכיל את הערך
225 | `3`),
226 | המתודה
227 | `values.includes()`
228 | מחזירה
229 | `false`
230 | מכיוון שהמספר
231 | `1`
232 | לא מופיע החל מאינדקס 2 ועד סוף המערך.
233 |
234 | ### השוואת ערכים
235 |
236 | ההשוואה שמבוצעת על ידי המתודה
237 | `includes()`
238 | משתמשת באופרטור
239 | `===`
240 | עם יוצא דופן אחד:
241 | `NaN`
242 | נחשב זהה לערך
243 | `NaN`
244 | אף על פי שהביטוי
245 | `NaN === NaN`
246 | מחזיר את הערך
247 | `false`.
248 | זוהי אינה התנהגות זהה לזו של המתודה
249 | `indexOf()`
250 | שמשתמשת אך ורק באופרטור
251 | `===`
252 | לצורך השוואה.
253 | כדי לראות את ההבדל נשתמש בדוגמה הבאה:
254 |
255 |
265 |
266 | המתודה
267 | `values.indexOf()`
268 | מחזירה את הערך
269 | `-1`
270 | עבור
271 | `NaN`
272 | למרות ש
273 | `NaN`
274 | מופיע במערך
275 | `values`
276 |
277 | ?> כאשר נרצה לבחון את הופעת הערך במערך ואין צורך לדעת את מיקומו, מומלץ להשתמש במתודה
278 | `includes()`
279 | בגלל השוני ביחס לערך
280 | `NaN`
281 | בין המתודה
282 | `includes()`
283 | למתודה
284 | `indexOf()`.
285 | אם נרצה לדעת את מיקום הערך במערך, ניאלץ להשתמש במתודה
286 | `indexOf()`.
287 |
288 | חשוב לדעת שבעת השוואה, הערכים
289 | `+0`
290 | וגם
291 | `-0`
292 | נחשבים זהים. במקרה זה ההתנהגות בעבור המתודות
293 | `indexOf()`
294 | וגם
295 | `includes()`
296 | זהה:
297 |
298 |
308 |
309 | בדוגמה לעיל המתודות
310 | `includes()`
311 | ו-
312 | `indexOf()`
313 | מאתרות את
314 | `+0`
315 | כאשר משתמשים בערך
316 | `-0`
317 | כפרמטר מכיוון ששני הערכים נחשבים לזהים. שימו לב לכך שזוהי התנהגות שונה מזו של המתודה
318 | `Object.is()`,
319 | שמתייחסת לערכים
320 | `+0`
321 | ו-
322 | `-0`
323 | כאל ערכים שונים.
324 |
325 | ## שינוי במצב קפדני בתוך פונקציה
326 |
327 | כאשר מצב קפדני הופיע לראשונה באקמהסקריפט 5, השפה הייתה פשוטה יותר יחסית למה שנהייתה באקמהסקריפט 6. למרות זאת אקמהסקריפט 6 עדיין אפשרה לנו להגדיר מצב קפדני על ידי שימוש בפקודת
328 | `"use strict"`
329 | במרחב הגלובלי
330 | (שיגרום לכל הקוד לרוץ במצב קפדני)
331 | או בתוך פונקציה
332 | (מה שיגרום רק לפונקציה לרוץ במצב קפדני).
333 | המצב השני היווה בעיה באקמהסקריפט 6 עקב הדרכים המורכבות יותר שבהן ניתן היה להגדיר פרמטרים. הכוונה לפירוק פרמטרים וערכי ברירת מחדל לפרמטרים.
334 | כדי להבין את הבעיה נבחן את הדוגמה הבאה:
335 |
336 |
347 |
348 | בדוגמה לעיל הפרמטר בשם
349 | `first`
350 | מקבל את ערך ברירת המחדל
351 | `this`.
352 | מה היינו מצפים שיהיה ערכו של
353 | `first`?
354 | הגדרות אקמהסקריפט 6 מורות למנועי ריצה של ג׳אווהסקריפט להתייחס לפרמטרים כאילו הם רצים במצב קפדני במצב שכזה.
355 | לכן
356 | `this`
357 | אמור לקבל את הערך
358 | `undefined`.
359 | אך מימוש פרמטרים במצב קפדני כאשר
360 | `"use strict"`
361 | מופיע בתוך הפונקציה התגלה כבעייתי במיוחד מכיוון שערכים דיפולטיביים לפרמטרים יכולים להיות פונקציות בעצמם.
362 | קושי זה הוביל לכך שרוב מנועי הריצה של ג׳אווהסקריפט לא מימשו שינוי זה.
363 | (ולכן
364 | `this`
365 | יצביע על האובייקט הגלובלי).
366 |
367 | כתוצאה מקושי זה במימוש, אקמהסקריפט 2016 אוסרת על פקודת
368 | `"use strict"`
369 | בתוך פונקציה שהפרמטרים שלה הינם פרמטרים מפורקים או בעלי ערכי ברירת מחדל.
370 | רק
371 | *פרמטרים פשוטים*
372 | (*simple parameter lists*),
373 | כאלו שאינם מכילים פרמטרים מפורקים או בעלי ערך דיפולטיבי, מותרים לשימוש כאשר פקודת
374 | `"use strict"`
375 | מופיעה בתוך הפונקציה.
376 | להלן מספר דוגמאות:
377 |
378 |
404 |
405 | ניתן עדיין להשתמש בפקודת
406 | `"use strict"`
407 | ביחד עם פרמטרים פשוטים, ולכן הפונקציה
408 | `okay()`
409 | עובדת כמצופה
410 | (בדיוק כמו שעבדה באקמהסקריפט 5).
411 | הפונקציה
412 | `notOkay1()`
413 | זורקת שגיאה תחבירית מכיוון שלא ניתן להשתמש בפקודת
414 | `"use strict"`
415 | בתוך פונקציה בעלת ערכי ברירת מחדל לפרמטרים.
416 | באופן דומה הפונקציה
417 | `notOkay2()`
418 | גם זורקת שגיאה תחבירית מכיוון שלא ניתן להשתמש בפקודת
419 | `"use strict"`
420 | בפונקציה עם פרמטרים מפורקים.
421 |
422 | בהיבט הכללי, השינוי הנ״ל מבטל מקור לבלבול בקרב מפתחים ופותר בעיית מימוש עבור מנועי ריצה של ג׳אווהסקריפט.
423 |
424 |
2 |
3 | # נספח ב: להבין אקמהסקריפט 7 (2016)
4 |
5 | לקח כ-4 שנים לפתח את אקמהסקריפט 6. ולאחר מכן
6 | TC-39
7 | הגיעה לכלל החלטה שתהליך פיתוח ארוך כל כך אינו בר קיימא. תחת זאת, הוחלט לעבור למחזור שחרור גרסאות שנתי כדי לאפשר לתכונות חדשות של השפה להיכנס לאפיון מוקדם יותר.
8 |
9 | שחרורים בתדירות גבוהה יותר משמעותם שכל מהדורה חדשה של אקמהסקריפט תכיל פחות שינויים יחסית לאקמהסקריפט 6. כדי לתת משמעות לשינוי זה, גרסאות חדשות של השפה לא יציגו את מספר המהדורה ובמקום זאת יתייחסו לשנת הפרסום. כתוצאה מכך, אקמהסקריפט 6 ידועה גם בתור אקמהסקריפט 2015 ואקמהסקריפט 7 ידועה באופן רשמי בתור אקמהסקריפט 2016.
10 | TC-39
11 | מצפה להשתמש בשיטת השמות על בסיס השנה עבור כל גרסאות אקמהסקריפט העתידיות.
12 |
13 | אקמהסקריפט 2016 פורסמה במרץ 2016 והכילה רק 3 תוספות לשפה:
14 | אופרטור מתמטי חדש, מתודת מערך חדשה, ושגיאת תחביר חדשה.
15 | נספח זה ירחיב על כך.
16 |
17 | ## האופרטור המעריכי
18 |
19 | השינוי היחיד לתחביר בג׳אווהסקריפט שהוצג בגרסת אקמהסקריפט 2016 הינו
20 | *האופרטור המעריכי*
21 | (*exponentiation operator*),
22 | שמבטא פעולה מתמטית שמגדילה בסיס במעריך.
23 | בג׳אווהסקריפט כבר הייתה קיימת המתודה
24 | `Math.pow()`
25 | שביצעה הגדלה במעריך, אך ג׳אווהסקריפט הייתה אחת מהשפות הבודדות שדרשה מתודה לשם ביצוע הפעולה ולא היה לה אופרטור רשמי לכך
26 | (וישנם מפתחים שטוענים שאופרטור הינו קל יותר לקריאה והבנה).
27 |
28 | האופרטור המעריכי מורכב משתי כוכביות
29 | (`**`)
30 | כאשר האופרנד השמאלי הינו הבסיס והאופרנד הימני הינו המעריך.
31 | לדוגמה:
32 |
33 |
43 |
44 | הדוגמה לעיל מחשבת את הערך עבור
45 | 5^2^,
46 | שנותן את התוצאה 25.
47 | ניתן עדיין להשתמש במתודה
48 | `Math.pow()`
49 | על מנת להשיג את אותה התוצאה.
50 |
51 | ### סדר הפעולות
52 |
53 | האופרטור המעריכי בעל העדיפות הגבוהה ביותר מכל האופרטורים הבינאריים בג׳אווהסקריפט
54 | (לאופרטורים אונאריים יש עדיפות גבוהה יותר מאשר
55 | `**`).
56 | המשמעות היא שהוא מופעל קודם כל עבור כל פעולה מורכבת,
57 | כמו בדוגמה הבאה:
58 |
59 |
67 |
68 | ראשית כל מתבצע החישוב עבור
69 | 5^2^.
70 | התוצאה מוכפלת ב-2 ונקבל תוצאה סופית של 50.
71 |
72 | ### הגבלת אופרנדים
73 |
74 | עבור האופרטור המעריכי קיימת מגבלה שאינה קיימת עבור אופרטורים אחרים. הצד השמאלי של האופרטור לא יכול להיות ביטוי אונארי מלבד
75 | `++`
76 | או
77 | `--`.
78 | הדוגמה הבאה מציגה תחביר לא תקני:
79 |
80 |
81 |
89 |
90 | המספר
91 | `-5`
92 | בדוגמה זו נחשב לשגיאה תחבירית מכיוון שסדר הפעולות אינו ברור.
93 | האם הסימן
94 | `-`
95 | מופעל רק עבור הערך
96 | `5`
97 | או עבור תוצאת הביטוי
98 | `5 ** 2` ?
99 | הטלת איסור על ביטויים אונאריים בצידו השמאלי של האופרטור מבטלת את חוסר הבהירות.
100 | על מנת להבהיר את הכוונה, יש להוסיף סוגריים מסביב למספר
101 | `-5`
102 | או מסביב לביטוי
103 | `5 ** 2`
104 | כמו בדוגמה הבאה:
105 |
106 |
117 |
118 | אם נשים את הסוגריים מסביב לביטוי, הסימן
119 | `-`
120 | פועל על כל הביטוי.
121 | כאשר הסוגריים מופיעים מסביב לערך
122 | `-5`,
123 | ברור שהכוונה היא להגדיל את
124 | `-5`
125 | בריבוע.
126 |
127 | אין צורך להשתמש בסוגריים עבור האופרטורים
128 | `++`
129 | ו-
130 | `--`
131 | בצד השמאלי של האופרטור המעריכי מכיוון שלשני האופרטורים יש התנהגות מוגדרת היטב על האופרנדים שלהם. מקדם של
132 | `++`
133 | או
134 | `--`
135 | משנה את האופרנד לפני ביצוע כל פעולה אחרת והגרסה של האופרטורים שמופיעה מצד ימין של האופרנד לא מבצעת שינוי עד לאחר קריאת כל הביטוי כולו. שני הסוגים בטוחים לשימוש בצד השמאלי של האופרטור המעריכי, כפי שרואים בדוגמה הבאה:
136 |
137 |
151 |
152 | בדוגמה זו,
153 | `num1`
154 | מועלה בערכו לפני שמופעל האופרטור המעריכי,
155 | לכן
156 | `num1`
157 | הופך למספר 3
158 | ותוצאת האופרציה היא
159 | 9.
160 | עבור
161 | `num2`,
162 | הערך נשאר 2 עבור פעולת האופרטור המעריכי ואז משתנה ערכה ל 1.
163 |
164 | ## Array.prototype.includes()
165 |
166 | ייתכן שתזכרו שאקמהסקריפט 6 הוסיפה את המתודה
167 | `String.prototype.includes()`
168 | על מנת לבדוק האם תת מחרוזת מסוימת קיימת בתוך מחרוזת נתונה.
169 | אקמהסקריפט 6 הייתה אמורה להוסיף את המתודה
170 | `Array.prototype.includes()`
171 | כדי להמשיך המנהג של התייחסות למחרוזות ומערכים בצורה דומה. אך האפיון עבור
172 | `Array.prototype.includes()`
173 | לא הושלם עד מועד הסיום שנקבע עבור סיום הגשת אקמהסקריפט 6
174 | ולכן
175 | `Array.prototype.includes()`
176 | הועבר לאקמהסקריפט 2016.
177 |
178 | ## כיצד להשתמש ב Array.prototype.includes()
179 |
180 | המתודה
181 | `Array.prototype.includes()`
182 | מקבלת שני ארגומנטים:
183 | הערך לחיפוש ואינדקס אופציונלי שממנו להתחיל את החיפוש.
184 | כאשר נתון הארגומנט השני
185 |
186 | מתחילה לחפש החל מאותו אינדקס.
187 | (
188 | אינדקס ברירת המחדל לחיפוש הוא
189 | `0`.
190 | ).
191 | הערך שיוחזר יהיה
192 | `true`
193 | אם הערך מופיע במערך או
194 | `false`
195 | אם אינו מופיע במערך.
196 |
197 |
209 |
210 | בדוגמה זו, קריאה לקוד
211 | `values.includes()`
212 | מחזירה
213 | `true`
214 | עבור הערך
215 | `1`
216 | ותחזיר
217 | `false`
218 | עבור הערך
219 | `0`
220 | מכיוון שהערך
221 | `0`
222 | אינו מופיע במערך.
223 | כאשר הארגומנט השני משמש כדי להתחיל את החיפוש מאינדקס 2
224 | (שמכיל את הערך
225 | `3`),
226 | המתודה
227 | `values.includes()`
228 | מחזירה
229 | `false`
230 | מכיוון שהמספר
231 | `1`
232 | לא מופיע החל מאינדקס 2 ועד סוף המערך.
233 |
234 | ### השוואת ערכים
235 |
236 | ההשוואה שמבוצעת על ידי המתודה
237 | `includes()`
238 | משתמשת באופרטור
239 | `===`
240 | עם יוצא דופן אחד:
241 | `NaN`
242 | נחשב זהה לערך
243 | `NaN`
244 | אף על פי שהביטוי
245 | `NaN === NaN`
246 | מחזיר את הערך
247 | `false`.
248 | זוהי אינה התנהגות זהה לזו של המתודה
249 | `indexOf()`
250 | שמשתמשת אך ורק באופרטור
251 | `===`
252 | לצורך השוואה.
253 | כדי לראות את ההבדל נשתמש בדוגמה הבאה:
254 |
255 |
265 |
266 | המתודה
267 | `values.indexOf()`
268 | מחזירה את הערך
269 | `-1`
270 | עבור
271 | `NaN`
272 | למרות ש
273 | `NaN`
274 | מופיע במערך
275 | `values`
276 |
277 | W> כאשר נרצה לבחון את הופעת הערך במערך ואין צורך לדעת את מיקומו, מומלץ להשתמש במתודה
278 | `includes()`
279 | בגלל השוני ביחס לערך
280 | `NaN`
281 | בין המתודה
282 | `includes()`
283 | למתודה
284 | `indexOf()`.
285 | אם נרצה לדעת את מיקום הערך במערך, ניאלץ להשתמש במתודה
286 | `indexOf()`.
287 |
288 | חשוב לדעת שבעת השוואה, הערכים
289 | `+0`
290 | וגם
291 | `-0`
292 | נחשבים זהים. במקרה זה ההתנהגות בעבור המתודות
293 | `indexOf()`
294 | וגם
295 | `includes()`
296 | זהה:
297 |
298 |
308 |
309 | בדוגמה לעיל המתודות
310 | `includes()`
311 | ו-
312 | `indexOf()`
313 | מאתרות את
314 | `+0`
315 | כאשר משתמשים בערך
316 | `-0`
317 | כפרמטר מכיוון ששני הערכים נחשבים לזהים. שימו לב לכך שזוהי התנהגות שונה מזו של המתודה
318 | `Object.is()`,
319 | שמתייחסת לערכים
320 | `+0`
321 | ו-
322 | `-0`
323 | כאל ערכים שונים.
324 |
325 | ## שינוי במצב קפדני בתוך פונקציה
326 |
327 | כאשר מצב קפדני הופיע לראשונה באקמהסקריפט 5, השפה הייתה פשוטה יותר יחסית למה שנהייתה באקמהסקריפט 6. למרות זאת אקמהסקריפט 6 עדיין אפשרה לנו להגדיר מצב קפדני על ידי שימוש בפקודת
328 | `"use strict"`
329 | במרחב הגלובלי
330 | (שיגרום לכל הקוד לרוץ במצב קפדני)
331 | או בתוך פונקציה
332 | (מה שיגרום רק לפונקציה לרוץ במצב קפדני).
333 | המצב השני היווה בעיה באקמהסקריפט 6 עקב הדרכים המורכבות יותר שבהן ניתן היה להגדיר פרמטרים. הכוונה לפירוק פרמטרים וערכי ברירת מחדל לפרמטרים.
334 | כדי להבין את הבעיה נבחן את הדוגמה הבאה:
335 |
336 |
347 |
348 | בדוגמה לעיל הפרמטר בשם
349 | `first`
350 | מקבל את ערך ברירת המחדל
351 | `this`.
352 | מה היינו מצפים שיהיה ערכו של
353 | `first`?
354 | הגדרות אקמהסקריפט 6 מורות למנועי ריצה של ג׳אווהסקריפט להתייחס לפרמטרים כאילו הם רצים במצב קפדני במצב שכזה.
355 | לכן
356 | `this`
357 | אמור לקבל את הערך
358 | `undefined`.
359 | אך מימוש פרמטרים במצב קפדני כאשר
360 | `"use strict"`
361 | מופיע בתוך הפונקציה התגלה כבעייתי במיוחד מכיוון שערכים דיפולטיביים לפרמטרים יכולים להיות פונקציות בעצמם.
362 | קושי זה הוביל לכך שרוב מנועי הריצה של ג׳אווהסקריפט לא מימשו שינוי זה.
363 | (ולכן
364 | `this`
365 | יצביע על האובייקט הגלובלי).
366 |
367 | כתוצאה מקושי זה במימוש, אקמהסקריפט 2016 אוסרת על פקודת
368 | `"use strict"`
369 | בתוך פונקציה שהפרמטרים שלה הינם פרמטרים מפורקים או בעלי ערכי ברירת מחדל.
370 | רק
371 | *פרמטרים פשוטים*
372 | (*simple parameter lists*),
373 | כאלו שאינם מכילים פרמטרים מפורקים או בעלי ערך דיפולטיבי, מותרים לשימוש כאשר פקודת
374 | `"use strict"`
375 | מופיעה בתוך הפונקציה.
376 | להלן מספר דוגמאות:
377 |
378 |
404 |
405 | ניתן עדיין להשתמש בפקודת
406 | `"use strict"`
407 | ביחד עם פרמטרים פשוטים, ולכן הפונקציה
408 | `okay()`
409 | עובדת כמצופה
410 | (בדיוק כמו שעבדה באקמהסקריפט 5).
411 | הפונקציה
412 | `notOkay1()`
413 | זורקת שגיאה תחבירית מכיוון שלא ניתן להשתמש בפקודת
414 | `"use strict"`
415 | בתוך פונקציה בעלת ערכי ברירת מחדל לפרמטרים.
416 | באופן דומה הפונקציה
417 | `notOkay2()`
418 | גם זורקת שגיאה תחבירית מכיוון שלא ניתן להשתמש בפקודת
419 | `"use strict"`
420 | בפונקציה עם פרמטרים מפורקים.
421 |
422 | בהיבט הכללי, השינוי הנ״ל מבטל מקור לבלבול בקרב מפתחים ופותר בעיית מימוש עבור מנועי ריצה של ג׳אווהסקריפט.
423 |
424 |
2 |
3 | # נספח א: שינויים קטנים
4 |
5 | יחד עם השינויים העיקריים שכוסו בספר זה, אקמהסקריפט 6 עשתה שינויים קטנים אך מועילים. השינויים הללו כוללים הפיכת ערכים מספריים לקלים יותר לשימוש, בעזרת הוספת צורות חישוב חדשות, שיפורים לעבודה עם קידוד טקסט מסוג יוניקוד והפיכת התכונה
6 | `__proto__`
7 | לתכונה פורמאלית. כל השינויים הללו מתוארים בנספח זה.
8 |
9 | ## עבודה עם ערכים מספריים
10 |
11 | ג׳אווהסקריפט משתמש בשיטת הקידוד
12 | IEEE 754
13 | על מנת לייצג גם ערכים מספריים שלמים
14 | (integers)
15 | וגם מספרים צפים
16 | (floats),
17 | דבר שגרם לבלבול רב במהלך השנים. השפה הלכה למרחק גדול על מנת לוודא שמפתחים לא יצטרכו לדאוג לגבי הפרטים הקטנים של קידוד מספרים, אך בעיות עדיין צצות מדי פעם. אקמהסקריפט 6 מטפלת בכך על ידי הפיכת ערכים מספרים שלמים קלים יותר לזיהוי ושימוש.
18 |
19 | ### זיהוי ערכים מספריים שלמים
20 |
21 | ראשית, אקמהסקריפט 6 הוסיפה את המתודה
22 | `Number.isInteger()`
23 | שמאפשרת לקבוע האם ערך כלשהו מייצג מספר שלם. למרות שג׳אווהסקריפט משתמשת בשיטת
24 | IEEE 754
25 | כדי לייצג שני סוגי המספרים, מספרים צפים ושלמים שמורים בצורה שונה.
26 | המתודה
27 | `Number.isInteger()`
28 | מנצלת עובדה זו וכאשר היא נקראת היא בודקת את הייצוג הפנימי של אותו ערך על מנת לקבוע האם מדובר במספר שלם. המשמעות היא שמספרים שנראים כמו מספרים צפים יכולים להיות שמורים כמספר שלם ויגרמו למתודה
29 | `Number.isInteger()`
30 | להחזיר את הערך
31 | `true`.
32 | לדוגמה:
33 |
34 |
42 |
43 | בקוד לעיל,
44 | `Number.isInteger()`
45 | מחזירה את הערך
46 | `true`
47 | גם עבור
48 | `25`
49 | וגם עבור
50 | `25.0`
51 | למרות שהערך האחרון נראה כמו מספר צף. הוספת נקודה עשרונית לערך מספרי לא הופכת אותו למספר צף באופן אוטומטי בג׳אווהסקריפט.
52 | לעומת זאת, הערך
53 | `25.1`
54 | נשמר כמספר צף כיוון שקיים אחרי הנקודה העשרונית.
55 |
56 | ### מספרים שלמים בטוחים לשימוש
57 |
58 | שיטת הקידוד המספרי
59 | IEEE 754
60 | יכולה לייצג באופן מדוייק ערכים שלמים בטווח הערכים
61 | -2^53^
62 | עד
63 | 2^53^.
64 | מעבר לטווח ״בטוח״ זה, ייצוג בינארי שונה משמש למספר מרובה של ערכים נומריים. משמעות הדבר היא שג׳אווהסקריפט יכולה לייצג ערכים שלמים בצורה מדוייקת בתוך טווח זה לפני שצצות בעיות.
65 | לדוגמה:
66 |
67 |
75 |
76 | כפי שרואים בדוגמה לעיל, שני ערכים שונים מיוצגים על ידי אותו ערך מספרי שלם.
77 | התופעה תופיע באופן תכוף יותר ככל שהערך המדובר רחוק יותר מן הטווח הבטוח.
78 |
79 | אקמהסקריפט 6 הוסיפה את המתודה
80 | `Number.isSafeInteger()`
81 | כדי לזהות בצורה טובה יותר ערכים שלמים שהשפה מציגה בצורה מדויקת. היא גם הוסיפה את התכונות
82 | `Number.MAX_SAFE_INTEGER`
83 | ו-
84 | `Number.MIN_SAFE_INTEGER`
85 | כדי לייצג את הגבול העליון והתחתון, בהתאמה, של הטווח הבטוח.
86 | המתודה
87 | `Number.isSafeInteger()`
88 | יכולה להבטיח לנו שערך הינו מספר שלם שנמצא בטווח הערכים השלמים הבטוח לשימוש, כמו בדוגמה הבאה:
89 |
90 |
103 |
104 | הערך
105 | `inside`
106 | הינו הערך המספרי השלם הבטוח הגדול ביותר, ולכן הוא מחזיר את הערך
107 | `true`
108 | עבור המתודות
109 | `Number.isInteger()`
110 | ו-
111 | `Number.isSafeInteger()`.
112 | המספר
113 | `outside`
114 | אינו נחשב לערך בטוח למרות שעדיין מדובר בערך מספרי שלם.
115 |
116 | רוב הזמן, נרצה להשתמש בערכים בטוחים לשימוש בעת חישובים או השוואות בג׳אווהסקריפט, כך ששימוש במתודה
117 | `Number.isSafeInteger()`.
118 | כחלק מוולידציה של קלט מהמשתמש נחשב לרעיון טוב.
119 |
120 | ## מתודות מתמטיות חדשות
121 |
122 | הדגש החדש על גיימינג
123 | (Gaming)
124 | וגרפיקה שהוביל את אקמהסקריפט 6 לכלול בתוכה מערכים בינאריים הוביל להבנה שמנוע הריצה של ג׳אווהסקריפט יכול לבצע חישובים מתמטיים רבים בצורה יעילה יותר. אך שיטות אופטימיזציה כמו
125 | asm.js,
126 | שפועלות על תת-קבוצה של ג׳אווהסקריפט על מנת לשפר ביצועים, זקוקות ליותר נתונים כדי לבצע חישובים באופן המהיר ביותר. למשל, הידיעה האם יש להתייחס לערכים נומריים בתור ערכים שלמים בני 32 ביט או בתור מספרים צפים בגודל 64 ביט חשובה עבור פעולות תלויות חומרה, שהן מהירות באופן משמעותי יותר מאשר פעולות על בסיס תוכנה.
127 |
128 | כתוצאה מכך, אקמהסקריפט 6 הוסיפה מספר מתודות לאובייקט
129 | `Math`
130 | כדי לשפר את מהירות החישובים המתמטיים הנפוצים.
131 | שיפור מהירות החישובים הנפוצים גם שיפרה את מהירות האפליקציות שמבצעות חישובים רבים, כמו תוכנות גרפיקה רבות. המתודות החדשות מופיעות בהמשך:
132 |
133 | * `Math.acosh(x)` מחזיר קוסינוס היפרבולי הפוך עבור `x`.
134 | * `Math.asinh(x)` מחזיר סינוס היפרבולי הפוך עבור `x`.
135 | * `Math.atanh(x)` מחזיר טנגנס היפרבולי הפוך עבור `x`
136 | * `Math.cbrt(x)` מחזיר שורש מעוקב עבור `x`.
137 | * `Math.clz32(x)` מחזיר את מספר הביטים המובילים בעלי ערך אפס בייצוג 32 ביט עבור `x`.
138 | * `Math.expm1(x)` מחזיר את התוצאה של חיסור 2 מהפונקציה האקספוננציאלית של `x`
139 | * `Math.fround(x)` מחזיר את המספר הצף הקרוב ביותר עבור `x`.
140 | * `Math.hypot(...values)` מחזיר את שורש סכום החזקות של כל הארגומנטים
141 | * `Math.imul(x, y)` מחזיר את תוצאת מכפלת 32 ביט של שני הארגומנטים
142 | * `Math.log1p(x)` מחזיר את הלוגריתם הטבעי של (`1 + x`).
143 | * `Math.log10(x)` מחזיר את הלוגריתם על העשרוני של `x`.
144 | * `Math.log2(x)` מחזיר את הלוגריתם על בסיס 2 של `x`.
145 | * `Math.sign(x)` מחזיר -1 אם הארגומנט שלילי, 0 אם ערכו +0 או -0 , ואת הערך 1 אם הארגומנט חיובי
146 | * `Math.cosh(x)` מחזיר קוסינוס היפרבולי עבור `x`.
147 | * `Math.sinh(x)` מחזיר את הסינוס ההיפרבולי עבור `x`.
148 | * `Math.tanh(x)` מחזיר טנגנס היפרבולי עבור `x`.
149 | * `Math.trunc(x)` מסלק ספרות לאחר הנקודה הדצימלית ממספר צף ומחזיר מספר שלם.
150 |
151 | הספר הזה לא ירחיב על כל מתודה חדשה שהוזכרה. אך במידה והאפליקציה שלכם צריכה לבצע חישובים מסוג נפוץ יחסית, חשוב לבדוק האם קיימת מתודה חדשה על האובייקט
152 | `Math`
153 | שעושה זאת בעבורכם לפני שמנסים לממש אותה בעצמכם.
154 |
155 | ## מזהי יוניקוד
156 |
157 | אקמהסקריפט 6 מציעה לנו תמיכה טובה יותר ביוניקוד מאשר גרסאות קודמות והיא גם משנה את סוג התווים שיכולים לשמש בתור מזהים.
158 | בגרסת אקמהסקריפט 5, היה ניתן להשתמש ברצף תווי בריחה מסוג יוניקוד בתור מזהים.
159 | לדוגמה:
160 |
161 |
162 |
174 |
175 | לאחר פקודת
176 | `var`
177 | בדוגמה זו,
178 | ניתן להשתמש ברצף התווים
179 | `\u0061`
180 | או פשוט
181 | `a`
182 | על מנת לגשת למשתנה.
183 | בגרסת אקמהסקריפט 6 ניתן להשתמש ברצף בריחה של נקודות קוד של יוניקוד בתור מזהים.
184 | למשל:
185 |
186 |
198 |
199 | הדוגמה לעיל רק מחליפה את המזהה
200 | `\u0061`
201 | בנקודת הקוד המקבילה. חוץ מהבדל זה היא זהה לחלוטין לדוגמה הקודמת.
202 |
203 | בנוסף לכך, אקמהסקריפט 6 מגדירה באופן רשמי מזהים תקינים לפי המונחים המצויים בדוקומנטציה של
204 |
205 | [Unicode Standard Annex #31: Unicode Identifier and Pattern Syntax](http://unicode.org/reports/tr31/)
206 | ,
207 | שנותנת לנו את הכללים הבאים:
208 |
209 | 1. התו הראשון צריך להיות התו
210 | `$`, `_`,
211 | או כל תו יוניקוד בעל תכונת
212 | `ID_Start`.
213 | 2. תו עוקב חייב להיות מהתווים
214 | `$`, `_`,
215 | `\u200c`
216 | (
217 | תו לא מחבר באורך אפסי
218 | a zero-width non-joiner
219 | ),
220 | `\u200d`
221 | (
222 | תו מחבר באורך אפסי
223 | a zero-width joiner
224 | ),
225 | או כל תו יוניקוד אחר בעל תכונת
226 | `ID_Continue`.
227 |
228 | התכונות
229 | `ID_Start`
230 | ו-
231 | `ID_Continue`
232 | מוגדרות בדוקומנטציה האמורה לעיל בתור דרך לזהות תווים שמקובלים לשימוש כמזהים בתור משתנים ושמות דומיין.
233 | ההגדרה המופיעה שם אינה בלעדית לג׳אווהסקריפט בלבד.
234 |
235 | ## התכונה `__proto__`
236 |
237 | אפילו לפני שהוציאו לאור את גרסת אקמהסקריפט 5, מספר מנועי ריצה של ג׳אווהסקריפט כבר מימשו תכונה מיוחדת בשם
238 | `__proto__`
239 | שהייתה מסוגלת לקרוא ולקבוע את התכונה הפנימית
240 | `[[Prototype]]`.
241 | באופן מעשי,
242 | התכונה
243 | `__proto__`
244 | הייתה הקדמה למתודות
245 | `Object.getPrototypeOf()`
246 | ו-
247 | `Object.setPrototypeOf()`.
248 | הציפייה שכל מנועי הריצה של ג׳אווהסקריפט יסירו את התכונה אינה מציאותית
249 | (היו ספריות ג׳אווהסקריפט פופולריות שעשו שימוש בתכונה
250 | `__proto__`),
251 | ולכן אקמהסקריפט 6 הפכה את התנהגות התכונה
252 | `__proto__`
253 | לרשמית.
254 | אך השינוי מופיע בנספח
255 | B
256 | של תקן
257 | ECMA-262
258 | ביחד עם אזהרה זו:
259 |
260 |
261 |
262 | > These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the
263 | implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.
264 |
265 |
266 |
267 | > תכונות אלו אינן נחשבות לחלק מעיקרי השפה.
268 | מתכנתים לא צריכים להשתמש או להסתמך על קיום תכונות והתנהגויות אלו בעת כתיבת קוד אקמהסקריפט חדש. מימושים של אקמהסקריפט צריכים להימנע ממימוש תכונות אלו אלא אם כן המימוש הינו חלק מדפדפן או נדרש להריץ את אותו קוד מגרסה קודמת של אקמהסקריפט שדפדפנים מריצים.
269 |
270 | האפיון עבור אקמהסקריפט ממליץ להשתמש
271 | במתודות
272 | `Object.getPrototypeOf()`
273 | ו-
274 | `Object.setPrototypeOf()`
275 | מכיוון שלתכונה
276 | `__proto__`
277 | יש את המאפיינים הבאים:
278 |
279 | 1. ניתן להגדיר את התכונה
280 | `__proto__`
281 | פעם אחת בלבד בעת הגדרת אובייקט ליטראל. אם נגדיר שתי תכונות מסוג
282 | `__proto__`
283 | תיזרק שגיאה. זוהי התכונה היחידה בתוך אובייקט ליטראל בעלת מגבלה זו.
284 | 1. הצורה המחושבת של תכונה זו
285 | `["__proto__"]`
286 | פועלת כמו תכונה רגילה ולא קוראת או משנה את הפרוטוטייפ של האובייקט הנוכחי. כל הכללים
287 | הקשורים לתכונות אובייקט ליטראל תקפים בצורה זו בניגוד לצורה הלא מחושבת, שיש לה כללים יוצאי דופן.
288 |
289 | בעוד שרצוי להימנע משימוש בתכונה
290 | `__proto__`
291 | הצורה שבה היא נמצאת באפיון השפה מעניינת מאוד. במנועי ריצה של אקמהסקריפט 6, התכונה
292 | `Object.prototype.__proto__`
293 | מוגדרת בתור תכונת גישה שהמתודה
294 |
295 | שלה קוראת למתודה
296 | `Object.getPrototypeOf()`
297 | והמתודה
298 | `set`
299 | שלה קוראת למתודה
300 | `Object.setPrototypeOf()`.
301 | זה אומר שאין הבדל אמיתי בין שימוש בתכונה
302 | `__proto__`
303 | לבין שימוש ב
304 | `Object.getPrototypeOf()`/`Object.setPrototypeOf()`,
305 | מלבד זאת שהתכונה
306 | `__proto__`
307 | מאפשרת לנו להגדיר פרוטוטייפ של אובייקט ליטראל בצורה ישירה.
308 | לדוגמה:
309 |
310 |
342 |
343 | במקום לקרוא למתודה
344 | `Object.create()`
345 | כדי לייצר את האובייקט
346 | `friend`
347 | דוגמה זו מייצרת אובייקט ליטראל סטנדרטי שמבצעת השמה לערך התכונה
348 | `__proto__`.
349 | בעת יצירת אובייקט בעזרת המתודה
350 | `Object.create()`
351 | ניאלץ להגדיר את כל מאפייני התכונה המלאים בעבור כל תכונה נוספת.
352 |
353 |
2 |
3 | # נספח א: שינויים קטנים
4 |
5 | יחד עם השינויים העיקריים שכוסו בספר זה, אקמהסקריפט 6 עשתה שינויים קטנים אך מועילים. השינויים הללו כוללים הפיכת ערכים מספריים לקלים יותר לשימוש, בעזרת הוספת צורות חישוב חדשות, שיפורים לעבודה עם קידוד טקסט מסוג יוניקוד והפיכת התכונה
6 | `__proto__`
7 | לתכונה פורמאלית. כל השינויים הללו מתוארים בנספח זה.
8 |
9 | ## עבודה עם ערכים מספריים
10 |
11 | ג׳אווהסקריפט משתמש בשיטת הקידוד
12 | IEEE 754
13 | על מנת לייצג גם ערכים מספריים שלמים
14 | (integers)
15 | וגם מספרים צפים
16 | (floats),
17 | דבר שגרם לבלבול רב במהלך השנים. השפה הלכה למרחק גדול על מנת לוודא שמפתחים לא יצטרכו לדאוג לגבי הפרטים הקטנים של קידוד מספרים, אך בעיות עדיין צצות מדי פעם. אקמהסקריפט 6 מטפלת בכך על ידי הפיכת ערכים מספרים שלמים קלים יותר לזיהוי ושימוש.
18 |
19 | ### זיהוי ערכים מספריים שלמים
20 |
21 | ראשית, אקמהסקריפט 6 הוסיפה את המתודה
22 | `Number.isInteger()`
23 | שמאפשרת לקבוע האם ערך כלשהו מייצג מספר שלם. למרות שג׳אווהסקריפט משתמשת בשיטת
24 | IEEE 754
25 | כדי לייצג שני סוגי המספרים, מספרים צפים ושלמים שמורים בצורה שונה.
26 | המתודה
27 | `Number.isInteger()`
28 | מנצלת עובדה זו וכאשר היא נקראת היא בודקת את הייצוג הפנימי של אותו ערך על מנת לקבוע האם מדובר במספר שלם. המשמעות היא שמספרים שנראים כמו מספרים צפים יכולים להיות שמורים כמספר שלם ויגרמו למתודה
29 | `Number.isInteger()`
30 | להחזיר את הערך
31 | `true`.
32 | לדוגמה:
33 |
34 |
42 |
43 | בקוד לעיל,
44 | `Number.isInteger()`
45 | מחזירה את הערך
46 | `true`
47 | גם עבור
48 | `25`
49 | וגם עבור
50 | `25.0`
51 | למרות שהערך האחרון נראה כמו מספר צף. הוספת נקודה עשרונית לערך מספרי לא הופכת אותו למספר צף באופן אוטומטי בג׳אווהסקריפט.
52 | לעומת זאת, הערך
53 | `25.1`
54 | נשמר כמספר צף כיוון שקיים אחרי הנקודה העשרונית.
55 |
56 | ### מספרים שלמים בטוחים לשימוש
57 |
58 | שיטת הקידוד המספרי
59 | IEEE 754
60 | יכולה לייצג באופן מדוייק ערכים שלמים בטווח הערכים
61 | -2^53^
62 | עד
63 | 2^53^.
64 | מעבר לטווח ״בטוח״ זה, ייצוג בינארי שונה משמש למספר מרובה של ערכים נומריים. משמעות הדבר היא שג׳אווהסקריפט יכולה לייצג ערכים שלמים בצורה מדוייקת בתוך טווח זה לפני שצצות בעיות.
65 | לדוגמה:
66 |
67 |
75 |
76 | כפי שרואים בדוגמה לעיל, שני ערכים שונים מיוצגים על ידי אותו ערך מספרי שלם.
77 | התופעה תופיע באופן תכוף יותר ככל שהערך המדובר רחוק יותר מן הטווח הבטוח.
78 |
79 | אקמהסקריפט 6 הוסיפה את המתודה
80 | `Number.isSafeInteger()`
81 | כדי לזהות בצורה טובה יותר ערכים שלמים שהשפה מציגה בצורה מדויקת. היא גם הוסיפה את התכונות
82 | `Number.MAX_SAFE_INTEGER`
83 | ו-
84 | `Number.MIN_SAFE_INTEGER`
85 | כדי לייצג את הגבול העליון והתחתון, בהתאמה, של הטווח הבטוח.
86 | המתודה
87 | `Number.isSafeInteger()`
88 | יכולה להבטיח לנו שערך הינו מספר שלם שנמצא בטווח הערכים השלמים הבטוח לשימוש, כמו בדוגמה הבאה:
89 |
90 |
103 |
104 | הערך
105 | `inside`
106 | הינו הערך המספרי השלם הבטוח הגדול ביותר, ולכן הוא מחזיר את הערך
107 | `true`
108 | עבור המתודות
109 | `Number.isInteger()`
110 | ו-
111 | `Number.isSafeInteger()`.
112 | המספר
113 | `outside`
114 | אינו נחשב לערך בטוח למרות שעדיין מדובר בערך מספרי שלם.
115 |
116 | רוב הזמן, נרצה להשתמש בערכים בטוחים לשימוש בעת חישובים או השוואות בג׳אווהסקריפט, כך ששימוש במתודה
117 | `Number.isSafeInteger()`.
118 | כחלק מוולידציה של קלט מהמשתמש נחשב לרעיון טוב.
119 |
120 | ## מתודות מתמטיות חדשות
121 |
122 | הדגש החדש על גיימינג
123 | (Gaming)
124 | וגרפיקה שהוביל את אקמהסקריפט 6 לכלול בתוכה מערכים בינאריים הוביל להבנה שמנוע הריצה של ג׳אווהסקריפט יכול לבצע חישובים מתמטיים רבים בצורה יעילה יותר. אך שיטות אופטימיזציה כמו
125 | asm.js,
126 | שפועלות על תת-קבוצה של ג׳אווהסקריפט על מנת לשפר ביצועים, זקוקות ליותר נתונים כדי לבצע חישובים באופן המהיר ביותר. למשל, הידיעה האם יש להתייחס לערכים נומריים בתור ערכים שלמים בני 32 ביט או בתור מספרים צפים בגודל 64 ביט חשובה עבור פעולות תלויות חומרה, שהן מהירות באופן משמעותי יותר מאשר פעולות על בסיס תוכנה.
127 |
128 | כתוצאה מכך, אקמהסקריפט 6 הוסיפה מספר מתודות לאובייקט
129 | `Math`
130 | כדי לשפר את מהירות החישובים המתמטיים הנפוצים.
131 | שיפור מהירות החישובים הנפוצים גם שיפרה את מהירות האפליקציות שמבצעות חישובים רבים, כמו תוכנות גרפיקה רבות. המתודות החדשות מופיעות בהמשך:
132 |
133 | * `Math.acosh(x)` מחזיר קוסינוס היפרבולי הפוך עבור `x`.
134 | * `Math.asinh(x)` מחזיר סינוס היפרבולי הפוך עבור `x`.
135 | * `Math.atanh(x)` מחזיר טנגנס היפרבולי הפוך עבור `x`
136 | * `Math.cbrt(x)` מחזיר שורש מעוקב עבור `x`.
137 | * `Math.clz32(x)` מחזיר את מספר הביטים המובילים בעלי ערך אפס בייצוג 32 ביט עבור `x`.
138 | * `Math.expm1(x)` מחזיר את התוצאה של חיסור 2 מהפונקציה האקספוננציאלית של `x`
139 | * `Math.fround(x)` מחזיר את המספר הצף הקרוב ביותר עבור `x`.
140 | * `Math.hypot(...values)` מחזיר את שורש סכום החזקות של כל הארגומנטים
141 | * `Math.imul(x, y)` מחזיר את תוצאת מכפלת 32 ביט של שני הארגומנטים
142 | * `Math.log1p(x)` מחזיר את הלוגריתם הטבעי של (`1 + x`).
143 | * `Math.log10(x)` מחזיר את הלוגריתם על העשרוני של `x`.
144 | * `Math.log2(x)` מחזיר את הלוגריתם על בסיס 2 של `x`.
145 | * `Math.sign(x)` מחזיר -1 אם הארגומנט שלילי, 0 אם ערכו +0 או -0 , ואת הערך 1 אם הארגומנט חיובי
146 | * `Math.cosh(x)` מחזיר קוסינוס היפרבולי עבור `x`.
147 | * `Math.sinh(x)` מחזיר את הסינוס ההיפרבולי עבור `x`.
148 | * `Math.tanh(x)` מחזיר טנגנס היפרבולי עבור `x`.
149 | * `Math.trunc(x)` מסלק ספרות לאחר הנקודה הדצימלית ממספר צף ומחזיר מספר שלם.
150 |
151 | הספר הזה לא ירחיב על כל מתודה חדשה שהוזכרה. אך במידה והאפליקציה שלכם צריכה לבצע חישובים מסוג נפוץ יחסית, חשוב לבדוק האם קיימת מתודה חדשה על האובייקט
152 | `Math`
153 | שעושה זאת בעבורכם לפני שמנסים לממש אותה בעצמכם.
154 |
155 | ## מזהי יוניקוד
156 |
157 | אקמהסקריפט 6 מציעה לנו תמיכה טובה יותר ביוניקוד מאשר גרסאות קודמות והיא גם משנה את סוג התווים שיכולים לשמש בתור מזהים.
158 | בגרסת אקמהסקריפט 5, היה ניתן להשתמש ברצף תווי בריחה מסוג יוניקוד בתור מזהים.
159 | לדוגמה:
160 |
161 |
162 |
174 |
175 | לאחר פקודת
176 | `var`
177 | בדוגמה זו,
178 | ניתן להשתמש ברצף התווים
179 | `\u0061`
180 | או פשוט
181 | `a`
182 | על מנת לגשת למשתנה.
183 | בגרסת אקמהסקריפט 6 ניתן להשתמש ברצף בריחה של נקודות קוד של יוניקוד בתור מזהים.
184 | למשל:
185 |
186 |
198 |
199 | הדוגמה לעיל רק מחליפה את המזהה
200 | `\u0061`
201 | בנקודת הקוד המקבילה. חוץ מהבדל זה היא זהה לחלוטין לדוגמה הקודמת.
202 |
203 | בנוסף לכך, אקמהסקריפט 6 מגדירה באופן רשמי מזהים תקינים לפי המונחים המצויים בדוקומנטציה של
204 |
205 | [Unicode Standard Annex #31: Unicode Identifier and Pattern Syntax](http://unicode.org/reports/tr31/)
206 | ,
207 | שנותנת לנו את הכללים הבאים:
208 |
209 | 1. התו הראשון צריך להיות התו
210 | `$`, `_`,
211 | או כל תו יוניקוד בעל תכונת
212 | `ID_Start`.
213 | 2. תו עוקב חייב להיות מהתווים
214 | `$`, `_`,
215 | `\u200c`
216 | (
217 | תו לא מחבר באורך אפסי
218 | a zero-width non-joiner
219 | ),
220 | `\u200d`
221 | (
222 | תו מחבר באורך אפסי
223 | a zero-width joiner
224 | ),
225 | או כל תו יוניקוד אחר בעל תכונת
226 | `ID_Continue`.
227 |
228 | התכונות
229 | `ID_Start`
230 | ו-
231 | `ID_Continue`
232 | מוגדרות בדוקומנטציה האמורה לעיל בתור דרך לזהות תווים שמקובלים לשימוש כמזהים בתור משתנים ושמות דומיין.
233 | ההגדרה המופיעה שם אינה בלעדית לג׳אווהסקריפט בלבד.
234 |
235 | ## התכונה `__proto__`
236 |
237 | אפילו לפני שהוציאו לאור את גרסת אקמהסקריפט 5, מספר מנועי ריצה של ג׳אווהסקריפט כבר מימשו תכונה מיוחדת בשם
238 | `__proto__`
239 | שהייתה מסוגלת לקרוא ולקבוע את התכונה הפנימית
240 | `[[Prototype]]`.
241 | באופן מעשי,
242 | התכונה
243 | `__proto__`
244 | הייתה הקדמה למתודות
245 | `Object.getPrototypeOf()`
246 | ו-
247 | `Object.setPrototypeOf()`.
248 | הציפייה שכל מנועי הריצה של ג׳אווהסקריפט יסירו את התכונה אינה מציאותית
249 | (היו ספריות ג׳אווהסקריפט פופולריות שעשו שימוש בתכונה
250 | `__proto__`),
251 | ולכן אקמהסקריפט 6 הפכה את התנהגות התכונה
252 | `__proto__`
253 | לרשמית.
254 | אך השינוי מופיע בנספח
255 | B
256 | של תקן
257 | ECMA-262
258 | ביחד עם אזהרה זו:
259 |
260 |
261 |
262 | > These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the
263 | implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.
264 |
265 |
266 |
267 | > תכונות אלו אינן נחשבות לחלק מעיקרי השפה.
268 | מתכנתים לא צריכים להשתמש או להסתמך על קיום תכונות והתנהגויות אלו בעת כתיבת קוד אקמהסקריפט חדש. מימושים של אקמהסקריפט צריכים להימנע ממימוש תכונות אלו אלא אם כן המימוש הינו חלק מדפדפן או נדרש להריץ את אותו קוד מגרסה קודמת של אקמהסקריפט שדפדפנים מריצים.
269 |
270 | האפיון עבור אקמהסקריפט ממליץ להשתמש
271 | במתודות
272 | `Object.getPrototypeOf()`
273 | ו-
274 | `Object.setPrototypeOf()`
275 | מכיוון שלתכונה
276 | `__proto__`
277 | יש את המאפיינים הבאים:
278 |
279 | 1. ניתן להגדיר את התכונה
280 | `__proto__`
281 | פעם אחת בלבד בעת הגדרת אובייקט ליטראל. אם נגדיר שתי תכונות מסוג
282 | `__proto__`
283 | תיזרק שגיאה. זוהי התכונה היחידה בתוך אובייקט ליטראל בעלת מגבלה זו.
284 | 1. הצורה המחושבת של תכונה זו
285 | `["__proto__"]`
286 | פועלת כמו תכונה רגילה ולא קוראת או משנה את הפרוטוטייפ של האובייקט הנוכחי. כל הכללים
287 | הקשורים לתכונות אובייקט ליטראל תקפים בצורה זו בניגוד לצורה הלא מחושבת, שיש לה כללים יוצאי דופן.
288 |
289 | בעוד שרצוי להימנע משימוש בתכונה
290 | `__proto__`
291 | הצורה שבה היא נמצאת באפיון השפה מעניינת מאוד. במנועי ריצה של אקמהסקריפט 6, התכונה
292 | `Object.prototype.__proto__`
293 | מוגדרת בתור תכונת גישה שהמתודה
294 |
295 | שלה קוראת למתודה
296 | `Object.getPrototypeOf()`
297 | והמתודה
298 | `set`
299 | שלה קוראת למתודה
300 | `Object.setPrototypeOf()`.
301 | זה אומר שאין הבדל אמיתי בין שימוש בתכונה
302 | `__proto__`
303 | לבין שימוש ב
304 | `Object.getPrototypeOf()`/`Object.setPrototypeOf()`,
305 | מלבד זאת שהתכונה
306 | `__proto__`
307 | מאפשרת לנו להגדיר פרוטוטייפ של אובייקט ליטראל בצורה ישירה.
308 | לדוגמה:
309 |
310 |
342 |
343 | במקום לקרוא למתודה
344 | `Object.create()`
345 | כדי לייצר את האובייקט
346 | `friend`
347 | דוגמה זו מייצרת אובייקט ליטראל סטנדרטי שמבצעת השמה לערך התכונה
348 | `__proto__`.
349 | בעת יצירת אובייקט בעזרת המתודה
350 | `Object.create()`
351 | ניאלץ להגדיר את כל מאפייני התכונה המלאים בעבור כל תכונה נוספת.
352 |
353 |
354 |
--------------------------------------------------------------------------------
/manuscript/01-Block-Bindings.md:
--------------------------------------------------------------------------------
1 | # Block Bindings
2 |
3 | Traditionally, the way variable declarations work has been one tricky part of programming in JavaScript. In most C-based languages, variables (or *bindings*) are created at the spot where the declaration occurs. In JavaScript, however, this is not the case. Where your variables are actually created depends on how you declare them, and ECMAScript 6 offers options to make controlling scope easier. This chapter demonstrates why classic `var` declarations can be confusing, introduces block-level bindings in ECMAScript 6, and then offers some best practices for using them.
4 |
5 | ## Var Declarations and Hoisting
6 |
7 | Variable declarations using `var` are treated as if they are at the top of the function (or global scope, if declared outside of a function) regardless of where the actual declaration occurs; this is called *hoisting*. For a demonstration of what hoisting does, consider the following function definition:
8 |
9 | ```js
10 | function getValue(condition) {
11 |
12 | if (condition) {
13 | var value = "blue";
14 |
15 | // other code
16 |
17 | return value;
18 | } else {
19 |
20 | // value exists here with a value of undefined
21 |
22 | return null;
23 | }
24 |
25 | // value exists here with a value of undefined
26 | }
27 | ```
28 |
29 | If you are unfamiliar with JavaScript, then you might expect the variable `value` to only be created if `condition` evaluates to true. In fact, the variable `value` is created regardless. Behind the scenes, the JavaScript engine changes the `getValue` function to look like this:
30 |
31 | ```js
32 | function getValue(condition) {
33 |
34 | var value;
35 |
36 | if (condition) {
37 | value = "blue";
38 |
39 | // other code
40 |
41 | return value;
42 | } else {
43 |
44 | return null;
45 | }
46 | }
47 | ```
48 |
49 | The declaration of `value` is hoisted to the top, while the initialization remains in the same spot. That means the variable `value` is actually still accessible from within the `else` clause. If accessed from there, the variable would just have a value of `undefined` because it hasn't been initialized.
50 |
51 | It often takes new JavaScript developers some time to get used to declaration hoisting, and misunderstanding this unique behavior can end up causing bugs. For this reason, ECMAScript 6 introduces block level scoping options to make the controlling a variable's lifecycle a little more powerful.
52 |
53 | ## Block-Level Declarations
54 |
55 | Block-level declarations are those that declare variables that are inaccessible outside of a given block scope. Block scopes, also called lexical scopes, are created:
56 |
57 | 1. Inside of a function
58 | 1. Inside of a block (indicated by the `{` and `}` characters)
59 |
60 | Block scoping is how many C-based languages work, and the introduction of block-level declarations in ECMAScript 6 is intended to bring that same flexibility (and uniformity) to JavaScript.
61 |
62 | ### Let Declarations
63 |
64 | The `let` declaration syntax is the same as the syntax for `var`. You can basically replace `var` with `let` to declare a variable, but limit the variable's scope to only the current code block (there are a few other subtle differences discussed a bit later, as well). Since `let` declarations are not hoisted to the top of the enclosing block, you may want to always place `let` declarations first in the block, so that they are available to the entire block. Here's an example:
65 |
66 | ```js
67 | function getValue(condition) {
68 |
69 | if (condition) {
70 | let value = "blue";
71 |
72 | // other code
73 |
74 | return value;
75 | } else {
76 |
77 | // value doesn't exist here
78 |
79 | return null;
80 | }
81 |
82 | // value doesn't exist here
83 | }
84 | ```
85 |
86 | This version of the `getValue` function behaves much closer to how you'd expect it to in other C-based languages. Since the variable `value` is declared using `let` instead of `var`, the declaration isn't hoisted to the top of the function definition, and the variable `value` is no longer accessible once execution flows out of the `if` block. If `condition` evaluates to false, then `value` is never declared or initialized.
87 |
88 | ### No Redeclaration
89 |
90 | If an identifier has already been defined in a scope, then using the identifier in a `let` declaration inside that scope causes an error to be thrown. For example:
91 |
92 | ```js
93 | var count = 30;
94 |
95 | // Syntax error
96 | let count = 40;
97 | ```
98 |
99 | In this example, `count` is declared twice: once with `var` and once with `let`. Because `let` will not redefine an identifier that already exists in the same scope, the `let` declaration will throw an error. On the other hand, no error is thrown if a `let` declaration creates a new variable with the same name as a variable in its containing scope, as demonstrated in the following code:
100 |
101 | ```js
102 | var count = 30;
103 |
104 | // Does not throw an error
105 | if (condition) {
106 |
107 | let count = 40;
108 |
109 | // more code
110 | }
111 | ```
112 |
113 | This `let` declaration doesn't throw an error because it creates a new variable called `count` within the `if` statement, instead of creating `count` in the surrounding block. Inside the `if` block, this new variable shadows the global `count`, preventing access to it until execution leaves the block.
114 |
115 | ### Constant Declarations
116 |
117 | You can also define variables in ECMAScript 6 with the `const` declaration syntax. Variables declared using `const` are considered *constants*, meaning their values cannot be changed once set. For this reason, every `const` variable must be initialized on declaration, as shown in this example:
118 |
119 | ```js
120 | // Valid constant
121 | const maxItems = 30;
122 |
123 | // Syntax error: missing initialization
124 | const name;
125 | ```
126 |
127 | The `maxItems` variable is initialized, so its `const` declaration should work without a problem. The `name` variable, however, would cause a syntax error if you tried to run the program containing this code, because `name` is not initialized.
128 |
129 |
130 | #### Constants vs Let Declarations
131 |
132 | Constants, like `let` declarations, are block-level declarations. That means constants are no longer accessible once execution flows out of the block in which they were declared, and declarations are not hoisted, as demonstrated in this example:
133 |
134 | ```js
135 | if (condition) {
136 | const maxItems = 5;
137 |
138 | // more code
139 | }
140 |
141 | // maxItems isn't accessible here
142 | ```
143 |
144 | In this code, the constant `maxItems` is declared within an `if` statement. Once the statement finishes executing, `maxItems` is not accessible outside of that block.
145 |
146 | In another similarity to `let`, a `const` declaration throws an error when made with an identifier for an already-defined variable in the same scope. It doesn't matter if that variable was declared using `var` (for global or function scope) or `let` (for block scope). For example, consider this code:
147 |
148 | ```js
149 | var message = "Hello!";
150 | let age = 25;
151 |
152 | // Each of these would throw an error.
153 | const message = "Goodbye!";
154 | const age = 30;
155 | ```
156 |
157 | The two `const` declarations would be valid alone, but given the previous `var` and `let` declarations in this case, neither will work as intended.
158 |
159 | Despite those similarities, there is one big difference between `let` and `const` to remember. Attempting to assign a `const` to a previously defined constant will throw an error, in both strict and non-strict modes:
160 |
161 | ```js
162 | const maxItems = 5;
163 |
164 | maxItems = 6; // throws error
165 | ```
166 |
167 | Much like constants in other languages, the `maxItems` variable can't be assigned a new value later on. However, unlike constants in other languages, the value a constant holds may be modified if it is an object.
168 |
169 | #### Declaring Objects with Const
170 |
171 | A `const` declaration prevents modification of the binding and not of the value itself. That means `const` declarations for objects do not prevent modification of those objects. For example:
172 |
173 | ```js
174 | const person = {
175 | name: "Nicholas"
176 | };
177 |
178 | // works
179 | person.name = "Greg";
180 |
181 | // throws an error
182 | person = {
183 | name: "Greg"
184 | };
185 | ```
186 |
187 | Here, the binding `person` is created with an initial value of an object with one property. It's possible to change `person.name` without causing an error because this changes what `person` contains and doesn't change the value that `person` is bound to. When this code attempts to assign a value to `person` (thus attempting to change the binding), an error will be thrown. This subtlety in how `const` works with objects is easy to misunderstand. Just remember: `const` prevents modification of the binding, not modification of the bound value.
188 |
189 | ### The Temporal Dead Zone
190 |
191 | A variable declared with either `let` or `const` cannot be accessed until after the declaration. Attempting to do so results in a reference error, even when using normally safe operations such as the `typeof` operation in this example:
192 |
193 | ```js
194 | if (condition) {
195 | console.log(typeof value); // ReferenceError!
196 | let value = "blue";
197 | }
198 | ```
199 |
200 | Here, the variable `value` is defined and initialized using `let`, but that statement is never executed because the previous line throws an error. The issue is that `value` exists in what the JavaScript community has dubbed the *temporal dead zone* (TDZ). The TDZ is never named explicitly in the ECMAScript specification, but the term is often used to describe why `let` and `const` declarations are not accessible before their declaration. This section covers some subtleties of declaration placement that the TDZ causes, and although the examples shown all use `let`, note that the same information applies to `const`.
201 |
202 | When a JavaScript engine looks through an upcoming block and finds a variable declaration, it either hoists the declaration to the top of the function or global scope (for `var`) or places the declaration in the TDZ (for `let` and `const`). Any attempt to access a variable in the TDZ results in a runtime error. That variable is only removed from the TDZ, and therefore safe to use, once execution flows to the variable declaration.
203 |
204 | This is true anytime you attempt to use a variable declared with `let` or `const` before it's been defined. As the previous example demonstrated, this even applies to the normally safe `typeof` operator. You can, however, use `typeof` on a variable outside of the block where that variable is declared, though it may not give the results you're after. Consider this code:
205 |
206 | ```js
207 | console.log(typeof value); // "undefined"
208 |
209 | if (condition) {
210 | let value = "blue";
211 | }
212 | ```
213 |
214 | The variable `value` isn't in the TDZ when the `typeof` operation executes because it occurs outside of the block in which `value` is declared. That means there is no `value` binding, and `typeof` simply returns `"undefined"`.
215 |
216 | The TDZ is just one unique aspect of block bindings. Another unique aspect has to do with their use inside of loops.
217 |
218 | ## Block Binding in Loops
219 |
220 | Perhaps one area where developers most want block level scoping of variables is within `for` loops, where the throwaway counter variable is meant to be used only inside the loop. For instance, it's not uncommon to see code like this in JavaScript:
221 |
222 | ```js
223 | for (var i = 0; i < 10; i++) {
224 | process(items[i]);
225 | }
226 |
227 | // i is still accessible here
228 | console.log(i); // 10
229 | ```
230 |
231 | In other languages, where block level scoping is the default, this example should work as intended, and only the `for` loop should have access to the `i` variable. In JavaScript, however, the variable `i` is still accessible after the loop is completed because the `var` declaration gets hoisted. Using `let` instead, as in the following code, should give the intended behavior:
232 |
233 | ```js
234 | for (let i = 0; i < 10; i++) {
235 | process(items[i]);
236 | }
237 |
238 | // i is not accessible here - throws an error
239 | console.log(i);
240 | ```
241 |
242 | In this example, the variable `i` only exists within the `for` loop. Once the loop is complete, the variable is no longer accessible elsewhere.
243 |
244 | ### Functions in Loops
245 |
246 | The characteristics of `var` have long made creating functions inside of loops problematic, because the loop variables are accessible from outside the scope of the loop. Consider the following code:
247 |
248 | ```js
249 | var funcs = [];
250 |
251 | for (var i = 0; i < 10; i++) {
252 | funcs.push(function() { console.log(i); });
253 | }
254 |
255 | funcs.forEach(function(func) {
256 | func(); // outputs the number "10" ten times
257 | });
258 | ```
259 |
260 | You might ordinarily expect this code to print the numbers 0 to 9, but it outputs the number 10 ten times in a row. That's because `i` is shared across each iteration of the loop, meaning the functions created inside the loop all hold a reference to the same variable. The variable `i` has a value of `10` once the loop completes, and so when `console.log(i)` is called, that value prints each time.
261 |
262 | To fix this problem, developers use immediately-invoked function expressions (IIFEs) inside of loops to force a new copy of the variable they want to iterate over to be created, as in this example:
263 |
264 | ```js
265 | var funcs = [];
266 |
267 | for (var i = 0; i < 10; i++) {
268 | funcs.push((function(value) {
269 | return function() {
270 | console.log(value);
271 | }
272 | }(i)));
273 | }
274 |
275 | funcs.forEach(function(func) {
276 | func(); // outputs 0, then 1, then 2, up to 9
277 | });
278 | ```
279 |
280 | This version uses an IIFE inside of the loop. The `i` variable is passed to the IIFE, which creates its own copy and stores it as `value`. This is the value used by the function for that iteration, so calling each function returns the expected value as the loop counts up from 0 to 9. Fortunately, block-level binding with `let` and `const` in ECMAScript 6 can simplify this loop for you.
281 |
282 | ### Let Declarations in Loops
283 |
284 | A `let` declaration simplifies loops by effectively mimicking what the IIFE does in the previous example. On each iteration, the loop creates a new variable and initializes it to the value of the variable with the same name from the previous iteration. That means you can omit the IIFE altogether and get the results you expect, like this:
285 |
286 | ```js
287 | var funcs = [];
288 |
289 | for (let i = 0; i < 10; i++) {
290 | funcs.push(function() {
291 | console.log(i);
292 | });
293 | }
294 |
295 | funcs.forEach(function(func) {
296 | func(); // outputs 0, then 1, then 2, up to 9
297 | })
298 | ```
299 |
300 | This loop works exactly like the loop that used `var` and an IIFE but is, arguably, cleaner. The `let` declaration creates a new variable `i` each time through the loop, so each function created inside the loop gets its own copy of `i`. Each copy of `i` has the value it was assigned at the beginning of the loop iteration in which it was created. The same is true for `for-in` and `for-of` loops, as shown here:
301 |
302 | ```js
303 | var funcs = [],
304 | object = {
305 | a: true,
306 | b: true,
307 | c: true
308 | };
309 |
310 | for (let key in object) {
311 | funcs.push(function() {
312 | console.log(key);
313 | });
314 | }
315 |
316 | funcs.forEach(function(func) {
317 | func(); // outputs "a", then "b", then "c"
318 | });
319 | ```
320 |
321 | In this example, the `for-in` loop shows the same behavior as the `for` loop. Each time through the loop, a new `key` binding is created, and so each function has its own copy of the `key` variable. The result is that each function outputs a different value. If `var` were used to declare `key`, all functions would output `"c"`.
322 |
323 | I> It's important to understand that the behavior of `let` declarations in loops is a specially-defined behavior in the specification and is not necessarily related to the non-hoisting characteristics of `let`. In fact, early implementations of `let` did not have this behavior, as it was added later on in the process.
324 |
325 | ### Constant Declarations in Loops
326 |
327 | The ECMAScript 6 specification doesn't explicitly disallow `const` declarations in loops; however, there are different behaviors based on the type of loop you're using. For a normal `for` loop, you can use `const` in the initializer, but the loop will throw a warning if you attempt to change the value. For example:
328 |
329 | ```js
330 | var funcs = [];
331 |
332 | // throws an error after one iteration
333 | for (const i = 0; i < 10; i++) {
334 | funcs.push(function() {
335 | console.log(i);
336 | });
337 | }
338 | ```
339 |
340 | In this code, the `i` variable is declared as a constant. The first iteration of the loop, where `i` is 0, executes successfully. An error is thrown when `i++` executes because it's attempting to modify a constant. As such, you can only use `const` to declare a variable in the loop initializer if you are not modifying that variable.
341 |
342 | When used in a `for-in` or `for-of` loop, on the other hand, a `const` variable behaves the same as a `let` variable. So the following should not cause an error:
343 |
344 | ```js
345 | var funcs = [],
346 | object = {
347 | a: true,
348 | b: true,
349 | c: true
350 | };
351 |
352 | // doesn't cause an error
353 | for (const key in object) {
354 | funcs.push(function() {
355 | console.log(key);
356 | });
357 | }
358 |
359 | funcs.forEach(function(func) {
360 | func(); // outputs "a", then "b", then "c"
361 | });
362 | ```
363 |
364 | This code functions almost exactly the same as the second example in the "Let Declarations in Loops" section. The only difference is that the value of `key` cannot be changed inside the loop. The `for-in` and `for-of` loops work with `const` because the loop initializer creates a new binding on each iteration through the loop rather than attempting to modify the value of an existing binding (as was the case with the previous example using `for` instead of `for-in`).
365 |
366 | ## Global Block Bindings
367 |
368 | Another way in which `let` and `const` are different from `var` is in their global scope behavior. When `var` is used in the global scope, it creates a new global variable, which is a property on the global object (`window` in browsers). That means you can accidentally overwrite an existing global using `var`, such as:
369 |
370 | ```js
371 | // in a browser
372 | var RegExp = "Hello!";
373 | console.log(window.RegExp); // "Hello!"
374 |
375 | var ncz = "Hi!";
376 | console.log(window.ncz); // "Hi!"
377 | ```
378 |
379 | Even though the `RegExp` global is defined on `window`, it is not safe from being overwritten by a `var` declaration. This example declares a new global variable `RegExp` that overwrites the original. Similarly, `ncz` is defined as a global variable and immediately defined as a property on `window`. This is the way JavaScript has always worked.
380 |
381 | If you instead use `let` or `const` in the global scope, a new binding is created in the global scope but no property is added to the global object. That also means you cannot overwrite a global variable using `let` or `const`, you can only shadow it. Here's an example:
382 |
383 | ```js
384 | // in a browser
385 | let RegExp = "Hello!";
386 | console.log(RegExp); // "Hello!"
387 | console.log(window.RegExp === RegExp); // false
388 |
389 | const ncz = "Hi!";
390 | console.log(ncz); // "Hi!"
391 | console.log("ncz" in window); // false
392 | ```
393 |
394 | Here, a new `let` declaration for `RegExp` creates a binding that shadows the global `RegExp`. That means `window.RegExp` and `RegExp` are not the same, so there is no disruption to the global scope. Also, the `const` declaration for `ncz` creates a binding but does not create a property on the global object. This capability makes `let` and `const` a lot safer to use in the global scope when you don't want to create properties on the global object.
395 |
396 | I> You may still want to use `var` in the global scope if you have a code that should be available from the global object. This is most common in a browser when you want to access code across frames or windows.
397 |
398 | ## Emerging Best Practices for Block Bindings
399 |
400 | While ECMAScript 6 was in development, there was widespread belief you should use `let` by default instead of `var` for variable declarations. For many JavaScript developers, `let` behaves exactly the way they thought `var` should have behaved, and so the direct replacement makes logical sense. In this case, you would use `const` for variables that needed modification protection.
401 |
402 | However, as more developers migrated to ECMAScript 6, an alternate approach gained popularity: use `const` by default and only use `let` when you know a variable's value needs to change. The rationale is that most variables should not change their value after initialization because unexpected value changes are a source of bugs. This idea has a significant amount of traction and is worth exploring in your code as you adopt ECMAScript 6.
403 |
404 | ## Summary
405 |
406 | The `let` and `const` block bindings introduce lexical scoping to JavaScript. These declarations are not hoisted and only exist within the block in which they are declared. This offers behavior that is more like other languages and less likely to cause unintentional errors, as variables can now be declared exactly where they are needed. As a side effect, you cannot access variables before they are declared, even with safe operators such as `typeof`. Attempting to access a block binding before its declaration results in an error due to the binding's presence in the temporal dead zone (TDZ).
407 |
408 | In many cases, `let` and `const` behave in a manner similar to `var`; however, this is not true for loops. For both `let` and `const`, `for-in` and `for-of` loops create a new binding with each iteration through the loop. That means functions created inside the loop body can access the loop bindings values as they are during the current iteration, rather than as they were after the loop's final iteration (the behavior with `var`). The same is true for `let` declarations in `for` loops, while attempting to use `const` declarations in a `for` loop may result in an error.
409 |
410 | The current best practice for block bindings is to use `const` by default and only use `let` when you know a variable's value needs to change. This ensures a basic level of immutability in code that can help prevent certain types of errors.
411 |
412 |
413 |
--------------------------------------------------------------------------------
/docs/01-Block-Bindings.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # שיוך משתנים לבלוקים של קוד
4 |
5 | הגדרת משתנים הייתה תמיד חלק בעייתי בג׳אווהסקריפט.
6 | ברוב השפות מבוססות שפת סי, משתנים נוצרים באותו מקום בקוד בו הם מוגדרים.
7 | המצב שונה בשפת ג׳אווהסקריפט.
8 | אופן ההגדרה של המשתנה תלוי בצורת הכתיבה, והמהדורה השישית של השפה נותנת אפשרויות חדשות לשליטה בהגדרת המשתנה והסביבה אליה הוא שייך
9 | (scope).
10 | פרק זה מראה מדוע הגדרת משתנים מסוג
11 | `var`
12 | יכולה ליצור בלבול, מציגה משתנים חדשים שמשוייכים מבחינת הסביבה שלהם לבלוקים של קוד
13 | ומציעה מספר שיטות מומלצות כיצד להשתמש בהם.
14 |
15 | ## הגדרת משתנים מסוג ״ var ״ ו״הרמת״ משתנים
16 |
17 | מתייחסים להגדרת משתנים מסוג
18 | `var`
19 | כאילו הוגדרו בראשית הפונקציה
20 | (או בסביבה הגלובלית, במידה והוגדרו מחוץ לפונקציה)
21 | ואין זה משנה כלל וכלל היכן המשתנה מוגדר בעת הכתיבה. התנהגות זו נקראת ״הרמת משתנים״.
22 | לדוגמה:
23 |
24 |
57 |
58 | אם ג׳אווהסקריפט היא שפה חדשה עבורכם ייתכן ותצפו מהמשתנה
59 | `value`
60 | שיתקיים אך ורק כאשר המשתנה בשם
61 | `condition`
62 | מקבל ערך מסוג ״אמת״
63 | (truthy value).
64 | למעשה, המשתנה
65 | `value`
66 | נוצר ללא קשר לערך המשתנה
67 | `condition`.
68 | מאחורי הקלעים סביבת הריצה משנה את הפונקציה
69 | `getValue`:
70 |
71 |
92 |
93 | פעולת ההגדרה של המשתנה
94 | `value`
95 | מורמת לתחילת הפונקציה בעוד שפעולת ההשמה והאתחול נשארת כפי שהייתה. המשמעות היא שהמשתנה
96 | `value`
97 | קיים ונגיש גם בתוך הבלוק של
98 | `else`.
99 | במידה וניגשים למשתנה הוא יהיה בעל הערך
100 | `undefined`
101 | מאחר ולא אותחל לערך אחר.
102 |
103 | לעיתים קרובות לוקח זמן מה עבור
104 | מפתחי ג׳אווהסקריפט חדשים להתרגל לעקרון ״ההרמה״ וחוסר הבנה של ההתנהגות יוצאת הדופן של השפה יכולה להוביל לבאגים. מסיבה זו אקמהסקריפט 6 הוסיפה אפשרות להגדיר משתנים שמשויכים לבלוקים של קוד ולא עבור פונקציות ועל ידי כך לשלוט בצורה טובה יותר על המשתנים שנוצרים בעת כתיבת הקוד.
105 |
106 | ## הגדרות משתנים ברמת בלוק
107 |
108 | הגדרות משתנים ברמת בלוק הן הגדרות שלא נגישות מחוץ לסביבת בלוק של קוד קיים.
109 | סביבת בלוק, שנקראת גם בשם סביבה לקסיקלית נוצרת:
110 |
111 | 1. בתוך פונקציה
112 | 1. בתוך בלוק של קוד.
113 |
114 | בלוק של קוד תחום בין התווים
115 | `{ }`
116 |
117 | שיוך סביבה לבלוק של קוד הינה הדרך בה עובדות שפות רבות מבוססות שפת סי, וההוספה של הגדרות ברמת בלוק של קוד באקמהסקריפט 6 נועדה להביא את אותה רמת גמישות
118 | (ואחידות)
119 | לשפה.
120 |
121 | ### הגדרות מסוג let
122 |
123 | הגדרת משתנה מסוג
124 | `let`
125 | זהה מבחינת כתיבה להגדרת משתנה מסוג
126 | `var`.
127 | ברמה הבסיסית
128 | אפשר להחליף הגדרת
129 | `var`
130 | בהגדרת
131 | `let`
132 | ובכך להגביל את שיוך המשתנה לבלוק הקוד הנוכחי
133 | (ישנם מספר הבדלים נוספים שיורחב עליהם בהמשך).
134 |
135 | מאחר והגדרות מסוג
136 | `let`
137 | אינן מורמות לתחילת בלוק הקוד הנוכחי, ייתכן ותרצה לכתוב הגדרות משתנים אלו בתחילת הבלוק, כך שיהיו נגישות עבור כל הבלוק.
138 |
139 | להלן דוגמה:
140 |
141 |
142 |
143 | ```js
144 | function getValue(condition) {
145 |
146 | if (condition) {
147 | let value = "כחול";
148 |
149 | // קוד אחר
150 |
151 | return value;
152 | } else {
153 |
154 | // המשתנה
155 | // value
156 | // אינו קיים כאן
157 |
158 | return null;
159 | }
160 |
161 | // המשתנה
162 | // value
163 | // אינו קיים כאן
164 | }
165 | ```
166 |
167 |
168 |
169 | גרסא זו של הפונקציה
170 | `getValue`
171 | מתנהגת בצורה דומה לאיך שמצופה משפות מבוססות שפת סי
172 | (C-based languages).
173 | מאחר והמשתנה בשם
174 | `value`
175 | מוגדר באמצעות
176 | `let`
177 | ההגדרה אינה מורמת לתחילת הפונקציה, והמשתנה
178 | `value`
179 | אינו נגיש ברגע שהקוד גולש מחוץ לבלוק ה
180 | `if`.
181 |
182 | אם ערך משתנה התנאי
183 | `condition`
184 | מקבל ערך ״שקרי״
185 |
186 | (falsy value)
187 |
188 | אזי המשתנה בשם
189 | `value`
190 | לא יוגדר ולא יאותחל לו ערך התחלתי.
191 |
192 | ### אין להגדיר שנית
193 |
194 | במידה ומשתנה מסוג
195 | `let`
196 | כבר הוגדר בתוך מרחב קוד, אז שימוש נוסף של
197 | `let`
198 | באותו המרחב יגרום לשגיאה. לדוגמה:
199 |
200 |
210 |
211 | בדוגמה למעלה המשתנה
212 | `count`
213 | מוגדר פעמיים: פעם אחת באמצעות
214 | `var`
215 | ופעם נוספת באמצעות
216 | `let`.
217 | מאחר ומשתנה מסוג
218 | `let`
219 | אינו יכול להגדיר מחדש משתנה שכבר קיים באותה הסביבה, תיזרק שגיאה.
220 | ואולם, לא תיזרק שגיאה אם הגדרת
221 | `let`
222 | מייצרת משתנה חדש בסביבה החיצונית, כפי שמדגים הקוד הבא:
223 |
224 |
225 |
226 | ```js
227 | var count = 30;
228 |
229 | // לא נזרקת שגיאה
230 | if (condition) {
231 |
232 | let count = 40;
233 |
234 | // קוד אחר
235 | }
236 | ```
237 |
238 |
239 |
240 | הגדרה מסוג
241 | `let`
242 | לא זורקת שגיאה מאחר והיא יוצרת משתנה חדש בשם
243 | `count`
244 | בתוך בלוק של הצהרת
245 | `if`,
246 | וזאת במקום לייצר את המשתנה בסביבה החיצונית לבלוק.
247 | בתוך בלוק ה -
248 | `if`
249 | המשתנה החדש שנוצר ״דורס״ את משתנה
250 | `count`
251 | החיצוני
252 |
253 | ### הגדרת משתנים קבועים
254 |
255 | באקמהסקריפט 6 ניתן להגדיר משתנים בעזרת המזהה
256 | `const`.
257 | משתנים שמוגדרים על ידי
258 | `const`
259 | נחשבים למשתנים *קבועים*, כלומר הערכים שהושמו בהם אינם ניתנים לשינוי.
260 | מסיבה זו חובה לאתחל כל משתנה מסוג
261 | `const`
262 | בזמן הגדרתו, כפי שרואים בדוגמה הבאה:
263 |
264 |
275 |
276 | המשתנה
277 | `maxItems`
278 | מאותחל בעת הגדרתו, לכן הקוד תקין.
279 | לעומת זאת, המשתנה
280 | `name`
281 | יזרוק שגיאת תחביר בעת הרצתו מאחר והמשתנה
282 | `name`
283 | אינו מאותחל
284 |
285 | #### הבדלים בין let ו const
286 |
287 | משתנים מסוג
288 | `const`
289 | בדומה למשתנים מסוג
290 | `let`
291 | הם משתנים המוגדרים בסביבת בלוק של קוד.
292 | המשמעות היא שמשתנים קבועים לא נגישים ברגע שהקוד שרץ יוצא מתחומי הבלוק שבו הוגדרו.
293 | בנוסף לכך משתנים קבועים אינם ״מורמים״ כפי שרואים בדוגמה הבאה:
294 |
295 |
309 |
310 | בדוגמת הקוד שראינו המשתנה הקבוע מוגדר בתוך בלוק של
311 | `if`.
312 | ברגע שהקוד בתוך אותו בלוק מסיים לרוץ המשתנה
313 | `maxItems`
314 | אינו נגיש יותר, כיוון שהוא אינו קיים מחוץ לבלוק שבו הוגדר.
315 |
316 | בדומה למשתנה מסוג
317 | `let`,
318 | משתנה מסוג
319 | `const`
320 | זורק שגיאה כאשר מנסים להשתמש בו עבור שם משתנה שכבר הוגדר באותה סביבה.
321 | אין זה משנה גם אם המשתנה הוגדר באמצעות
322 | `var`
323 | (עבור סביבה גלובלית או סביבת פונקציה)
324 | או באמצעות
325 | `let`
326 | (בתוך סביבת בלוק של קוד).
327 |
328 | לדוגמה:
329 |
330 |
331 |
332 | ```js
333 | var message = "שלום";
334 | let age = 25;
335 |
336 | // כל אחת מהשורות למטה תזרוק שגיאה
337 | const message = "להתראות";
338 | const age = 30;
339 | ```
340 |
341 |
342 |
343 | שתי ההגדרות מסוג
344 | `const`
345 | בדוגמה למעלה יהיו תקינות במצב רגיל, אך בגלל הגדרות מסוג
346 | `var`
347 | ו-
348 | `let`
349 | שקדמו להן, אף אחת לא תעבוד.
350 |
351 | למרות הדמיון הרב יש הבדל מובהק בין הגדרת
352 | `let`
353 | לבין הגדרת
354 | `const`.
355 | כל ניסיון לבצע השמת ערך חדש למשתנה מסוג
356 | `const`
357 | יזרוק שגיאה,
358 | בין אם מדובר במצב ״קפדני״
359 | (strict mode)
360 | או לא:
361 |
362 |
371 |
372 | בדומה למשתנים קבועים בשפות תכנות אחרות, המשתנה
373 | `maxItems`
374 | לא יכול לקבל ערך חדש מאוחר יותר.
375 | יחד עם זאת, בניגוד לשפות אחרות, במידה והערך הינו אוביקט ג׳אווהסקריפט אז הוא ניתן לשינוי.
376 |
377 | #### הגדרת אוביקט באמצעות משתנה קבוע
378 |
379 | הגדרת משתנה מסוג
380 | `const`
381 | מונעת שינוי של הקישור אך לא של הערך עצמו. משמעות הדבר היא שהגדרת משתנה מסוג
382 | `const`
383 | עבור אוביקט לא מונעת שינוי של אותו אוביקט. לדוגמה:
384 |
385 |
402 |
403 | בדוגמה הקודמת, הקישור עבור
404 | `person`
405 | נוצר עם הערך התחלתי של אוביקט בעל תכונה אחת.
406 | ניתן לשנות את הערך עבור
407 | `person.name`
408 | מבלי שתיזרק שגיאה מאחר והדבר משנה רק את התוכן של
409 | `person`
410 | ולא משנה את הערך ש
411 | `person`
412 | קשור אליו.
413 |
414 | כאשר מנסים לתת ערך חדש למשתנה
415 | `person`
416 | (ועל ידי כך לשנות את הקישור אליו),
417 | תיזרק שגיאה.
418 |
419 | קל להתבלבל מהתנהגות זו ולכן יש לזכור את העקרון הבא:
420 | שימוש ב-
421 | `const`
422 | מונע שינוי של הקישור לערך, אך אינו מונע שינוי של הערך עצמו.
423 |
424 | ### האזור המת הזמני - The Temporal Dead Zone
425 |
426 | לא ניתן להשתמש במשתנה שהוגדר באמצעות
427 | `let`
428 | או
429 | `const`
430 | עד לאחר שהוגדר. ניסיון להשתמש במשתנה קודם כן זורק שגיאת ייחוס
431 | (ReferenceError),
432 | השגיאה נזרקת גם אם משתמשים בפעולות בטוחות יחסית כמו פעולת
433 | `typeof`
434 | בדוגמה הבאה:
435 |
436 |
446 |
447 | בדוגמה זו, המשתנה בשם
448 | `value`
449 | מוגדר ומאותחל באמצעות הגדרת
450 | `let`
451 | אך קודם לכן נזרקת שגיאה בגלל שורת הקוד הקודמת. הבעיה היא שהמשתנה
452 | `value`
453 | נמצא בתוך מה שקהילת המפתחים כינתה בשם
454 | *האזור המת הזמני*
455 | (TDZ).
456 |
457 | השם
458 | TDZ
459 | אינו מופיע באופן מפורש באפיון אקמהסקריפט אך המונח משמש כדי לתאר מדוע הגדרות מסוג
460 | `let`
461 | ו-
462 | `const`
463 | אינן נגישות לפני הגדרתן. בהמשך נעבור על מספר תופעות שנגרמות עקב עקרון ה
464 | TDZ
465 | ולמרות שהדוגמאות משתמשות במשתנה מסוג
466 | `let`,
467 | חשוב לדעת שהמידע משמש גם עבור משתנה מסוג
468 | `const`
469 |
470 | כאשר מנוע ריצה של ג׳אווהסקריפט מסתכל בתוך בלוק של קוד ומוצא הגדרת משתנה, אזי הוא מרים את ההגדרה לתחילת הפונקציה או לתחילת הסביבה הגלובלית
471 | (עבור משתנה מסוג
472 | `var`)
473 | או שאותו מנוע ריצה ממקם את ההגדרה בתוך ה-
474 | TDZ
475 | (
476 | עבור משתנים מסוג
477 | `let`
478 | ו
479 | `const`
480 | ).
481 | כל ניסיון לגשת למשתנה בתוך ה
482 | TDZ
483 | יגרום לזריקת שגיאה בזמן ריצה.
484 |
485 | אותו משתנה יוצא מן ה
486 | TDZ
487 | והופך בטוח לשימוש,
488 | רק כאשר הקוד מגיע לשורת הקוד בה מוגדר המשתנה.
489 |
490 | אותה התנהגות מתקיימת בכל ניסיון לגשת למשתנה מסוג
491 | `let` או `const`
492 | טרם הגדרתו. כפי שמדגימה הדוגמה הקודמת, הדבר תקף גם עבור האופרטור
493 | `typeof`
494 | שנחשב לבטוח לשימוש.
495 | יחד עם זאת ניתן להשתמש ב
496 | `typeof`
497 | על משתנה שקיים מחוץ לבלוק בו מוגדר המשתנה, אך לא בטוח שיתקבלו אותן התוצאות :
498 |
499 |
510 |
511 | המשתנה
512 | `value`
513 | אינו נמצא בתוך
514 | TDZ
515 | בזמן שאופרטור
516 | `typeof`
517 | פועל מאחר והאופרציה מתבצעת מבחוץ לבלוק הקוד שבתוכו מוגדר המשתנה
518 | `value`.
519 | משמעות הדבר היא שכלל לא קיים קישור עבור
520 | `value`
521 | ולכן פעולת
522 | `typeof`
523 | תסתיים עם הערך
524 | `"undefined"`.
525 |
526 | ה-
527 | TDZ
528 | מהווה רק היבט ייחודי אחד של קשירות לסביבת בלוק.
529 | היבט ייחודי אחר מתבטא בעת השימוש בלולאות.
530 |
531 | ## קישור משתנים בסביבת בלוק בתוך לולאות
532 |
533 | אחד המקומות הטובים ביותר עבור משתנים ברמת בלוק הוא בתוך לולאות
534 | `for`,
535 | היכן שמשתנה אינדקס נועד לשימוש רק בתוך הלולאה. ראה דוגמה :
536 |
537 |
551 |
552 | בשפות אחרות היכן ששיוך ברמת בלוק קורה כברירת מחדל, הדוגמה האחרונה תעבוד כך שהמשתנה
553 | `i`
554 | נגיש אך ורק בתוך לולאת ה
555 | `for`.
556 | לעומת זאת, בג׳אווהסקריפט המשתנה
557 | `i`
558 | עדיין נגיש לאחר שהלולאה סיימה לרוץ מאחר והגדרת ה
559 | `var`
560 | מורמת.
561 | באמצעות שימוש בהגדרת
562 | `let`
563 | תתקבל התוצאה הרצויה, כפי שמודגם בהמשך:
564 |
565 |
579 |
580 | בדוגמה זו, המשתנה
581 | `i`
582 | קיים אך ורק בתוך לולאת ה
583 | `for`.
584 | ברגע שהלולאה סיימה לרוץ המשתנה אינו נגיש.
585 |
586 | ### פונקציות בתוך לולאות
587 |
588 | מאז ומתמיד משתנים מסוג
589 | `var`
590 | היו בעייתיים מבחינת הגדרת פונקציות בתוך לולאות, מאחר ומשתנים שנוצרו בלולאה עדיין נגישים מחוצה לה.
591 | לדוגמה:
592 |
593 |
594 |
595 | ```js
596 | var funcs = [];
597 |
598 | for (var i = 0; i < 10; i++) {
599 | funcs.push(function() { console.log(i); });
600 | }
601 |
602 | funcs.forEach(function(func) {
603 | func(); // המספר ״10״ יודפס עשר פעמים
604 | });
605 | ```
606 |
607 |
608 |
609 | ייתכן ותצפו שהקוד הנ״ל ידפיס את הספרות 0-9 אך במקום זאת הוא ידפיס את המספר 10 עשר פעמים רצופות.
610 | הסיבה היא שהמשתנה
611 | `i`
612 | קיים באופן משותף עבור כל איטרציה של הלולאה, והמשמעות היא שפונקציות שנוצרו בתוך הלולאה מתייחסות לאותו משתנה.
613 | המשתנה
614 | `i`
615 | מקבל את הערך
616 | `10`
617 | לאחר שהלולאה מסתיימת, וכאשר קוראים ל
618 | `console.log(i)`
619 | זהו הערך שיודפס.
620 |
621 | כדי לטפל בבעיה זו נהוג להשתמש בפונקציה שנקראת באופן מיידי
622 | (IIFEs - immediately-invoked function expressions)
623 | בתוך לולאות על מנת ליצור עותק מקומי לפונקציה.
624 | לדוגמה:
625 |
626 |
645 |
646 | בדוגמה זו משתמשים ב
647 | (IIFE)
648 | בתוך הלולאה.
649 |
650 | המשתנה
651 | `i`
652 | מועבר לתוך ה
653 | IIFE
654 | אשר יוצרת עותק מקומי משלה ושומרת אותו בתור המשתנה בשם
655 | `value`.
656 | זהו הערך שישמש בתוך הפונקציה, עבור אותה איטרציה, וכך הקריאה לכל פונקציה מדפיסה את הערך לו מצפים, ספירה מ
657 | 0
658 | ועד
659 | 9.
660 |
661 | למרבה המזל, שיוך לבלוק באמצעות משתנים מסוג
662 | `let`
663 | ו
664 | `const`
665 | מאפשר לנו לעשות זאת באופן פשוט וקל.
666 |
667 | ### הגדרת let בתוך לולאה
668 |
669 | הגדרת
670 | `let`
671 | מפשטת שימוש בלולאות על ידי מתן אותה תוצאה כמו השימוש ב
672 | IIFE
673 | בדוגמה הקודמת.
674 |
675 | בכל איטרציה, הלולאה יוצרת משתנה חדש ומאתחלת אותו לערך של המשתנה בעל אותו השם מהאיטרציה האחרונה של הלולאה.
676 | בצורה זו אין יותר צורך להשתמש ב
677 | IIFE
678 | על מנת לקבל את התוצאה הרצויה.
679 |
680 | לדוגמה:
681 |
682 |
683 | ```js
684 | var funcs = [];
685 |
686 | for (let i = 0; i < 10; i++) {
687 | funcs.push(function() {
688 | console.log(i);
689 | });
690 | }
691 |
692 | funcs.forEach(function(func) {
693 | func(); // יודפס 0 ואז 1 ואז 2 עד שמגיעים ל 9
694 | })
695 | ```
696 |
697 |
698 |
699 | לולאה כמו בדוגמה האחרונה פועלת באותה צורה כמו הלולאה בדוגמה שלפניה שהשתמשה במשתנה מסוג
700 | `var`
701 | ביחד עם
702 | IIFE,
703 | אך היא קריאה וקצרה יותר.
704 | השימוש במשתנה מסוג
705 | `let`
706 | יוצר משתנה חדש בשם
707 | `i`
708 | בכל איטרציה של הלולאה, ולכן כל פונקציה שמוגדרת בתוך הלולאה מקבלת עותק מקומי של המשתנה
709 | `i`.
710 | לכל עותק של המשתנה
711 | `i`
712 | ניתן הערך שהתקבל בתחילת האיטרציה שבה נוצר.
713 | הדבר נכון גם עבור לולאות מסוג
714 | `for-in` ו `for-of`
715 | כפי שניתן לראות בדוגמה הבאה:
716 |
717 |
745 |
746 | בדוגמה זו לולאה
747 | `for-in`
748 | פועלת באותה צורה כמו לולאת
749 | `for`.
750 | בכל איטרציה של הלולאה נוצר שיוך חדש
751 | (binding)
752 | של המשתנה
753 | `key`
754 | ולכן כל פונקציה מקבלת עותק מקומי משלה עבור המשתנה
755 | `key`.
756 | כתוצאה מכך כל פונקציה מדפיסה ערך שונה.
757 | לו היינו משתמשים במשתנה מסוג
758 | `var`
759 | במקום משתנה מסוג
760 | `let`
761 | אזי כל הפונקציות היו מדפיסות
762 | `"c"`.
763 |
764 | !> חשוב
765 | לדעת שההתנהגות של משתנים מסוג
766 | `let`
767 | בתוך לולאות היא התנהגות שמאופיינת בשפה עצמה
768 | ואינה בהכרח קשורה למאפייני הגדרת
769 | `let`
770 | כמשתנה שאינו מורם.
771 | למעשה, מימושים מוקדמים פעלו ללא התנהגות זו והיא התווספה רק בשלב מאוחר יותר.
772 |
773 | ### הגדרת משתנים קבועים בתוך לולאות
774 |
775 | המהדורה השישית של אקמהסקריפט אינה אוסרת שימוש בהגדרת
776 | `const`
777 | בתוך לולאות. אך קיימת התנהגות שונה בהתאם לסוג הלולאה.
778 |
779 | עבור לולאת
780 | `for`
781 | רגילה, ניתן להשתמש במשתנה מסוג
782 | `const`
783 | עבור החלק המאתחל של הלולאה, אך הלולאה תזרוק שגיאה במידה ומנסים לשנות את הערך.
784 | לדוגמה:
785 |
786 |
787 |
788 | ```js
789 | var funcs = [];
790 |
791 | // שגיאה לאחר האיטרציה הראשונה
792 | for (const i = 0; i < 10; i++) {
793 | funcs.push(function() {
794 | console.log(i);
795 | });
796 | }
797 | ```
798 |
799 |
800 |
801 | בדוגמת הקוד האחרונה, המשתנה
802 | `i`
803 | מוגדר בתור משתנה קבוע.
804 | האיטרציה הראשונה של הלולאה, היכן שערכו של המשתנה
805 | `i`
806 | אותחל ל 0
807 | תרוץ בהצלחה.
808 | אולם, תיזרק שגיאה כאשר הקוד
809 | `i++`
810 | ירוץ מאחר והקוד הנ״ל מנסה לשנות ערך קבוע.
811 | לכן ניתן להשתמש בהגדרת
812 | `const`
813 |
814 | בניגוד לכך, כאשר משתמשים בו בלולאות מסוג
815 | `for-in` או `for-of`
816 | משתנה מסוג
817 | `const`
818 | מתנהג בדיוק כמו משתנה מסוג
819 | `let`.
820 | לכן דוגמה הקוד הבאה תרוץ בהצלחה
821 |
822 |
851 |
852 | דוגמה הקוד האחרונה מתנהגת כמו הדוגמה השניה בפסקה
853 | "הגדרת let בתוך לולאה".
854 | ההבדל היחיד הוא שהערך של המשתנה
855 | `key`
856 | אינו ניתן לשינוי בתוך הלולאה.
857 | לולאות מסוג
858 | `for-in` או `for-of`
859 | עובדות בצורה כזו בשילוב עם הגדרות
860 | `const`
861 | מאחר ומאתחל הלולאה יוצר שיוך חדש בכל איטרציה ולא מנסה לשנות את הערך של שיוך קיים
862 | (כפי שקרה בדוגמה הקודמת כאשר ניסינו לעשות זאת בתוך לולאה מסוג לולאת `for`).
863 |
864 | ## שיוך לסביבת בלוק גלובלית
865 |
866 | הבדל נוסף בין משתנים מסוג
867 | `let`
868 | ו
869 | `const`
870 | לבין משתנים מסוג
871 | `var`
872 | הוא התנהגותם בסביבה הגלובלית.
873 | כאשר משתנים מסוג
874 | `var`
875 | מוגדרים בסביבה הגלובלית, הדבר יוצר משתנה גלובלי חדש,
876 | שהופך לשם תכונה של האובייקט הגלובלי
877 | (`window` בדפדפנים).
878 | בצורה כזו ניתן בטעות לדרוס ערך גלובלי קיים,
879 | לדוגמה:
880 |
881 |
893 |
894 | למרות שהמשתנה הגלובלי
895 | `RegExp`
896 | מוגדר כחלק של
897 | `window`
898 | אין לו חסינות מפני דריסה על ידי משתנה מסוג
899 | `var`.
900 | הדוגמה הנ״ל מגדירה משתנה גלובלי חדש בשם
901 | `RegExp`
902 | אשר דורסת את המשתנה המקורי.
903 | בצורה דומה,
904 | המשתנה
905 | `ncz`
906 | מוגדר כמשתנה בסביבה הגלובלית ומייד מגדיר מפתח חדש באוביקט
907 | `window`.
908 | זוהי הדרך שבה ג׳אווהסקריפט עובדת מאז תחילתה.
909 |
910 | אילו היינו משתמשים במשתנים מסוג
911 | `let`
912 | או
913 | `const`
914 | בסביבה הגלובלית,
915 | היינו יוצרים שיוך חדש בסביבה הגלובלית אך לא הייתה מתווספת תכונה חדשה לאוביקט הגלובלי.
916 | משמעות הדבר היא שלא ניתן לדרוס משתנה גלובלי על ידי הגדרת
917 | `let` או `const`,
918 | ניתן רק להסתיר אותו.
919 |
920 | לדוגמה:
921 |
922 |
936 |
937 | בדוגמה זו,
938 | משתנה חדש מסוג
939 | `let`
940 | מוגדר תחת השם
941 | `RegExp`
942 | ובתורו מסתיר את משתנה
943 | `RegExp`
944 | שכבר קיים בסביבה הגלובלית.
945 |
946 | משמעות הדבר הינה שהערכים עבור
947 | `window.RegExp`
948 | ו
949 | `RegExp`
950 | שונים זה מזה, כך שאין דריסה של משתנים בסביבה הגלובלית.
951 | כמו כן, הגדרת
952 | `const`
953 | עבור המשתנה בשם
954 | `ncz`
955 | יוצרת שיוך עבור משתנה בסביבה הגלובלית אך אינה יוצרת מפתח חדש באוביקט הגלובלי.
956 | תכונה זו של משתנים מסוג
957 | `let`
958 | ו
959 | `const`
960 | הופכת אותם לבטוחים יותר לשימוש בסביבה הגלובלית כאשר אין זה רצוי ליצור מפתחות חדשים באוביקט הגלובלי.
961 |
962 | !> ניתן עדיין להשתמש בהגדרה מסוג
963 | `var`
964 | בסביבה הגלובלית עבור קוד שרוצים לאפשר את הגישה אליו מתוך האוביקט הגלובלי.
965 | זהו דבר נפוץ בסביבת הדפדפן כאשר ברצונך לשתף קוד בין אלמנטים מסוג
966 | iframe
967 | או חלונות נפרדים.
968 |
969 | ## דרכים מומלצות לשימוש בשיוך משתנים בסביבת בלוק
970 |
971 | בעת שהמהדורה השישית הייתה בפיתוח, היו רבים שהאמינו שיש להשתמש בהגדרת
972 | `let`
973 | בתור הגדרת ברירת מחדל במקום להשתמש בהגדרות מסוג
974 | `var`.
975 | עבור מפתחי ג׳אווהסקריפט רבים,
976 | הגדרות
977 | `let`
978 | פועלות כמו שהם האמינו שהגדרות מסוג
979 | `var`
980 | היו צריכות לפעול מלכתחילה,
981 | לכן החלפה ישירה בין הסוגים היא הגיונית.
982 | במקרים שכאלה, השימוש בהגדרות
983 | `const`
984 | ייעשה עבור משתנים שצריכים להיות מוגנים בפני שינוי.
985 |
986 | ואולם, עם הזמן התפתחה גישה חלופית לפיה
987 | יש להשתמש בהגדרות מסוג
988 | `const`
989 | בתור ברירת מחדל ויש להשתמש בהגדרות מסוג
990 | `let`
991 | כאשר ידוע מראש כי ערך המשתנה עתיד להשתנות.
992 | הרציונל לכך הוא שרוב המשתנים לא משנים את ערכם לאחר האתחול ואילו שינויי ערך שאינם צפויים הם מקור לבאגים.
993 |
994 | ## סיכום
995 |
996 | הגדרות משתנים עבור סביבת בלוק מסוג
997 | `let`
998 | ו
999 | `const`
1000 | מביאות איתן מה שנקרא שיוך לקסיקאלי לג׳אווהסקריפט.
1001 | הגדרות אלו אינן מורמות וקיימות רק עבור אותו הבלוק בקוד בו הוגדרו.
1002 | דבר זה מייצר התנהגות שדומה יותר לשפות קיימות אחרות, ופחות נוטה ליצירת שגיאות לא מכוונות, מאחר ומשתנים יכולים כעת להיות מוגדרים בדיוק היכן שמשתמשים בהם.
1003 | כתופעת לוואי לכך, לא ניתן לגשת למשתנים לפני שהם מוגדרים בפועל, אפילו לא באמצעות אופרטורים שנחשבים בטוחים לשימוש כגון אופרטור
1004 | `typeof`.
1005 | ניסיון לגשת למשתנה עבור סביבת בלוק לפני הגדרתו זורק שגיאה עקב שיוך המשתנה במה שנקרה
1006 | האזור המת הזמני
1007 | (TDZ).
1008 |
1009 | במקרים רבים,
1010 | משתנים מסוג
1011 | `let`
1012 | ו
1013 | `const`
1014 | מתנהגים באופן דומה למשתנים מסוג
1015 | `var`.
1016 | הדבר אינו נכון עבור לולאות.
1017 | השימוש במשתנים מסוג
1018 | `let`
1019 | ו
1020 | `const`
1021 | בתוך לולאות
1022 | `for-in`
1023 | ו
1024 | `for-of`
1025 | יוצר שיוך חדש עבור כל איטרציה של הלולאה.
1026 | משמעות הדבר היא שפונקציות אשר מוגדרות בתוך הלולאה יכולות לגשת למשתנים ולערכם כפי שהיה בעת האיטרציה הנוכחית,
1027 | ולא כפי שהם לאחר שהלולאה סיימה לעבוד
1028 | (
1029 | ההתנהגות הקיימת עבור משתנים מסוג
1030 | `var`
1031 | ).
1032 | הדבר נכון גם עבור הגדרת משתנים מסוג
1033 | `let`
1034 | בתוך לולאות מסוג לולאות,
1035 | `for`
1036 | בעוד שניסיון להשתמש בהגדרת
1037 | `const`
1038 | בתוך לולאת
1039 | `for`
1040 | עלול לזרוק שגיאה.
1041 |
1042 | הדרך המומלצת לשימוש במשתנים עבור סביבת בלוק הינה
1043 | להשתמש בהגדרת
1044 | `const`
1045 | כברירת מחדל ולהעדיף שימוש בהגדרת
1046 | `let`
1047 | כאשר ידוע לך שהמשתנה עתיד לקבל ערך שונה.
1048 | דרך זו מבטיחה רמה בסיסית של
1049 | קוד מוגן כנגד שינויים בלתי צפויים
1050 | שיכול למנוע שגיאות מסויימות.
1051 |
1052 |
2 |
3 | # שיוך משתנים לבלוקים של קוד
4 |
5 | הגדרת משתנים הייתה תמיד חלק בעייתי בג׳אווהסקריפט.
6 | ברוב השפות מבוססות שפת סי, משתנים נוצרים באותו מקום בקוד בו הם מוגדרים.
7 | המצב שונה בשפת ג׳אווהסקריפט.
8 | אופן ההגדרה של המשתנה תלוי בצורת הכתיבה, והמהדורה השישית של השפה נותנת אפשרויות חדשות לשליטה בהגדרת המשתנה והסביבה אליה הוא שייך
9 | (scope).
10 | פרק זה מראה מדוע הגדרת משתנים מסוג
11 | `var`
12 | יכולה ליצור בלבול, מציגה משתנים חדשים שמשוייכים מבחינת הסביבה שלהם לבלוקים של קוד
13 | ומציעה מספר שיטות מומלצות כיצד להשתמש בהם.
14 |
15 | ## הגדרת משתנים מסוג ״ var ״ ו״הרמת״ משתנים
16 |
17 | מתייחסים להגדרת משתנים מסוג
18 | `var`
19 | כאילו הוגדרו בראשית הפונקציה
20 | (או בסביבה הגלובלית, במידה והוגדרו מחוץ לפונקציה)
21 | ואין זה משנה כלל וכלל היכן המשתנה מוגדר בעת הכתיבה. התנהגות זו נקראת ״הרמת משתנים״.
22 | לדוגמה:
23 |
24 |
57 |
58 | אם ג׳אווהסקריפט היא שפה חדשה עבורכם ייתכן ותצפו מהמשתנה
59 | `value`
60 | שיתקיים אך ורק כאשר המשתנה בשם
61 | `condition`
62 | מקבל ערך מסוג ״אמת״
63 | (truthy value).
64 | למעשה, המשתנה
65 | `value`
66 | נוצר ללא קשר לערך המשתנה
67 | `condition`.
68 | מאחורי הקלעים סביבת הריצה משנה את הפונקציה
69 | `getValue`:
70 |
71 |
92 |
93 | פעולת ההגדרה של המשתנה
94 | `value`
95 | מורמת לתחילת הפונקציה בעוד שפעולת ההשמה והאתחול נשארת כפי שהייתה. המשמעות היא שהמשתנה
96 | `value`
97 | קיים ונגיש גם בתוך הבלוק של
98 | `else`.
99 | במידה וניגשים למשתנה הוא יהיה בעל הערך
100 | `undefined`
101 | מאחר ולא אותחל לערך אחר.
102 |
103 | לעיתים קרובות לוקח זמן מה עבור
104 | מפתחי ג׳אווהסקריפט חדשים להתרגל לעקרון ״ההרמה״ וחוסר הבנה של ההתנהגות יוצאת הדופן של השפה יכולה להוביל לבאגים. מסיבה זו אקמהסקריפט 6 הוסיפה אפשרות להגדיר משתנים שמשויכים לבלוקים של קוד ולא עבור פונקציות ועל ידי כך לשלוט בצורה טובה יותר על המשתנים שנוצרים בעת כתיבת הקוד.
105 |
106 | ## הגדרות משתנים ברמת בלוק
107 |
108 | הגדרות משתנים ברמת בלוק הן הגדרות שלא נגישות מחוץ לסביבת בלוק של קוד קיים.
109 | סביבת בלוק, שנקראת גם בשם סביבה לקסיקלית נוצרת:
110 |
111 | 1. בתוך פונקציה
112 | 1. בתוך בלוק של קוד.
113 |
114 | בלוק של קוד תחום בין התווים
115 | `{ }`
116 |
117 | שיוך סביבה לבלוק של קוד הינה הדרך בה עובדות שפות רבות מבוססות שפת סי, וההוספה של הגדרות ברמת בלוק של קוד באקמהסקריפט 6 נועדה להביא את אותה רמת גמישות
118 | (ואחידות)
119 | לשפה.
120 |
121 | ### הגדרות מסוג let
122 |
123 | הגדרת משתנה מסוג
124 | `let`
125 | זהה מבחינת כתיבה להגדרת משתנה מסוג
126 | `var`.
127 | ברמה הבסיסית
128 | אפשר להחליף הגדרת
129 | `var`
130 | בהגדרת
131 | `let`
132 | ובכך להגביל את שיוך המשתנה לבלוק הקוד הנוכחי
133 | (ישנם מספר הבדלים נוספים שיורחב עליהם בהמשך).
134 |
135 | מאחר והגדרות מסוג
136 | `let`
137 | אינן מורמות לתחילת בלוק הקוד הנוכחי, ייתכן ותרצה לכתוב הגדרות משתנים אלו בתחילת הבלוק, כך שיהיו נגישות עבור כל הבלוק.
138 |
139 | להלן דוגמה:
140 |
141 |
142 |
143 | ```js
144 | function getValue(condition) {
145 |
146 | if (condition) {
147 | let value = "כחול";
148 |
149 | // קוד אחר
150 |
151 | return value;
152 | } else {
153 |
154 | // המשתנה
155 | // value
156 | // אינו קיים כאן
157 |
158 | return null;
159 | }
160 |
161 | // המשתנה
162 | // value
163 | // אינו קיים כאן
164 | }
165 | ```
166 |
167 |
168 |
169 | גרסא זו של הפונקציה
170 | `getValue`
171 | מתנהגת בצורה דומה לאיך שמצופה משפות מבוססות שפת סי
172 | (C-based languages).
173 | מאחר והמשתנה בשם
174 | `value`
175 | מוגדר באמצעות
176 | `let`
177 | ההגדרה אינה מורמת לתחילת הפונקציה, והמשתנה
178 | `value`
179 | אינו נגיש ברגע שהקוד גולש מחוץ לבלוק ה
180 | `if`.
181 |
182 | אם ערך משתנה התנאי
183 | `condition`
184 | מקבל ערך ״שקרי״
185 |
186 | (falsy value)
187 |
188 | אזי המשתנה בשם
189 | `value`
190 | לא יוגדר ולא יאותחל לו ערך התחלתי.
191 |
192 | ### אין להגדיר שנית
193 |
194 | במידה ומשתנה מסוג
195 | `let`
196 | כבר הוגדר בתוך מרחב קוד, אז שימוש נוסף של
197 | `let`
198 | באותו המרחב יגרום לשגיאה. לדוגמה:
199 |
200 |
210 |
211 | בדוגמה למעלה המשתנה
212 | `count`
213 | מוגדר פעמיים: פעם אחת באמצעות
214 | `var`
215 | ופעם נוספת באמצעות
216 | `let`.
217 | מאחר ומשתנה מסוג
218 | `let`
219 | אינו יכול להגדיר מחדש משתנה שכבר קיים באותה הסביבה, תיזרק שגיאה.
220 | ואולם, לא תיזרק שגיאה אם הגדרת
221 | `let`
222 | מייצרת משתנה חדש בסביבה החיצונית, כפי שמדגים הקוד הבא:
223 |
224 |
225 |
226 | ```js
227 | var count = 30;
228 |
229 | // לא נזרקת שגיאה
230 | if (condition) {
231 |
232 | let count = 40;
233 |
234 | // קוד אחר
235 | }
236 | ```
237 |
238 |
239 |
240 | הגדרה מסוג
241 | `let`
242 | לא זורקת שגיאה מאחר והיא יוצרת משתנה חדש בשם
243 | `count`
244 | בתוך בלוק של הצהרת
245 | `if`,
246 | וזאת במקום לייצר את המשתנה בסביבה החיצונית לבלוק.
247 | בתוך בלוק ה -
248 | `if`
249 | המשתנה החדש שנוצר ״דורס״ את משתנה
250 | `count`
251 | החיצוני
252 |
253 | ### הגדרת משתנים קבועים
254 |
255 | באקמהסקריפט 6 ניתן להגדיר משתנים בעזרת המזהה
256 | `const`.
257 | משתנים שמוגדרים על ידי
258 | `const`
259 | נחשבים למשתנים *קבועים*, כלומר הערכים שהושמו בהם אינם ניתנים לשינוי.
260 | מסיבה זו חובה לאתחל כל משתנה מסוג
261 | `const`
262 | בזמן הגדרתו, כפי שרואים בדוגמה הבאה:
263 |
264 |
275 |
276 | המשתנה
277 | `maxItems`
278 | מאותחל בעת הגדרתו, לכן הקוד תקין.
279 | לעומת זאת, המשתנה
280 | `name`
281 | יזרוק שגיאת תחביר בעת הרצתו מאחר והמשתנה
282 | `name`
283 | אינו מאותחל
284 |
285 | #### הבדלים בין let ו const
286 |
287 | משתנים מסוג
288 | `const`
289 | בדומה למשתנים מסוג
290 | `let`
291 | הם משתנים המוגדרים בסביבת בלוק של קוד.
292 | המשמעות היא שמשתנים קבועים לא נגישים ברגע שהקוד שרץ יוצא מתחומי הבלוק שבו הוגדרו.
293 | בנוסף לכך משתנים קבועים אינם ״מורמים״ כפי שרואים בדוגמה הבאה:
294 |
295 |
309 |
310 | בדוגמת הקוד שראינו המשתנה הקבוע מוגדר בתוך בלוק של
311 | `if`.
312 | ברגע שהקוד בתוך אותו בלוק מסיים לרוץ המשתנה
313 | `maxItems`
314 | אינו נגיש יותר, כיוון שהוא אינו קיים מחוץ לבלוק שבו הוגדר.
315 |
316 | בדומה למשתנה מסוג
317 | `let`,
318 | משתנה מסוג
319 | `const`
320 | זורק שגיאה כאשר מנסים להשתמש בו עבור שם משתנה שכבר הוגדר באותה סביבה.
321 | אין זה משנה גם אם המשתנה הוגדר באמצעות
322 | `var`
323 | (עבור סביבה גלובלית או סביבת פונקציה)
324 | או באמצעות
325 | `let`
326 | (בתוך סביבת בלוק של קוד).
327 |
328 | לדוגמה:
329 |
330 |
331 |
332 | ```js
333 | var message = "שלום";
334 | let age = 25;
335 |
336 | // כל אחת מהשורות למטה תזרוק שגיאה
337 | const message = "להתראות";
338 | const age = 30;
339 | ```
340 |
341 |
342 |
343 | שתי ההגדרות מסוג
344 | `const`
345 | בדוגמה למעלה יהיו תקינות במצב רגיל, אך בגלל הגדרות מסוג
346 | `var`
347 | ו-
348 | `let`
349 | שקדמו להן, אף אחת לא תעבוד.
350 |
351 | למרות הדמיון הרב יש הבדל מובהק בין הגדרת
352 | `let`
353 | לבין הגדרת
354 | `const`.
355 | כל ניסיון לבצע השמת ערך חדש למשתנה מסוג
356 | `const`
357 | יזרוק שגיאה,
358 | בין אם מדובר במצב ״קפדני״
359 | (strict mode)
360 | או לא:
361 |
362 |
371 |
372 | בדומה למשתנים קבועים בשפות תכנות אחרות, המשתנה
373 | `maxItems`
374 | לא יכול לקבל ערך חדש מאוחר יותר.
375 | יחד עם זאת, בניגוד לשפות אחרות, במידה והערך הינו אוביקט ג׳אווהסקריפט אז הוא ניתן לשינוי.
376 |
377 | #### הגדרת אוביקט באמצעות משתנה קבוע
378 |
379 | הגדרת משתנה מסוג
380 | `const`
381 | מונעת שינוי של הקישור אך לא של הערך עצמו. משמעות הדבר היא שהגדרת משתנה מסוג
382 | `const`
383 | עבור אוביקט לא מונעת שינוי של אותו אוביקט. לדוגמה:
384 |
385 |
402 |
403 | בדוגמה הקודמת, הקישור עבור
404 | `person`
405 | נוצר עם הערך התחלתי של אוביקט בעל תכונה אחת.
406 | ניתן לשנות את הערך עבור
407 | `person.name`
408 | מבלי שתיזרק שגיאה מאחר והדבר משנה רק את התוכן של
409 | `person`
410 | ולא משנה את הערך ש
411 | `person`
412 | קשור אליו.
413 |
414 | כאשר מנסים לתת ערך חדש למשתנה
415 | `person`
416 | (ועל ידי כך לשנות את הקישור אליו),
417 | תיזרק שגיאה.
418 |
419 | קל להתבלבל מהתנהגות זו ולכן יש לזכור את העקרון הבא:
420 | שימוש ב-
421 | `const`
422 | מונע שינוי של הקישור לערך, אך אינו מונע שינוי של הערך עצמו.
423 |
424 | ### האזור המת הזמני - The Temporal Dead Zone
425 |
426 | לא ניתן להשתמש במשתנה שהוגדר באמצעות
427 | `let`
428 | או
429 | `const`
430 | עד לאחר שהוגדר. ניסיון להשתמש במשתנה קודם כן זורק שגיאת ייחוס
431 | (ReferenceError),
432 | השגיאה נזרקת גם אם משתמשים בפעולות בטוחות יחסית כמו פעולת
433 | `typeof`
434 | בדוגמה הבאה:
435 |
436 |
446 |
447 | בדוגמה זו, המשתנה בשם
448 | `value`
449 | מוגדר ומאותחל באמצעות הגדרת
450 | `let`
451 | אך קודם לכן נזרקת שגיאה בגלל שורת הקוד הקודמת. הבעיה היא שהמשתנה
452 | `value`
453 | נמצא בתוך מה שקהילת המפתחים כינתה בשם
454 | *האזור המת הזמני*
455 | (TDZ).
456 |
457 | השם
458 | TDZ
459 | אינו מופיע באופן מפורש באפיון אקמהסקריפט אך המונח משמש כדי לתאר מדוע הגדרות מסוג
460 | `let`
461 | ו-
462 | `const`
463 | אינן נגישות לפני הגדרתן. בהמשך נעבור על מספר תופעות שנגרמות עקב עקרון ה
464 | TDZ
465 | ולמרות שהדוגמאות משתמשות במשתנה מסוג
466 | `let`,
467 | חשוב לדעת שהמידע משמש גם עבור משתנה מסוג
468 | `const`
469 |
470 | כאשר מנוע ריצה של ג׳אווהסקריפט מסתכל בתוך בלוק של קוד ומוצא הגדרת משתנה, אזי הוא מרים את ההגדרה לתחילת הפונקציה או לתחילת הסביבה הגלובלית
471 | (עבור משתנה מסוג
472 | `var`)
473 | או שאותו מנוע ריצה ממקם את ההגדרה בתוך ה-
474 | TDZ
475 | (
476 | עבור משתנים מסוג
477 | `let`
478 | ו
479 | `const`
480 | ).
481 | כל ניסיון לגשת למשתנה בתוך ה
482 | TDZ
483 | יגרום לזריקת שגיאה בזמן ריצה.
484 |
485 | אותו משתנה יוצא מן ה
486 | TDZ
487 | והופך בטוח לשימוש,
488 | רק כאשר הקוד מגיע לשורת הקוד בה מוגדר המשתנה.
489 |
490 | אותה התנהגות מתקיימת בכל ניסיון לגשת למשתנה מסוג
491 | `let` או `const`
492 | טרם הגדרתו. כפי שמדגימה הדוגמה הקודמת, הדבר תקף גם עבור האופרטור
493 | `typeof`
494 | שנחשב לבטוח לשימוש.
495 | יחד עם זאת ניתן להשתמש ב
496 | `typeof`
497 | על משתנה שקיים מחוץ לבלוק בו מוגדר המשתנה, אך לא בטוח שיתקבלו אותן התוצאות :
498 |
499 |
510 |
511 | המשתנה
512 | `value`
513 | אינו נמצא בתוך
514 | TDZ
515 | בזמן שאופרטור
516 | `typeof`
517 | פועל מאחר והאופרציה מתבצעת מבחוץ לבלוק הקוד שבתוכו מוגדר המשתנה
518 | `value`.
519 | משמעות הדבר היא שכלל לא קיים קישור עבור
520 | `value`
521 | ולכן פעולת
522 | `typeof`
523 | תסתיים עם הערך
524 | `"undefined"`.
525 |
526 | ה-
527 | TDZ
528 | מהווה רק היבט ייחודי אחד של קשירות לסביבת בלוק.
529 | היבט ייחודי אחר מתבטא בעת השימוש בלולאות.
530 |
531 | ## קישור משתנים בסביבת בלוק בתוך לולאות
532 |
533 | אחד המקומות הטובים ביותר עבור משתנים ברמת בלוק הוא בתוך לולאות
534 | `for`,
535 | היכן שמשתנה אינדקס נועד לשימוש רק בתוך הלולאה. ראה דוגמה :
536 |
537 |
551 |
552 | בשפות אחרות היכן ששיוך ברמת בלוק קורה כברירת מחדל, הדוגמה האחרונה תעבוד כך שהמשתנה
553 | `i`
554 | נגיש אך ורק בתוך לולאת ה
555 | `for`.
556 | לעומת זאת, בג׳אווהסקריפט המשתנה
557 | `i`
558 | עדיין נגיש לאחר שהלולאה סיימה לרוץ מאחר והגדרת ה
559 | `var`
560 | מורמת.
561 | באמצעות שימוש בהגדרת
562 | `let`
563 | תתקבל התוצאה הרצויה, כפי שמודגם בהמשך:
564 |
565 |
579 |
580 | בדוגמה זו, המשתנה
581 | `i`
582 | קיים אך ורק בתוך לולאת ה
583 | `for`.
584 | ברגע שהלולאה סיימה לרוץ המשתנה אינו נגיש.
585 |
586 | ### פונקציות בתוך לולאות
587 |
588 | מאז ומתמיד משתנים מסוג
589 | `var`
590 | היו בעייתיים מבחינת הגדרת פונקציות בתוך לולאות, מאחר ומשתנים שנוצרו בלולאה עדיין נגישים מחוצה לה.
591 | לדוגמה:
592 |
593 |
594 |
595 | ```js
596 | var funcs = [];
597 |
598 | for (var i = 0; i < 10; i++) {
599 | funcs.push(function() { console.log(i); });
600 | }
601 |
602 | funcs.forEach(function(func) {
603 | func(); // המספר ״10״ יודפס עשר פעמים
604 | });
605 | ```
606 |
607 |
608 |
609 | ייתכן ותצפו שהקוד הנ״ל ידפיס את הספרות 0-9 אך במקום זאת הוא ידפיס את המספר 10 עשר פעמים רצופות.
610 | הסיבה היא שהמשתנה
611 | `i`
612 | קיים באופן משותף עבור כל איטרציה של הלולאה, והמשמעות היא שפונקציות שנוצרו בתוך הלולאה מתייחסות לאותו משתנה.
613 | המשתנה
614 | `i`
615 | מקבל את הערך
616 | `10`
617 | לאחר שהלולאה מסתיימת, וכאשר קוראים ל
618 | `console.log(i)`
619 | זהו הערך שיודפס.
620 |
621 | כדי לטפל בבעיה זו נהוג להשתמש בפונקציה שנקראת באופן מיידי
622 | (IIFEs - immediately-invoked function expressions)
623 | בתוך לולאות על מנת ליצור עותק מקומי לפונקציה.
624 | לדוגמה:
625 |
626 |
645 |
646 | בדוגמה זו משתמשים ב
647 | (IIFE)
648 | בתוך הלולאה.
649 |
650 | המשתנה
651 | `i`
652 | מועבר לתוך ה
653 | IIFE
654 | אשר יוצרת עותק מקומי משלה ושומרת אותו בתור המשתנה בשם
655 | `value`.
656 | זהו הערך שישמש בתוך הפונקציה, עבור אותה איטרציה, וכך הקריאה לכל פונקציה מדפיסה את הערך לו מצפים, ספירה מ
657 | 0
658 | ועד
659 | 9.
660 |
661 | למרבה המזל, שיוך לבלוק באמצעות משתנים מסוג
662 | `let`
663 | ו
664 | `const`
665 | מאפשר לנו לעשות זאת באופן פשוט וקל.
666 |
667 | ### הגדרת let בתוך לולאה
668 |
669 | הגדרת
670 | `let`
671 | מפשטת שימוש בלולאות על ידי מתן אותה תוצאה כמו השימוש ב
672 | IIFE
673 | בדוגמה הקודמת.
674 |
675 | בכל איטרציה, הלולאה יוצרת משתנה חדש ומאתחלת אותו לערך של המשתנה בעל אותו השם מהאיטרציה האחרונה של הלולאה.
676 | בצורה זו אין יותר צורך להשתמש ב
677 | IIFE
678 | על מנת לקבל את התוצאה הרצויה.
679 |
680 | לדוגמה:
681 |
682 |
683 | ```js
684 | var funcs = [];
685 |
686 | for (let i = 0; i < 10; i++) {
687 | funcs.push(function() {
688 | console.log(i);
689 | });
690 | }
691 |
692 | funcs.forEach(function(func) {
693 | func(); // יודפס 0 ואז 1 ואז 2 עד שמגיעים ל 9
694 | })
695 | ```
696 |
697 |
698 |
699 | לולאה כמו בדוגמה האחרונה פועלת באותה צורה כמו הלולאה בדוגמה שלפניה שהשתמשה במשתנה מסוג
700 | `var`
701 | ביחד עם
702 | IIFE,
703 | אך היא קריאה וקצרה יותר.
704 | השימוש במשתנה מסוג
705 | `let`
706 | יוצר משתנה חדש בשם
707 | `i`
708 | בכל איטרציה של הלולאה, ולכן כל פונקציה שמוגדרת בתוך הלולאה מקבלת עותק מקומי של המשתנה
709 | `i`.
710 | לכל עותק של המשתנה
711 | `i`
712 | ניתן הערך שהתקבל בתחילת האיטרציה שבה נוצר.
713 | הדבר נכון גם עבור לולאות מסוג
714 | `for-in` ו `for-of`
715 | כפי שניתן לראות בדוגמה הבאה:
716 |
717 |
745 |
746 | בדוגמה זו לולאה
747 | `for-in`
748 | פועלת באותה צורה כמו לולאת
749 | `for`.
750 | בכל איטרציה של הלולאה נוצר שיוך חדש
751 | (binding)
752 | של המשתנה
753 | `key`
754 | ולכן כל פונקציה מקבלת עותק מקומי משלה עבור המשתנה
755 | `key`.
756 | כתוצאה מכך כל פונקציה מדפיסה ערך שונה.
757 | לו היינו משתמשים במשתנה מסוג
758 | `var`
759 | במקום משתנה מסוג
760 | `let`
761 | אזי כל הפונקציות היו מדפיסות
762 | `"c"`.
763 |
764 | I>
765 | חשוב
766 | לדעת שההתנהגות של משתנים מסוג
767 | `let`
768 | בתוך לולאות היא התנהגות שמאופיינת בשפה עצמה
769 | ואינה בהכרח קשורה למאפייני הגדרת
770 | `let`
771 | כמשתנה שאינו מורם.
772 | למעשה, מימושים מוקדמים פעלו ללא התנהגות זו והיא התווספה רק בשלב מאוחר יותר.
773 |
774 | ### הגדרת משתנים קבועים בתוך לולאות
775 |
776 | המהדורה השישית של אקמהסקריפט אינה אוסרת שימוש בהגדרת
777 | `const`
778 | בתוך לולאות. אך קיימת התנהגות שונה בהתאם לסוג הלולאה.
779 |
780 | עבור לולאת
781 | `for`
782 | רגילה, ניתן להשתמש במשתנה מסוג
783 | `const`
784 | עבור החלק המאתחל של הלולאה, אך הלולאה תזרוק שגיאה במידה ומנסים לשנות את הערך.
785 | לדוגמה:
786 |
787 |
788 |
789 | ```js
790 | var funcs = [];
791 |
792 | // שגיאה לאחר האיטרציה הראשונה
793 | for (const i = 0; i < 10; i++) {
794 | funcs.push(function() {
795 | console.log(i);
796 | });
797 | }
798 | ```
799 |
800 |
801 |
802 | בדוגמת הקוד האחרונה, המשתנה
803 | `i`
804 | מוגדר בתור משתנה קבוע.
805 | האיטרציה הראשונה של הלולאה, היכן שערכו של המשתנה
806 | `i`
807 | אותחל ל 0
808 | תרוץ בהצלחה.
809 | אולם, תיזרק שגיאה כאשר הקוד
810 | `i++`
811 | ירוץ מאחר והקוד הנ״ל מנסה לשנות ערך קבוע.
812 | לכן ניתן להשתמש בהגדרת
813 | `const`
814 |
815 | בניגוד לכך, כאשר משתמשים בו בלולאות מסוג
816 | `for-in` או `for-of`
817 | משתנה מסוג
818 | `const`
819 | מתנהג בדיוק כמו משתנה מסוג
820 | `let`.
821 | לכן דוגמה הקוד הבאה תרוץ בהצלחה
822 |
823 |
852 |
853 | דוגמה הקוד האחרונה מתנהגת כמו הדוגמה השניה בפסקה
854 | "הגדרת let בתוך לולאה".
855 | ההבדל היחיד הוא שהערך של המשתנה
856 | `key`
857 | אינו ניתן לשינוי בתוך הלולאה.
858 | לולאות מסוג
859 | `for-in` או `for-of`
860 | עובדות בצורה כזו בשילוב עם הגדרות
861 | `const`
862 | מאחר ומאתחל הלולאה יוצר שיוך חדש בכל איטרציה ולא מנסה לשנות את הערך של שיוך קיים
863 | (כפי שקרה בדוגמה הקודמת כאשר ניסינו לעשות זאת בתוך לולאה מסוג לולאת `for`).
864 |
865 | ## שיוך לסביבת בלוק גלובלית
866 |
867 | הבדל נוסף בין משתנים מסוג
868 | `let`
869 | ו
870 | `const`
871 | לבין משתנים מסוג
872 | `var`
873 | הוא התנהגותם בסביבה הגלובלית.
874 | כאשר משתנים מסוג
875 | `var`
876 | מוגדרים בסביבה הגלובלית, הדבר יוצר משתנה גלובלי חדש,
877 | שהופך לשם תכונה של האובייקט הגלובלי
878 | (`window` בדפדפנים).
879 | בצורה כזו ניתן בטעות לדרוס ערך גלובלי קיים,
880 | לדוגמה:
881 |
882 |
894 |
895 | למרות שהמשתנה הגלובלי
896 | `RegExp`
897 | מוגדר כחלק של
898 | `window`
899 | אין לו חסינות מפני דריסה על ידי משתנה מסוג
900 | `var`.
901 | הדוגמה הנ״ל מגדירה משתנה גלובלי חדש בשם
902 | `RegExp`
903 | אשר דורסת את המשתנה המקורי.
904 | בצורה דומה,
905 | המשתנה
906 | `ncz`
907 | מוגדר כמשתנה בסביבה הגלובלית ומייד מגדיר מפתח חדש באוביקט
908 | `window`.
909 | זוהי הדרך שבה ג׳אווהסקריפט עובדת מאז תחילתה.
910 |
911 | אילו היינו משתמשים במשתנים מסוג
912 | `let`
913 | או
914 | `const`
915 | בסביבה הגלובלית,
916 | היינו יוצרים שיוך חדש בסביבה הגלובלית אך לא הייתה מתווספת תכונה חדשה לאוביקט הגלובלי.
917 | משמעות הדבר היא שלא ניתן לדרוס משתנה גלובלי על ידי הגדרת
918 | `let` או `const`,
919 | ניתן רק להסתיר אותו.
920 |
921 | לדוגמה:
922 |
923 |
937 |
938 | בדוגמה זו,
939 | משתנה חדש מסוג
940 | `let`
941 | מוגדר תחת השם
942 | `RegExp`
943 | ובתורו מסתיר את משתנה
944 | `RegExp`
945 | שכבר קיים בסביבה הגלובלית.
946 |
947 | משמעות הדבר הינה שהערכים עבור
948 | `window.RegExp`
949 | ו
950 | `RegExp`
951 | שונים זה מזה, כך שאין דריסה של משתנים בסביבה הגלובלית.
952 | כמו כן, הגדרת
953 | `const`
954 | עבור המשתנה בשם
955 | `ncz`
956 | יוצרת שיוך עבור משתנה בסביבה הגלובלית אך אינה יוצרת מפתח חדש באוביקט הגלובלי.
957 | תכונה זו של משתנים מסוג
958 | `let`
959 | ו
960 | `const`
961 | הופכת אותם לבטוחים יותר לשימוש בסביבה הגלובלית כאשר אין זה רצוי ליצור מפתחות חדשים באוביקט הגלובלי.
962 |
963 | I> ניתן עדיין להשתמש בהגדרה מסוג
964 | `var`
965 | בסביבה הגלובלית עבור קוד שרוצים לאפשר את הגישה אליו מתוך האוביקט הגלובלי.
966 | זהו דבר נפוץ בסביבת הדפדפן כאשר ברצונך לשתף קוד בין אלמנטים מסוג
967 | iframe
968 | או חלונות נפרדים.
969 |
970 | ## דרכים מומלצות לשימוש בשיוך משתנים בסביבת בלוק
971 |
972 | בעת שהמהדורה השישית הייתה בפיתוח, היו רבים שהאמינו שיש להשתמש בהגדרת
973 | `let`
974 | בתור הגדרת ברירת מחדל במקום להשתמש בהגדרות מסוג
975 | `var`.
976 | עבור מפתחי ג׳אווהסקריפט רבים,
977 | הגדרות
978 | `let`
979 | פועלות כמו שהם האמינו שהגדרות מסוג
980 | `var`
981 | היו צריכות לפעול מלכתחילה,
982 | לכן החלפה ישירה בין הסוגים היא הגיונית.
983 | במקרים שכאלה, השימוש בהגדרות
984 | `const`
985 | ייעשה עבור משתנים שצריכים להיות מוגנים בפני שינוי.
986 |
987 | ואולם, עם הזמן התפתחה גישה חלופית לפיה
988 | יש להשתמש בהגדרות מסוג
989 | `const`
990 | בתור ברירת מחדל ויש להשתמש בהגדרות מסוג
991 | `let`
992 | כאשר ידוע מראש כי ערך המשתנה עתיד להשתנות.
993 | הרציונל לכך הוא שרוב המשתנים לא משנים את ערכם לאחר האתחול ואילו שינויי ערך שאינם צפויים הם מקור לבאגים.
994 |
995 | ## סיכום
996 |
997 | הגדרות משתנים עבור סביבת בלוק מסוג
998 | `let`
999 | ו
1000 | `const`
1001 | מביאות איתן מה שנקרא שיוך לקסיקאלי לג׳אווהסקריפט.
1002 | הגדרות אלו אינן מורמות וקיימות רק עבור אותו הבלוק בקוד בו הוגדרו.
1003 | דבר זה מייצר התנהגות שדומה יותר לשפות קיימות אחרות, ופחות נוטה ליצירת שגיאות לא מכוונות, מאחר ומשתנים יכולים כעת להיות מוגדרים בדיוק היכן שמשתמשים בהם.
1004 | כתופעת לוואי לכך, לא ניתן לגשת למשתנים לפני שהם מוגדרים בפועל, אפילו לא באמצעות אופרטורים שנחשבים בטוחים לשימוש כגון אופרטור
1005 | `typeof`.
1006 | ניסיון לגשת למשתנה עבור סביבת בלוק לפני הגדרתו זורק שגיאה עקב שיוך המשתנה במה שנקרה
1007 | האזור המת הזמני
1008 | (TDZ).
1009 |
1010 | במקרים רבים,
1011 | משתנים מסוג
1012 | `let`
1013 | ו
1014 | `const`
1015 | מתנהגים באופן דומה למשתנים מסוג
1016 | `var`.
1017 | הדבר אינו נכון עבור לולאות.
1018 | השימוש במשתנים מסוג
1019 | `let`
1020 | ו
1021 | `const`
1022 | בתוך לולאות
1023 | `for-in`
1024 | ו
1025 | `for-of`
1026 | יוצר שיוך חדש עבור כל איטרציה של הלולאה.
1027 | משמעות הדבר היא שפונקציות אשר מוגדרות בתוך הלולאה יכולות לגשת למשתנים ולערכם כפי שהיה בעת האיטרציה הנוכחית,
1028 | ולא כפי שהם לאחר שהלולאה סיימה לעבוד
1029 | (
1030 | ההתנהגות הקיימת עבור משתנים מסוג
1031 | `var`
1032 | ).
1033 | הדבר נכון גם עבור הגדרת משתנים מסוג
1034 | `let`
1035 | בתוך לולאות מסוג לולאות,
1036 | `for`
1037 | בעוד שניסיון להשתמש בהגדרת
1038 | `const`
1039 | בתוך לולאת
1040 | `for`
1041 | עלול לזרוק שגיאה.
1042 |
1043 | הדרך המומלצת לשימוש במשתנים עבור סביבת בלוק הינה
1044 | להשתמש בהגדרת
1045 | `const`
1046 | כברירת מחדל ולהעדיף שימוש בהגדרת
1047 | `let`
1048 | כאשר ידוע לך שהמשתנה עתיד לקבל ערך שונה.
1049 | דרך זו מבטיחה רמה בסיסית של
1050 | קוד מוגן כנגד שינויים בלתי צפויים
1051 | שיכול למנוע שגיאות מסויימות.
1052 |
1053 |
--------------------------------------------------------------------------------
/docs/05-Destructuring.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # פעולת פירוק לשם גישה קלה יותר לנתונים
4 |
5 | כתיבה מקוצרת של אוביקטים ומערכים מאוד נפוצה בג׳אווהסקריפט, והודות לפורמט הנתונים מסוג
6 | JSON,
7 | הפכה לחלק חשוב במיוחד של השפה. כיום אין זה נדיר להגדיר אוביקטים ומערכים ואז למשוך חתיכות קטנות מתוך אותם מבני נתונים.
8 | ECMAScript 6
9 | מפשט פעולה זו ע״י הוספת יכולת
10 | *פירוק*
11 | (*destructuring*),
12 | שהיא פעולת פירוק מבנה נתונים לחלקים קטנים יותר. פרק זה מראה כיצד לפרק אוביקטים ומערכים.
13 |
14 | ## כיצד מועילה לנו פעולת הפירוק?
15 |
16 | בגרסת
17 | ECMAScript 5
18 | וקודם לכן,
19 | הצורך לחלץ נתונים מתוך אוביקטים ומערכים יכול היה להוביל לכתיבה מרובה של קוד כמעט זהה לחלוטין לקודמו, אך ורק בשביל להעביר נתונים מסוימים אל תוך משתנים מקומיים.
20 | לדוגמה:
21 |
22 |
23 |
24 | ```js
25 | let options = {
26 | repeat: true,
27 | save: false
28 | };
29 |
30 | // חילוץ נתונים מתוך האוביקט
31 | let repeat = options.repeat,
32 | save = options.save;
33 | ```
34 |
35 |
36 |
37 | הקוד לעיל מחלץ את הערכים
38 | `repeat`
39 | ו
40 | `save`
41 | מתוך האוביקט בשם
42 | `options`
43 | ושומר אותם במשתנים מקומיים עם שם זהה. בעוד שהקוד נדמה פשוט, ניתן לשער מה קורה בעת ריבוי משתנים.
44 | במקרה כזה יש צורך לבצע השמה עבור כל אחד ואחד מהם בנפרד, אחד אחרי השני.
45 | ובמידה והמידע נמצא עמוק בתוך מבנה נתונים פנימי
46 | (nested data structure)
47 | יש צורך לסרוק את כל מבנה הנתונים על מנת למצוא נתון אחד.
48 |
49 | מסיבה זו הוסיפו את היכולת לפרק אוביקטים ומערכים בגרסת
50 | ECMAScript 6.
51 | כאשר מפרקים מבנה נתונים לחלקים קטנים יותר, הדבר מקל על השגת המבוקש. שפות שונות משתמשות בשיטות פירוק עם תחביר מינימליסטי כדי לפשט את התהליך.
52 | המימוש של
53 | ECMAScript 6
54 | משתמש בתחביר מוכר:
55 | זה של כתיבת אוביקטים ומערכים.
56 |
57 | ## פירוק אוביקטים
58 |
59 | התחביר עבור פירוק מערכים משתמש באוביקט ליטראל בצד השמאלי של פעולת ההשמה.
60 | לדוגמה:
61 |
62 |
63 |
64 | ```js
65 | let node = {
66 | type: "Identifier",
67 | name: "foo"
68 | };
69 |
70 | let { type, name } = node;
71 |
72 | console.log(type); // "Identifier"
73 | console.log(name); // "foo"
74 | ```
75 |
76 |
77 |
78 | בקוד לעיל, ערכו של
79 | `node.type`
80 | שמור במשתנה
81 | `type`
82 | וערכו של
83 | `node.name`
84 | שמור במשתנה
85 | `name`.
86 | צורת הכתיבה זהה לצורה המקוצרת לאתחול תכונה שהופיעה בפרק 4.
87 | המזהים
88 | `type`
89 | ו-
90 | `name`
91 | שניהם נחשבים להגדרות משתנים מקומיים,
92 | כמו גם שמות התכונות שייקראו מתוך אוביקט
93 | `node`.
94 |
95 | #### אל תשכחו את אתחול הערך
96 |
97 | כאשר משתמשים בפעולת פירוק על מנת להגדיר משתנים מסוג
98 | `var`, `let`,
99 | או
100 | `const`,
101 | חובה לספק מה שנקרא מאתחל הערך
102 | (initializer),
103 | זהו הערך שבא אחרי סימן ההשוואה
104 | (`=`).
105 | השורות הבאות בקוד יגרמו כולן לזריקת שגיאות תחביר בגלל היעדר מאתחל ערך:
106 |
107 |
108 |
109 |
110 | ```js
111 | // שגיאה תחבירית
112 | var { type, name };
113 |
114 | // שגיאה תחבירית
115 | let { type, name };
116 |
117 | // שגיאה תחבירית
118 | const { type, name };
119 | ```
120 |
121 |
122 | בעוד שמשתנה מסוג
123 | `const`
124 | תמיד דורש מאתחל לערך, אפילו בעת הגדרת משתנה רגילה, ולא רק בפירוק, משתנים מסוג
125 | `var`
126 | ו-
127 | `let`
128 | דורשים אתחול רק בעת פעולת פירוק.
129 |
130 | #### השמה בפירוק
131 |
132 | הדוגמאות שהוצגו עד עתה הגדירו משתנים.
133 | אך ניתן לבצע השמה באמצעות פעולת פירוק. כך למשל, ניתן לשנות ערכי משתנים לאחר הגדרתם, כמו בדוגמה הבאה:
134 |
135 |
136 |
137 | ```js
138 | let node = {
139 | type: "Identifier",
140 | name: "foo"
141 | };
142 | let type = "Literal";
143 | let name = 5;
144 |
145 | // השמת ערכים באמצעות פירוק אוביקט
146 | ({ type, name } = node);
147 |
148 | console.log(type); // "Identifier"
149 | console.log(name); // "foo"
150 | ```
151 |
152 |
153 |
154 | בדוגמה לעיל, המשתנים
155 | `type`
156 | ו-
157 | `name`
158 | מאותחלים עם ערכים בעת הגדרתם ולאחר מכן שני משתנים בעלי אותו שם מאותחלים עם ערכים שונים.
159 | השורה הבאה משתמשת בהשמה דרך פעולת פירוק
160 | (destructuring assignment)
161 | כדי לשנות את אותם ערכים על ידי קריאת האוביקט
162 | `node`.
163 | שים לב לסוגריים שחובה לשים מסביב לפעולת הפירוק.
164 | הסיבה לכך היא שמבחינת התחביר, סוגרים מסולסלים פותחים
165 | (`{`)
166 | נחשבים לתחילת בלוק של קוד
167 | (block statement).
168 | אך בלוק של קוד אינו יכול להופיע בצידה השמאלי של פעולת השמה.
169 | הסוגריים מסמנים לנו שהסוגריים המסולסלים אינם תחילת בלוק של קוד ויש להתייחס אליהם בתור ביטוי
170 | (expression),
171 | וכך ניתן לבצע את פעולת ההשמה.
172 |
173 | פעולת הפירוק מחשבת ומקבלת את ערכו של צדו הימני של הביטוי
174 | (
175 | החלק שלאחר סימן
176 | `=`
177 | ).
178 | המשמעות היא שניתן להשתמש בפעולת השמה בעזרת פירוק בכל מקום שבו מצפים לקבל ערך. כך למשל ניתן להעביר ערך אל תוך פונקציה:
179 |
180 |
181 |
182 | ```js
183 | let node = {
184 | type: "Identifier",
185 | name: "foo"
186 | },
187 | type = "Literal",
188 | name = 5;
189 |
190 | function outputInfo(value) {
191 | console.log(value === node);
192 | }
193 |
194 | outputInfo({ type, name } = node); // true
195 |
196 | console.log(type); // "Identifier"
197 | console.log(name); // "foo"
198 | ```
199 |
200 |
201 |
202 | הפונקציה
203 | `outputInfo()`
204 | נקראת עם ביטוי השמה על ידי פירוק
205 | (destructuring assignment expression).
206 | הביטוי מקבל את הערך עבור המשתנה
207 | `node`
208 | מכיוון וזה ערך הצד הימני של הביטוי.
209 | ההשמה עבור המשתנים
210 | `type`
211 | ו-
212 | `name`
213 | מתבצעת כרגיל והמשתנה
214 | `node`
215 | מועבר כארגומנט לפונקציה
216 | `outputInfo()`.
217 |
218 | ?> תיזרק שגיאה במידה וצידו הימני של ביטוי השמה על ידי פירוק
219 | (
220 | החלק שלאחר סימן
221 | `=`)
222 | מקבל את הערכים
223 | `null`
224 | או
225 | `undefined`.
226 | זה קורה מכיוון שכל ניסיון לקרוא תכונה בערך מסוג
227 | `null`
228 | או
229 | `undefined`
230 | זורק שגיאת ריצה
231 | (runtime error).
232 |
233 | #### ערכים דיפולטיביים
234 |
235 | כאשר משתמשים בפקודת השמה על ידי פירוק
236 | (destructuring assignment statement),
237 | אם מציינים משתנה עבור תכונה שאינה קיימת על האוביקט, אזי אותו משתנה מקבל את הערך
238 | `undefined`.
239 | לדוגמה:
240 |
241 |
242 |
243 | ```js
244 | let node = {
245 | type: "Identifier",
246 | name: "foo"
247 | };
248 |
249 | let { type, name, value } = node;
250 |
251 | console.log(type); // "Identifier"
252 | console.log(name); // "foo"
253 | console.log(value); // undefined
254 | ```
255 |
256 |
257 | הקוד בדוגמה האחרונה מגדיר משתנה בשם
258 | `value`
259 | ומנסה לבצע השמת ערך אליו. ואולם, אין בנמצא תכונה על האוביקט
260 | `node`
261 | באותו שם, ולכן המשתנה מקבל את הערך
262 | `undefined`
263 | כמצופה.
264 |
265 | ניתן להגדיר ערך התחלתי דיפולטיבי עבור תכונה ספציפית שאינה מוגדרת. כדי לעשות זאת יש להשתמש בסימן ההשוואה
266 | (`=`)
267 | לאחר שם התכונה ואז להגדיר את הערך ההתחלתי. כמו בדוגמה הבאה:
268 |
269 |
270 |
271 | ```js
272 | let node = {
273 | type: "Identifier",
274 | name: "foo"
275 | };
276 |
277 | let { type, name, value = true } = node;
278 |
279 | console.log(type); // "Identifier"
280 | console.log(name); // "foo"
281 | console.log(value); // true
282 | ```
283 |
284 |
285 |
286 | בדוגמה זו, המשתנה
287 | `value`
288 | מקבל את הערך
289 | `true`
290 | בתור ערך דיפולטיבי. הערך הדיפולטיבי יתקבל רק במידה והתכונה אינה קיימת על האוביקט
291 | `node`
292 | או בעלת ערך
293 | `undefined`.
294 | מאחר ואין תכונה מסוג
295 | `node.value`
296 | המשתנה
297 | `value`
298 | משתמש בערך הדיפולטיבי. זה עובד בצורה דומה לערכי פרמטר דיפולטיביים עבור פונקציות כפי שהוסבר בפרק 3.
299 |
300 | #### השמה למשתנים בעלי שמות שונים
301 |
302 | עד עתה, כל דוגמה השתמשה בשמות התכונות על האוביקט בתור שמות המשתנים. כך למשך, הערך עבור
303 | `node.type`
304 | נשמר במשתנה בשם
305 | `type`.
306 | זה טוב כל עוד נרצה להשתמש באותו שם, אבל מה אם לא?
307 | לגרסת
308 | אקמהסקריפט 6
309 | יש צורת תחביר שמאפשר השמת ערכים למשתנים בעלי שם שונה, והתחביר דומה במראהו לזה של אתחול תכונה על אוביקט. לדוגמה:
310 |
311 |
312 |
313 | ```js
314 | let node = {
315 | type: "Identifier",
316 | name: "foo"
317 | };
318 |
319 | let { type: localType, name: localName } = node;
320 |
321 | console.log(localType); // "Identifier"
322 | console.log(localName); // "foo"
323 | ```
324 |
325 |
326 | הקוד בדוגמה משתמש בהשמה ע״י פירוק על מנת להגדיר את המשתנים
327 | `localType`
328 | ו-
329 | `localName`,
330 | שמכילים את ערכי התכונות
331 | `node.type`
332 | ו-
333 | `node.name`,
334 | בהתאמה.
335 | התחביר
336 | `type: localType`
337 | מורה על קריאת התכונה בשם
338 | `node.type`
339 | ולשמור את ערכה במשתנה
340 | `localType`.
341 | התחביר הנ״ל הינו למעשה ההיפך מהתחביר הרגיל לכתיבת אוביקט ליטראל, שבו שם התכונה נמצא משמאל לסימן הנקודותיים והערך מצידו הימני. במקרה שלנו, שם המשתנה מופיע מימין לסימן הנקודותיים ומיקום הערך על האוביקט נמצא מצידו השמאלי.
342 |
343 | באפשרותכם להוסיף ערכים דיפולטיביים כאשר משתמשים בשמות שונים.
344 | סימן ההשוואה והערך הדיפולטיבי עדיין ממוקמים לאחר שם המשתנה. לדוגמה:
345 |
346 |
347 |
348 | ```js
349 | let node = {
350 | type: "Identifier"
351 | };
352 |
353 | let { type: localType, name: localName = "bar" } = node;
354 |
355 | console.log(localType); // "Identifier"
356 | console.log(localName); // "bar"
357 | ```
358 |
359 |
360 | בדוגמה זו, למשתנה
361 | `localName`
362 | יש את הערך הדיפולטיבי
363 | `"bar"`.
364 | המשתנה יקבל את הערך הדיפולטיבי מכיוון והתכונה
365 | `node.name`
366 | אינה קיימת.
367 |
368 | בינתיים, ראינו כיצד לבצע פירוק לאוביקט שתכונותיו מקבלות ערכים פשוטים.
369 | פירוק אוביקטים יכול לשמש אותנו כדי לקבל ערכים בתוך אוביקט פנימי
370 | (nested object structures).
371 |
372 | #### פירוק עבור אוביקטים פנימיים
373 |
374 | בעזרת כתיבה הדומה לזו של אוביקט ליטראל, ניתן לנווט בתוך אוביקט פנימי על מנת לקבל את הנתונים הרצויים. לדוגמה:
375 |
376 |
377 |
378 | ```js
379 | let node = {
380 | type: "Identifier",
381 | name: "foo",
382 | loc: {
383 | start: {
384 | line: 1,
385 | column: 1
386 | },
387 | end: {
388 | line: 1,
389 | column: 4
390 | }
391 | }
392 | };
393 |
394 | let { loc: { start }} = node;
395 |
396 | console.log(start.line); // 1
397 | console.log(start.column); // 1
398 | ```
399 |
400 |
401 | טכניקת הפירוק בקוד בדוגמה לעיל משתמש בסוגריים מסולסלים כדי להורות לפעולת הפירוק לנווט לתכונה בשם
402 | `loc`
403 | שבאוביקט
404 | `node`
405 | ולתור אחר התכונה
406 | `start`.
407 | היכן שקיים סימן נקודותיים באופרציית פירוק, המשמעות שהמזהה לפני הסימן משמש לקביעת המיקום לבדיקה והצד הימני משמש לקביעת הערך. כאשר רואים סוגריים מסולסלים לאחר סימן נקודותיים המשמעות היא שהיעד הסופי הוא פנימי בתוך האוביקט.
408 |
409 | ניתן אף לתת שם אחר למשתנה:
410 |
411 |
412 |
413 | ```js
414 | let node = {
415 | type: "Identifier",
416 | name: "foo",
417 | loc: {
418 | start: {
419 | line: 1,
420 | column: 1
421 | },
422 | end: {
423 | line: 1,
424 | column: 4
425 | }
426 | }
427 | };
428 |
429 | // מציאת הערך עבור
430 | // node.loc.start
431 | let { loc: { start: localStart }} = node;
432 |
433 | console.log(localStart.line); // 1
434 | console.log(localStart.column); // 1
435 | ```
436 |
437 |
438 | בדוגמה לעיל, הערך בתוך
439 | `node.loc.start`
440 | שמור במשתנה בשם
441 | `localStart`.
442 | פירוק יכול להתבצע בכל רמת עומק, ובכל רמה יש לנו גישה מלאה ליכולות המלאות של פעולת הפירוק.
443 |
444 | פעולת פירוק אוביקטים היא פעולה עוצמתית עם אפשרויות רבות, אך פירוק של מערכים מוסיף מספר יכולות מיוחדות שמאפשרות לחלץ נתונים מתוך מערכים.
445 |
446 | #### בעיות תחביר
447 |
448 | יש להיזהר ממצב שבו יוצרים פקודה חסרת משמעות בעת פירוק עמוק. סוגריים מסולסלים ריקים נחשבים לתחביר תקין בעת פירוק אוביקט, אך הינם חסרי משמעות. לדוגמה:
449 |
450 |
451 |
452 | ```js
453 | // אין הגדרת משתנה!
454 | let { loc: {} } = node;
455 | ```
456 |
457 | בקוד למעלה לא מתבצע קישור בין תכונה למשתנה. בגלל הסוגריים המסולסלים בצד ימין, השם
458 | `loc`
459 | משמש לניווט ולא בתור משתנה. במקרה כזה סביר שהכוונה הייתה להשתמש בסימן
460 | `=`
461 | כדי להגדיר ערך דיפולטיבי ולא להשתמש בסימן
462 | `:`
463 | שמשמש לקביעת מיקום. ייתכן שתחביר כזה יהיה בלתי תקין בעתיד, אך כרגע יש להיזהר ממנו.
464 |
465 | ## פירוק מערכים
466 |
467 | התחביר עבור פירוק מערכים דומה לזה של פירוק אוביקטים. הוא פשוט משתמש בתחביר של כתיבת מערך במקום זה של כתיבת אוביקט ליטראל. פעולת הפירוק עובדת על מיקומים בתוך המערך במקום לעבוד על תכונות בתוך האוביקט.
468 | לדוגמה:
469 |
470 |
471 |
472 | ```js
473 | let colors = [ "red", "green", "blue" ];
474 |
475 | let [ firstColor, secondColor ] = colors;
476 |
477 | console.log(firstColor); // "red"
478 | console.log(secondColor); // "green"
479 | ```
480 |
481 |
482 | בדוגמה לעיל, פירוק על המערך מושך את הערכים
483 | `"red"`
484 | ו-
485 | `"green"`
486 | מתוך המערך בשם
487 | `colors`
488 | ושומר אותם במשתנים
489 | `firstColor`
490 | ו-
491 | `secondColor`.
492 | הערכים הללו נבחרו בשל מיקומם בתוך המערך. שמות המשתנים היו יכולים להיות כל שם. כל פריט במערך שאין אליו התייחסות - מתעלמים ממנו. המערך עצמו אינו מושפע מפעולת הפירוק.
493 |
494 | ניתן להתעלם מפריטים קיימים בעת הפירוק ולתת שמות רק עבור פריטים שאנו מעוניינים בהם. אם למשל היינו רוצים למצוא רק את הערך במיקום השלישי במערך, אין צורך לספק שמות עבור הפריט הראשון והשני.
495 | לדוגמה:
496 |
497 |
498 |
499 | ```js
500 | let colors = [ "red", "green", "blue" ];
501 |
502 | let [ , , thirdColor ] = colors;
503 |
504 | console.log(thirdColor); // "blue"
505 | ```
506 |
507 |
508 |
509 | הקוד בדוגמה לעיל משתמש בפעולת פירוק כדי לקבל את הפריט השלישי במערך
510 | `colors`.
511 | סימוני הפסיק לפני השם
512 | `thirdColor`
513 | משמשים בתור סמנים לפריטים במערך שבאים לפניו.
514 |
515 | ?> בדומה לפירוק אוביקטים, חובה להשתמש במאתחל ערך כאשר מפרקים מערך, על ידי שימוש במזהה מסוג
516 | `var`, `let`,
517 | או
518 | `const`.
519 |
520 | #### השמה על ידי פירוק
521 |
522 | ניתן להשתמש בפירוק מערכים בכדי לבצע השמה, אך בניגוד לפירוק אוביקט אין צורך לעטוף את הביטוי בסוגריים. לדוגמה:
523 |
524 |
525 |
526 | ```js
527 | let colors = [ "red", "green", "blue" ],
528 | firstColor = "black",
529 | secondColor = "purple";
530 |
531 | [ firstColor, secondColor ] = colors;
532 |
533 | console.log(firstColor); // "red"
534 | console.log(secondColor); // "green"
535 | ```
536 |
537 |
538 |
539 | ההשמה על ידי פירוק בקוד לעיל מתבצעת באופן דומה לדוגמה הקודמת שעסקה בפירוק מערך. ההבדל היחידי הינו שהמשתנים
540 | `firstColor`
541 | ו-
542 | `secondColor`
543 | כבר הוגדרו מבעוד מועד. במרבית המקרים אין צורך לדעת יותר מעבר לכך בעת פירוק מערכים. אך במקצת המקרים יש דבר נוסף שיועיל לנו.
544 |
545 | פירוק מערכים מאפשר במצב מסוים להחליף את ערכם של שני משתנים. החלפת ערכים היא פעולה נפוצה בפעולות מיון, ושיטת החלפת הערכים בגרסת
546 | ECMAScript 5
547 | מערבת משתנה שלישי זמני כמו בדוגמה הבאה:
548 |
549 |
550 |
551 | ```js
552 | // החלפת מערכים בגרסת אקמהסקריפט 5
553 | let a = 1,
554 | b = 2,
555 | tmp;
556 |
557 | tmp = a;
558 | a = b;
559 | b = tmp;
560 |
561 | console.log(a); // 2
562 | console.log(b); // 1
563 | ```
564 |
565 |
566 |
567 | המשתנה הזמני
568 | `tmp`
569 | נחוץ על מנת להחליף את ערכי
570 | `a`
571 | ו-
572 | `b`.
573 | באמצעות פירוק מערכים , אין צורך במשתנה הנוסף. בדוגמה הבאה נראה כיצד ניתן להחליף ערכים בגרסת
574 | ECMAScript 6:
575 |
576 |
577 |
578 | ```js
579 | // החלפת ערכים בגרסת אקמהסקריפט 6
580 | let a = 1,
581 | b = 2;
582 |
583 | [ a, b ] = [ b, a ];
584 |
585 | console.log(a); // 2
586 | console.log(b); // 1
587 | ```
588 |
589 |
590 |
591 | פעולת ההשמה על ידי פירוק בדוגמה לעיל נראית כמו השתקפות בראי. הצד השמאלי של ההשמה
592 | (לפני סימן ההשוואה)
593 | מראה שימוש בפירוק בדומה לדוגמאות הקודמות עבור פירוק מערכים.
594 | הצד הימני הינו מערך שנוצר באופן זמני לצורך פעולת ההחלפה.
595 | הפירוק מתרחש על המערך הזמני, שמכיל בתוכו את הערכים
596 | `a`
597 | ו-
598 | `b`
599 | אשר מועתקים למיקום הראשון והשני במערך, בהתאמה.
600 | התוצאה היא שערכי המשתנים הוחלפו זה בזה.
601 |
602 | ?> בדומה לפעולת השמה על ידי פירוק באוביקט, גם כאן תיזרק שגיאה במידה וצידה הימני של פעולת ההשמה מכיל את הערכים
603 | `null`
604 | או
605 | `undefined`.
606 |
607 | #### ערכים דיפולטיביים
608 |
609 | ניתן להגדיר ערך דיפולטיבי עבור כל מיקום ספציפי במערך. הערך הדיפולטיבי ישמש כאשר הפריט במיקום הנתון אינו קיים או בעל הערך
610 | `undefined`.
611 | לדוגמה:
612 |
613 |
614 |
615 | ```js
616 | let colors = [ "red" ];
617 |
618 | let [ firstColor, secondColor = "green" ] = colors;
619 |
620 | console.log(firstColor); // "red"
621 | console.log(secondColor); // "green"
622 | ```
623 |
624 |
625 |
626 | בקוד לעיל למערך
627 | `colors`
628 | פריט אחד בלבד, ולכן אין ערך תואם במערך עבור
629 | `secondColor`.
630 | מכיוון ונתון ערך דיפולטיבי, המשתנה
631 | `secondColor`
632 | מקבל את הערך
633 | `"green"`
634 | במקום את הערך
635 | `undefined`.
636 |
637 | #### פירוק מערכים פנימיים
638 |
639 | ניתן לבצע פירוק עמוק במערכים פנימיים בצורה דומה לזו של פירוק אוביקטים פנימיים. על ידי שימוש בתחביר פירוק פנימי בתוך התבנית העוטפת, פעולת הפירוק תמשיך אל תוך המערך הפנימי. לדוגמה:
640 |
641 |
642 |
643 | ```js
644 | let colors = [ "red", [ "green", "lightgreen" ], "blue" ];
645 |
646 | // לאחר מכן
647 |
648 | let [ firstColor, [ secondColor ] ] = colors;
649 |
650 | console.log(firstColor); // "red"
651 | console.log(secondColor); // "green"
652 | ```
653 |
654 |
655 |
656 | בדוגמה זו, המשתנה בשם
657 | `secondColor`
658 | מתייחס לערך
659 | `"green"`
660 | בתוך מערך
661 | `colors`
662 | הפריט הנ״ל מופיע בתוך מערך נוסף, ומכאן הצורך בסוגריים המרובעים מסביב למשתנה
663 | `secondColor`.
664 | בדוגמה לאוביקטים ניתן לבצע פירוק פנימי בכל רמת עומק.
665 |
666 | #### פריטים מסוג רסט
667 |
668 | פרק 3 הציג פרמטרים מסוג רסט
669 | (rest parameters)
670 | עבור פונקציות.
671 | עבור פירוק מערכים קיימת התייחסות למושג
672 | *פריטים מסוג רסט*
673 | (*rest items*).
674 | פריטים מסוג רסט משתמשים בתחביר בצורת
675 | `...`
676 | כדי לבצע השמת ערכים של יתר הפריטים במערך למשתנה מסוים.
677 | לדוגמה:
678 |
679 |
680 |
681 | ```js
682 | let colors = [ "red", "green", "blue" ];
683 |
684 | let [ firstColor, ...restColors ] = colors;
685 |
686 | console.log(firstColor); // "red"
687 | console.log(restColors.length); // 2
688 | console.log(restColors[0]); // "green"
689 | console.log(restColors[1]); // "blue"
690 | ```
691 |
692 |
693 |
694 | ערך הפריט הראשון במערך
695 | `colors`
696 | עובר למשתנה
697 | `firstColor`,
698 | והיתר מועברים
699 | לתוך מערך חדש בשם
700 | `restColors`.
701 | המערך
702 | `restColors`
703 | יכיל 2 פריטים:
704 | `"green"`
705 | ו-
706 | `"blue"`.
707 | פריטים מסוג רסט שימושיים עבור השגת פריטים מסוימים מתוך מערך ושמירת היתר זמינים לשימוש, אך קיים שימוש נוסף.
708 |
709 | בעיה קיימת עבור מערכים בג׳אווהסקריפט הינה היעדר היכולת לשכפל מערך בקלות. בגרסת
710 | ECMAScript 5,
711 | מפתחים היו נוהגים להשתמש במתודה
712 | `concat()`
713 | על מנת לשכפל מערך. לדוגמה:
714 |
715 |
716 |
717 | ```js
718 | // שכפול מערך באקמהסקריפט 5
719 | var colors = [ "red", "green", "blue" ];
720 | var clonedColors = colors.concat();
721 |
722 | console.log(clonedColors); // "[red,green,blue]"
723 | ```
724 |
725 |
726 |
727 | בעוד שהמתודה
728 | `concat()`
729 | נועדה לחבר שני מערכים, במידה וקוראים לה ללא ארגומנט היא תחזיר עותק של המערך. בגרסת
730 | ECMAScript 6,
731 | ניתן להשתמש בפריטים מסוג רסט על מנת להשיג אותה תוצאה ובעזרת תחביר שנועד לכך. זה עובד כך:
732 |
733 |
734 |
735 | ```js
736 | // שכפול מערך באקמהסקריפט 6
737 | let colors = [ "red", "green", "blue" ];
738 | let [ ...clonedColors ] = colors;
739 |
740 | console.log(clonedColors); // "[red,green,blue]"
741 | ```
742 |
743 |
744 |
745 | בדוגמה זו, פריטים מסוג רסט משמשים אותנו להעתקת ערכים מן המערך
746 | `colors`
747 | לתוך המערך
748 | `clonedColors`.
749 | בעוד שמדובר בעניין של השקפה לגבי השאלה האם טכניקה זו ברורה יותר בעניין כוונת המפתח שכתב את הקוד מאשר שימוש במתודה
750 | `concat()`
751 | זוהי יכולת שימושית שכדאי להכיר.
752 |
753 | ?> פריטים מסוג רסט חייבים להיות הפריט האחרון בפעולת הפירוק ואסור שיבוא פסיק לאחר מכן. שימוש בפסיק לאחר הגדרת פריטים מסוג רסט נחשב לשגיאה תחבירית
754 |
755 | ## פירוק מעורב
756 |
757 | ניתן להשתמש בו זמנית בפירוק אוביקטים ובפירוק מערכים ביחד כדי ליצור ביטויים מורכבים. על ידי כך ניתן לחלץ מתוך מבנה נתונים מסויים אך ורק את הנתונים החשובים לנו מתוך עירוב של אוביקטים ומערכים.
758 | לדוגמה:
759 |
760 |
761 |
762 | ```js
763 | let node = {
764 | type: "Identifier",
765 | name: "foo",
766 | loc: {
767 | start: {
768 | line: 1,
769 | column: 1
770 | },
771 | end: {
772 | line: 1,
773 | column: 4
774 | }
775 | },
776 | range: [0, 3]
777 | };
778 |
779 | let {
780 | loc: { start },
781 | range: [ startIndex ]
782 | } = node;
783 |
784 | console.log(start.line); // 1
785 | console.log(start.column); // 1
786 | console.log(startIndex); // 0
787 | ```
788 |
789 |
790 |
791 | הקוד לעיל מבצע השמה של הערכים
792 | `node.loc.start`
793 | ו-
794 | `node.range[0]`
795 | אל המשתנים
796 | `start`
797 | ו-
798 | `startIndex`,
799 | בהתאמה.
800 | יש לזכור שהביטויים
801 | `loc:`
802 | ו-
803 | `range:`
804 | בפעולת הפירוק הם רק מיקומים עבור תכונות באוביקט
805 | `node`.
806 | גישה זו שימושית מאוד לשליפת ערכים מתוך מבנה נתונים בצורת
807 | JSON
808 | מבלי הצורך לסקור את המבנה כולו.
809 |
810 | ## פירוק פרמטרים
811 |
812 | לפעולת הפירוק קיים שימוש מועיל במיוחד כאשר מעבירים ארגומנטים לפונקציה. כאשר פונקציה בג׳אווהסקריפט מקבלת מספר רב של פרמטרים אופציונליים, טכניקה נפוצה היא ליצור אוביקט עבור אותן אפשרויות, בד״כ בשם
813 | `options`,
814 | שהוא אוביקט שתכונותיו מגדירות באופן מפורש את הפרמטרים האפשריים, כמו בדוגמה הבאה:
815 |
816 |
817 |
818 | ```js
819 | // תכונות על הארגומנט האחרון מייצגות פרמטרים נוספים
820 | function setCookie(name, value, options) {
821 |
822 | options = options || {};
823 |
824 | let secure = options.secure,
825 | path = options.path,
826 | domain = options.domain,
827 | expires = options.expires;
828 |
829 | // קוד להגדרת ״עוגיה״
830 | }
831 |
832 | // הארגומנט השלישי ממופה לאפשרויות נוספות
833 | setCookie("type", "js", {
834 | secure: true,
835 | expires: 60000
836 | });
837 | ```
838 |
839 |
840 |
841 | ספריות ג׳אווהסקריפט רבות מכילות פונקציות בשם
842 | `setCookie()`
843 | שדומות במראה לזו שבדוגמה האחרונה. בפונקציה זו הארגומנטים
844 | `name`
845 | ו-
846 | `value`
847 | חיוניים אך
848 | `secure`, `path`, `domain`, `expires`
849 | אינן חיוניות. ומכיוון ואין סדר עדיפות לנתונים הנוספים עדיף להוסיף אוביקט
850 | `options`
851 | עבורן מאשר לייצר פרמטרים נוספים לפונקציה. הגישה עובדת היטב, אך כעת לא ניתן לדעת למה מצפה הפונקציה רק על ידי מבט בחתימת הפונקציה, כעת חובה לקרוא את גוף הפונקציה כדי להבין אותה.
852 |
853 | פירוק פרמטרים מספק לנו דרך לכתוב בצורה ברורה יותר לאילו ארגומנטים הפונקציה מצפה. פרמטר שעובר פעולת פירוק משתמש בטכניקת פירוק אוביקט או פירוק מערך במקום פרמטר בעל שם. על מנת לראות זאת בפעולה ניתן לסקור את הגרסה המשוכתבת של הפונקציה
854 | `setCookie()`
855 | מהדוגמה הקודמת:
856 |
857 |
858 |
859 | ```js
860 | function setCookie(name, value, { secure, path, domain, expires }) {
861 |
862 | // קוד לכתיבת ״עוגיה״
863 | }
864 |
865 | setCookie("type", "js", {
866 | secure: true,
867 | expires: 60000
868 | });
869 | ```
870 |
871 |
872 |
873 | פונקציה זו מתנהגת בצורה דומה לדוגמה הקודמת, אך עתה, הארגומנט השלישי משתמש בפעולת פירוק בכדי לשלוף את הנתונים. הפרמטרים מחוץ לתבנית הפירוק הינם כאלו שמצפים לקבלם ובאותה עת, גם ברור למי שמשתמש בפונקציה
874 | `setCookie()`
875 | אילו אפשרויות נוספות זמינות להם מבחינת ארגומנטים נוספים. במידה ונחוץ ארגומנט שלישי אזי הערכים שעוברים פירוק ברורים לנו. הפרמטרים המפורקים פועלים כמו פרמטרים רגילים בכך שהם מאותחלים לערך
876 | `undefined`
877 | במידה ואינם מקבלים ערך אחר.
878 |
879 | A> פרמטרים מפורקים מקבלים את כל יכולות פעולת הפירוק שלמדנו עד כה. ניתן להשתמש בערכים דיפולטיביים, לערבב צורות פירוק של אוביקטים ומערכים, כמו גם להשתמש בשמות משתנים שונים משמות התכונות אותן קוראים.
880 |
881 | ### פרמטרים בפעולת פירוק כפרמטר חובה
882 |
883 | היבט מעניין של שימוש בפרמטרים של פעולת פירוק הוא שנזרקת שגיאה כאשר לא מעבירים אותם לפונקציה.
884 | הקריאה לפונקציה
885 | `setCookie()`
886 | שהוגדרה בדוגמת הקוד לעיל
887 | תגרום לשגיאה בדוגמת הקוד הבאה
888 |
889 |
890 |
891 | ```js
892 | // Error!
893 | setCookie("type", "js");
894 | ```
895 |
896 |
897 |
898 | הארגומנט השלישי חסר, ולכן ערכו ייחשב בתור
899 | `undefined`
900 | כמצופה מכל פרמטר חסר. זה גורם לשגיאה מכיוון שפרמטרים בפעולת פירוק הינם אך ורק צורת כתיבה מקוצרת לפעולת הפירוק. כאשר הפונקציה
901 | `setCookie()`
902 | נקראת, מה שמנוע הג׳אווהסקריפט עושה הוא:
903 |
904 |
905 |
906 | ```js
907 | function setCookie(name, value, options) {
908 |
909 | let { secure, path, domain, expires } = options;
910 |
911 | // שאר הפונקציה
912 | }
913 | ```
914 |
915 |
916 | מאחר ופעולת פירוק זורקת שגיאה כאשר הצד הימני של הביטוי מקבל את הערך
917 | `null`
918 | או
919 | `undefined`,
920 | הדבר קורה גם כאשר הארגומנט השלישי לא מועבר לפונקציה
921 | `setCookie()`.
922 |
923 | במידה ונרצה לדאוג שהפרמטר המפורק יהיה חיוני, הבעיה אינה מהותית. אך במידה ונחליט שהפרמטר המפורק הינו פרמטר רשות, ניתן לפתור את הבעיה על ידי מתן ערך דיפולטיבי, בדומה לדוגמה הבאה:
924 |
925 |
926 |
927 | ```js
928 | function setCookie(name, value, { secure, path, domain, expires } = {}) {
929 |
930 | // ...
931 | }
932 | ```
933 |
934 |
935 |
936 | הדוגמה לעיל מספקת אוביקט חדש בתור ערך דיפולטיבי לפרמטר השלישי. מתן ערך דיפולטיבי לפרמטר המפורק משמעו שהערכים עבור
937 | `secure`, `path`, `domain`, `expires`
938 | יהיו כולם
939 | `undefined`
940 | במידה והארגומנט השלישי לפונקציה
941 | `setCookie()`
942 | חסר ולא תיזרק שגיאה.
943 |
944 | ### ערכים דיפולטיביים לפרמטרים מפורקים
945 |
946 | ניתן להגדיר ערכים דיפולטיביים עבור פרמטרים מפורקים בדיוק כמו עבור פעולת פירוק רגילה. פשוט מוסיפים את סימן ההשוואה לאחר הפרמטר וכותבים את הערך הדיפולטיבי. לדוגמה:
947 |
948 |
949 |
950 | ```js
951 | function setCookie(name, value,
952 | {
953 | secure = false,
954 | path = "/",
955 | domain = "example.com",
956 | expires = new Date(Date.now() + 360000000)
957 | } = {}
958 | ) {
959 |
960 | // ...
961 | }
962 | ```
963 |
964 |
965 |
966 | לכל תכונה בפרמטר המפורק קיים ערך דיפולטיבי, ולכן אין צורך לבצע בדיקה כדי לוודא שערך מסויים הועבר לפונקציה לפני שמשתמשים בו. כמו כן, לפרמטר המפורק עצמו יש ערך דיפולטיבי של אוביקט ריק, מה שהופך את הפרמטר לפרמטר רשות. אכן, הפונקציה נראית יותר מורכבת מן הרגיל, אך זהו מחיר קטן לשלם בעד הבטחון שלכל ארגומנט יש ערך ניתן לשימוש.
967 |
968 | ## סיכום
969 |
970 | פעולת הפירוק מייעלת את העבודה עם אוביקטים ומערכים בג׳אווהסקריפט. שימוש בתחביר מוכר עבור כתיבת אוביקט ליטראל ומערך ליטראלי מאפשר לנו לשלוף את הנתונים הדרושים לנו בקלות ובמהירות.
971 |
972 | פעולת פירוק, הן בעבור אוביקטים והן בעבור מערכים , יכולה להגדיר ערכים דיפולטיביים עבור כל תכונה או פריט בעל הערך
973 | `undefined`
974 | ובשני המצבים היא זורקת שגיאה כאשר הצד הימני של הפעולה מקבל את הערך
975 | `null`
976 | או
977 | `undefined`.
978 | ניתן גם לנווט בתוך מבני נתונים עמוקים לכל עומק.
979 |
980 | פעולות פירוק משתמשות במשתנים מסוג
981 | `var`, `let`, `const`
982 | וחובה לאתחל אותם. פעולות פירוק שמבצעות השמה יכולות להחליף פעולות השמה אחרות ומאפשרות לבצע השמה אל תוך משתנים שכבר הוגדרו קודם לפעולה.
983 |
984 | פרמטרים מפורקים משתמשים בתחביר הפירוק כדי ליצור אוביקטים מסוג ״אופציות״
985 | ("options")
986 | שקופים יותר בעת שימוש בתור פרמטרים לפונקציה. הנתונים שבהם מעוניינים יכולים לקבל שם לצד פרמטרים אחרים בעלי שם שעומדים לבדם.
987 | פרמטרים מפורקים יכולים להיות מערכים, אוביקטים או שילוב שלהם, וניתן לבצע עליהם את כל פעולות הפירוק.
988 |
989 |
990 |
--------------------------------------------------------------------------------