למרות ששנים התעסקתי בתחום הווב, רוב הזמן עסקתי בצד-השרת. ההיכרות שלי עם שפת JavaScript הייתה חלקית: יצא לי לדבג קוד ולקלל את מי שכתב אותו - אך אף פעם לא כתבתי קוד ג'אווהסקריפט בעצמי. בחודשים האחרונים אני מתעסק לא מעט בצד הלקוח והרגשתי שיש לי חוסר להשלים. החלטתי ללמוד ג'אווהסקריפט - ומהר. אין לי הרבה זמן ודילגתי על קורס באורך שלושה ימים שהקצו לי ממקום העבודה. חיפשתי דרכי קיצור.
אם אתם נמצאים במקום דומה, או שפשוט מעניין אתכם ללמוד כמה תובנות חשובות על השפה המוזרה והמבטיחה הזו בזמן קצר - זהו פוסט בשבילכם. בסופו, בשאיפה, תדעו יותר על ג'אווהסקריפט ותדעו שאתם בעצם יודעים פחות ג'אווהסקריפט ממה שחשבתם עד כה.
שייך לסדרה מבוא מואץ ל JavaScript ו jQuery
למה JavaScript?
חלקכם אולי עשוי לחשוב שג'אווהסקריפט היא היסטוריה ולכן - השקעה בלימוד השפה איננה פעולה משתלמת.
ג'אווהסקריפט אמנם איננה שפה חדשה (היא הופיעה ב95' ביחד עם ג'אווה) - אך בעוד ג'אווה בדעיכה, ג'אווהסקריפט תופסת תאוצה. אני רואה לכך כמה סיבות:
ע"פ ה HTTP Archive, למשל, פרויקט שעוקב אחרת מגמות באתרי האינטרנט הגדולים, כמות הג'אווהסקריפט בשימוש באתרים שבמדגם זינקה ב 50% בשנה האחרונה בלבד.
כיצד לומדים ג'אווהסקריפט?
י
ש 3 טעויות שמפתח ג'אווה ותיק עלול לעשות לגבי שפת ג'אווהסקריפט. אני, לפחות, עשיתי את כולן:
טעות מס' 1: "אני מכיר את ה Syntax". זהו C-Syntax. אני אסתדר Along the way.
המציאות: Java ו JavaScript דומים אחד לשני כמו "Car" ו "Carpet": בעיקר בשם.
הקוד הבא אכן נראה מוכר למדי:
אם אתם מדמיינים כך את הג'אווהסקריפט שתתקלו בו - אתם טועים. מהר מאוד תתקלו בביטויים כמו prototype, הסימן $ שמופיע המון, משתנים עם קווים תחתונים __ ובכלל - סגנון זר ולא מוכר. זהו אמנם C-Sytnax - אך יש בו מספיק אלמנטים לא מוכרים על מנת לבלבל אתכם לגמרי.
למשל, האם אתם יכולים להסביר מה עושה הקוד הבא (שכתוב ב C-Syntax)?
טעות מס' 2: לחפש דוגמאות קוד והסברים באינטרנט.
המציאות: פעם סיפרתי איך שמם הטוב של האקרים נהרס בגלל הפצה של כלים פשוטים לפריצה - שהמשתמשים בהם לא ידעו כמעט כלום על Hacking באמת. ההאקרים קראו לאלה Script Kiddies - מריצי סקריפטים.
בעולם הג'אווהסקריפט יש תופעה דומה. המון ממשתמשי הג'אווהסקריפט הם לא מתכנתים מקצועיים - אלא משתמשים לא מקצועיים ש"בונים אתרי אינטרנט". אני קורא להם: ה-JS Kiddies.
התוצאה היא שיש המון חומר ברמה נמוכה באינטרנט, בפורומים ובכלל. אם תחפשו סתם כך בגוגל יש סיכוי גבוה למדי שתקבלו עצות חלקיות, תקראו tutorials שלא מגרדים את עומק השפה ותראו דוגמאות קוד לא מקצועיות.
קחו לדוגמה את האתר W3Schools - אתר reference לטכנולוגיות ווב המוכר. לאתר, מסתבר, אין שום קשר ל W3C והוא התבקש ע"י ה W3C, כמה פעמים, לשנות את שמו. מדוע?
האתר כולל שגיאות גסות בנוגע ל HTML, CSS וג'אווהסקריפט בכלל. אמנם הוא עושה רושם רציני - אך העובדות מדברות בשם עצמן. W3Fools - אתר שאוסף (חלק מ-) השגיאות וחוסרי הדיוקים של W3Schools, ממליץ למשל לחפש בגוגל עם "w3schools-", כלומר: רק תוצאות שלא כוללות את W3Schools. בהסתמך על הפופולאריות של W3Schools וכמות הפעמים שהוא מופיע בגוגל - זו עצה לא כל-כך רעה...
טעות מס' 3: "הבנתי. משהו פה לא עובד. נקרא את התיעוד הרשמי מקצה-לקצה כך שלא יהיו לנו הפתעות".
המציאות: כשלמדתי Java באמת הלכתי ל Spec - אך זו הייתה שפה ראשונה והייתי סטודנט צעיר. כשניסיתי לקרוא את התיעוד המומלץ של ג'אווהסקריפט - מצאתי את עצמי משתעמם מהר מאוד. רוב מה שכתוב שם הוא ברור מאליו למי שמכיר את ג'אווה או #C לעומק.
אני אנסה בסדרת הפוסטים הבאה לתת לכם מספיק Kick Start, על מנת להתחיל ולהכנס לנבכי ה JavaScript. אני מקווה שאצליח למצוא את האיזון בין "מובן" ל"לא משעמם".
אם אתם רוצים לכתוב בדפדפן, ידיעת ג'אווהסקריפט היא רק חלק מהתמונה. כנראה שתרצו ללמוד גם jQuery לעומק (סימן ה $ שהזכרתי קודם), קצת על ה DOM ולהכיר ספריות רלוונטיות ושימושיות.
בהמשך הסדרה אני עוסק ב jQuery, מקור מקיף לגבי ה DOM הוא DOM Enlightenment. עוד חומר על פיתוח ווב תוכלו למצוא באתר העברי אינטרנט-ישראל.
כמה הבדלים בסיסיים בין JavaScript וג'אווה:
הערה: אני מקצר וכותב "ג'אווה" בלבד, אך מה כתוב כאן רלוונטי באותה מידה ל #C.
Scope
בג'אווה, Scope נוצר ע"י סוגריים מסולסלים, יהיה זה משפט if, לולאת for או סתם סוגריים שהוכנסו בתוך הקוד.
בג'אווהסקריפט Scope נוצר רק על ידי פונקציה. הנה דוגמה להתנהגות הצפויה (מקור: javaScriptGarden):
Global Scope
מה שלא מוגדר בתוך ה Scope של הפונקציה - שייך ל Scope הגלובלי. דריסות הדדיות הן תקלה שכיחה.
קצת יותר גרוע היא העובדה שאם הגדרתם משתנה בתוך פונקציה ללא המילה השמורה "var" - אותו משתנה יוגדר אוטומטית ב Scope הגלובלי.
שכחתם? התבלבלתם? בזמן Refactoring מחקתם את ה "var" - בעיה שלכם.
הנה דוגמה מרגיזה: כתבתם אפליקציה לניהול חתונות. נשיא וארה"ב ואשתו אישרו הגעה. הפקיד ראה הודעות Alert ברורות של אישור ההשתתפות על מסך-המחשב, אבל המקומות לא נשמרו והם נאלצו לאכול בעמידה (כלומר, הורי החתן נאלצו לאכול בעמידה - כמובן).
איך מתמודדים עם pitfall שכזה שבנוי בתוך השפה? כלי וריפיקציה בשם JSLint יספק הזהרה, אם אתם עובדים ב IDE שמריץ אותו תוך כדי כתיבה - יש סיכוי שתצליחו להימנע מכמה תקלות. הבעיה ב JSLint (או חברו JSHint) הוא שההזהרות שהם מספקים נכונות רק ל 90% המקרים ואתם נשארים עם רשימה של Warnings לא מטופלים - גם כאשר יש לכם קוד נפלא.
CoffeScript היא דרך נוספת להתמודדות. הוא לדוגמא מוסיף var מאחורי הקלעים לכל הגדרה.
Hoisting
חשבתם שהדברים מסתדרים? שימו לב לכלל הבא: הקוד בג'אווהסקריפט לא ירוץ ע"פ הסדר שבו הוא נכתב.
אני חוזר: הקוד בג'אווהסקריפט לא ירוץ ע"פ הסדר שבו הוא נכתב.
הגדרות var והגדרות של פונקציה, רצות לפני כל שאר הקוד. זה כאילו ה interpreter מחפש את כל ההגדרות הללו ומעביר אותן לראש ה scope הנוכחי, ורק אז מריץ את הקוד. תהליך זה נקרא Hoisting.
Hoisting הרבה פעמים עוזר, וברוב המקרים הגדול לא יהיו איתו בעיות. אבל ברגע שהבעיות יקרו, הן יהיו מאוד קשה לאיתור. עצם העובדה שניתן לכתוב הרבה קוד שעובד כהלכה ללא המודעות לתהליך ה hoisting - מטשטשת את החושים לסכנות שהוא מציג.
התבוננו על דוגמת הקוד הבאה:
סביבת הרצה לג'אווהסקריפט אליה ניתן לגשת בקלות
כמפתחים מנוסים, אתם בוודאי יודעים שאם רק תקראו על שפת תכנות מבלי לנסות אותה - הידע לא יושרש היטב. חשוב "ללכלך את הידיים".
הנה אני מקליד בו איזו פיסת קוד:
יכולת שאני אוהב היא להתבונן על אובייקטים בתוך ה console בכל פעם שהם חוזרים כערך. למשל אנחנו רואים שלאובייקט שיצרנו יש משתנה (סודי) בשם __proto__. הממ... מעניין מה זה אומר...
הדרך המומלצת להגדרת פונקציות בג'אווהסקריפט היא להגדיר פונקציות אנונימיות ולבצע השמה למשתנה:
כאשר יש פונקציה בודדת, המשתנים שלה ימחקו מייד לאחר הרצת הפונקציה:
כאשר יש לי פונקציה פנימית, בתוך הפונקציה, שקוראת למשתנים - הם ישארו לחיות לעד. אני מנחש שזה עניין של pointer counting. הנה דוגמה:
בדוגמת קוד תמימה זו, אני משתמש ב anonymous function מהסוג שהזכרנו קודם לכן על מנת לממש Listener. זוהי פעולה נפוצה למדי בג'אווהסקריפט. אני מגדיר פונקציה פשוטה למדי שתכתוב לערכים לlog.
התוצאה הצפויה היא, אם כן, ספירה מ 1 עד 15 ל log.
אם אתם נמצאים במקום דומה, או שפשוט מעניין אתכם ללמוד כמה תובנות חשובות על השפה המוזרה והמבטיחה הזו בזמן קצר - זהו פוסט בשבילכם. בסופו, בשאיפה, תדעו יותר על ג'אווהסקריפט ותדעו שאתם בעצם יודעים פחות ג'אווהסקריפט ממה שחשבתם עד כה.
שייך לסדרה מבוא מואץ ל JavaScript ו jQuery
למה JavaScript?
חלקכם אולי עשוי לחשוב שג'אווהסקריפט היא היסטוריה ולכן - השקעה בלימוד השפה איננה פעולה משתלמת.
ג'אווהסקריפט אמנם איננה שפה חדשה (היא הופיעה ב95' ביחד עם ג'אווה) - אך בעוד ג'אווה בדעיכה, ג'אווהסקריפט תופסת תאוצה. אני רואה לכך כמה סיבות:
- בעוד ה JVM של ג'אווה רץ על "כל שרת" - עובדה שסייעה לג'אווה להפוך למאוד נפוצה, ג'אווהסקריפט יכולה לרוץ על כל פלטפורמות ה JVM (בעזרת Rhino) ובנוסף - גם על דפדפנים ועל iOS -> שתי פלטפורמות אסטרטגיות שחשיבותן רק הולכת ועולה. תאהבו את זה או לא, אבל ג'אווהסקריפט היא שפת התכנות האוניברסלית ביותר שקיימת כיום.
- ג'אווהסקריפט היא "מונופוליסטית": אין שפה אחרת שנתמכת ע"ג כל הדפדפנים החשובים.
בניגוד ל JVM וה CLI שמריצים ByteCode ולא מודעים לשפה שבהם ה ByteCode נכתב (וכך נפתח פתח ל JRuby, סקאלה ועוד) JavaScript היא שפה מפוענחת (interpreted) והמנועים של ג'אווהסקריפט שפרוסים על הדפדפנים יודעים להריץ רק אותה. - ג'אווהסקריפט טובה לבניית UI. בניגוד לג'אווה שבמשך שנים ניסתה וניסתה - אך נכשלה, בג'אווהסקריפט ניתן לכתוב UI יפה ובצורה קלה. (על בסיס HTML ו CSS - כמובן).
- לג'אווהסקריפט יש בסיס מתכנתים גדול. הרבה מפתחי #PHP, Ruby, C וג'אווה - יודעים גם קצת ג'אווהסקריפט. יש איזה ייתרון בלהיות שפה זקנה בת 17.
- לג'אווהסקריפט אין הרבה מתנגדים. אין פה מלחמה בין אורקל למייקרוסופט, בין גוגל לפייסבוק וכו'. ג'אווהסקריפט נמצאת בעין הסערה.
ע"פ ה HTTP Archive, למשל, פרויקט שעוקב אחרת מגמות באתרי האינטרנט הגדולים, כמות הג'אווהסקריפט בשימוש באתרים שבמדגם זינקה ב 50% בשנה האחרונה בלבד.
כמה ג'אווהסקריפט יש בדפי אינטרנט של אתרים מפורסמים? הממ... זה גדל! מקור: pingdom.com |
כיצד לומדים ג'אווהסקריפט?
י
ש 3 טעויות שמפתח ג'אווה ותיק עלול לעשות לגבי שפת ג'אווהסקריפט. אני, לפחות, עשיתי את כולן:
טעות מס' 1: "אני מכיר את ה Syntax". זהו C-Syntax. אני אסתדר Along the way.
המציאות: Java ו JavaScript דומים אחד לשני כמו "Car" ו "Carpet": בעיקר בשם.
הקוד הבא אכן נראה מוכר למדי:
for (i = 0; i< set.length; i++) {
set[i].doSomething();
}
האם אתם יכולים לומר בוודאות באיזו שפה כתוב קוד זה? אני בספק.set[i].doSomething();
}
אם אתם מדמיינים כך את הג'אווהסקריפט שתתקלו בו - אתם טועים. מהר מאוד תתקלו בביטויים כמו prototype, הסימן $ שמופיע המון, משתנים עם קווים תחתונים __ ובכלל - סגנון זר ולא מוכר. זהו אמנם C-Sytnax - אך יש בו מספיק אלמנטים לא מוכרים על מנת לבלבל אתכם לגמרי.
למשל, האם אתם יכולים להסביר מה עושה הקוד הבא (שכתוב ב C-Syntax)?
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object, args.concat(Array.prototype.slice.call(arguments)));
};
};
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object, args.concat(Array.prototype.slice.call(arguments)));
};
};
טעות מס' 2: לחפש דוגמאות קוד והסברים באינטרנט.
המציאות: פעם סיפרתי איך שמם הטוב של האקרים נהרס בגלל הפצה של כלים פשוטים לפריצה - שהמשתמשים בהם לא ידעו כמעט כלום על Hacking באמת. ההאקרים קראו לאלה Script Kiddies - מריצי סקריפטים.
בעולם הג'אווהסקריפט יש תופעה דומה. המון ממשתמשי הג'אווהסקריפט הם לא מתכנתים מקצועיים - אלא משתמשים לא מקצועיים ש"בונים אתרי אינטרנט". אני קורא להם: ה-JS Kiddies.
התוצאה היא שיש המון חומר ברמה נמוכה באינטרנט, בפורומים ובכלל. אם תחפשו סתם כך בגוגל יש סיכוי גבוה למדי שתקבלו עצות חלקיות, תקראו tutorials שלא מגרדים את עומק השפה ותראו דוגמאות קוד לא מקצועיות.
קחו לדוגמה את האתר W3Schools - אתר reference לטכנולוגיות ווב המוכר. לאתר, מסתבר, אין שום קשר ל W3C והוא התבקש ע"י ה W3C, כמה פעמים, לשנות את שמו. מדוע?
האתר כולל שגיאות גסות בנוגע ל HTML, CSS וג'אווהסקריפט בכלל. אמנם הוא עושה רושם רציני - אך העובדות מדברות בשם עצמן. W3Fools - אתר שאוסף (חלק מ-) השגיאות וחוסרי הדיוקים של W3Schools, ממליץ למשל לחפש בגוגל עם "w3schools-", כלומר: רק תוצאות שלא כוללות את W3Schools. בהסתמך על הפופולאריות של W3Schools וכמות הפעמים שהוא מופיע בגוגל - זו עצה לא כל-כך רעה...
טעות מס' 3: "הבנתי. משהו פה לא עובד. נקרא את התיעוד הרשמי מקצה-לקצה כך שלא יהיו לנו הפתעות".
המציאות: כשלמדתי Java באמת הלכתי ל Spec - אך זו הייתה שפה ראשונה והייתי סטודנט צעיר. כשניסיתי לקרוא את התיעוד המומלץ של ג'אווהסקריפט - מצאתי את עצמי משתעמם מהר מאוד. רוב מה שכתוב שם הוא ברור מאליו למי שמכיר את ג'אווה או #C לעומק.
אני אנסה בסדרת הפוסטים הבאה לתת לכם מספיק Kick Start, על מנת להתחיל ולהכנס לנבכי ה JavaScript. אני מקווה שאצליח למצוא את האיזון בין "מובן" ל"לא משעמם".
אם אתם רוצים לכתוב בדפדפן, ידיעת ג'אווהסקריפט היא רק חלק מהתמונה. כנראה שתרצו ללמוד גם jQuery לעומק (סימן ה $ שהזכרתי קודם), קצת על ה DOM ולהכיר ספריות רלוונטיות ושימושיות.
בהמשך הסדרה אני עוסק ב jQuery, מקור מקיף לגבי ה DOM הוא DOM Enlightenment. עוד חומר על פיתוח ווב תוכלו למצוא באתר העברי אינטרנט-ישראל.
כמה הבדלים בסיסיים בין JavaScript וג'אווה:
הערה: אני מקצר וכותב "ג'אווה" בלבד, אך מה כתוב כאן רלוונטי באותה מידה ל #C.
Scope
בג'אווה, Scope נוצר ע"י סוגריים מסולסלים, יהיה זה משפט if, לולאת for או סתם סוגריים שהוכנסו בתוך הקוד.
בג'אווהסקריפט Scope נוצר רק על ידי פונקציה. הנה דוגמה להתנהגות הצפויה (מקור: javaScriptGarden):
function test() { // a scope
for(var i = 0; i < 10; i++) { // not a scope
// do something
}
console.log(i); // 10
}
for(var i = 0; i < 10; i++) { // not a scope
// do something
}
console.log(i); // 10
}
Global Scope
מה שלא מוגדר בתוך ה Scope של הפונקציה - שייך ל Scope הגלובלי. דריסות הדדיות הן תקלה שכיחה.
// Defined in Global Scope
foo = '42';
// Defined in "current" scope. Either function or global
var foo = '42';
foo = '42';
// Defined in "current" scope. Either function or global
var foo = '42';
קצת יותר גרוע היא העובדה שאם הגדרתם משתנה בתוך פונקציה ללא המילה השמורה "var" - אותו משתנה יוגדר אוטומטית ב Scope הגלובלי.
שכחתם? התבלבלתם? בזמן Refactoring מחקתם את ה "var" - בעיה שלכם.
הנה דוגמה מרגיזה: כתבתם אפליקציה לניהול חתונות. נשיא וארה"ב ואשתו אישרו הגעה. הפקיד ראה הודעות Alert ברורות של אישור ההשתתפות על מסך-המחשב, אבל המקומות לא נשמרו והם נאלצו לאכול בעמידה (כלומר, הורי החתן נאלצו לאכול בעמידה - כמובן).
weddingParticipantsCounter = 100;
function acceptParticipation(name) {
weddingParticipnatsCounter = weddingParticipantsCounter + 1;
alert('Registration Accepted: ' + name);
}
acceptParticipation("Mr. President");
acceptParticipation("The First Lady");
console.log(weddingParticipantsCounter); // -> 100?!
האם אתם יכולים להסביר מה השתבש? [א]function acceptParticipation(name) {
weddingParticipnatsCounter = weddingParticipantsCounter + 1;
alert('Registration Accepted: ' + name);
}
acceptParticipation("Mr. President");
acceptParticipation("The First Lady");
console.log(weddingParticipantsCounter); // -> 100?!
CoffeScript היא דרך נוספת להתמודדות. הוא לדוגמא מוסיף var מאחורי הקלעים לכל הגדרה.
Hoisting
חשבתם שהדברים מסתדרים? שימו לב לכלל הבא: הקוד בג'אווהסקריפט לא ירוץ ע"פ הסדר שבו הוא נכתב.
אני חוזר: הקוד בג'אווהסקריפט לא ירוץ ע"פ הסדר שבו הוא נכתב.
הגדרות var והגדרות של פונקציה, רצות לפני כל שאר הקוד. זה כאילו ה interpreter מחפש את כל ההגדרות הללו ומעביר אותן לראש ה scope הנוכחי, ורק אז מריץ את הקוד. תהליך זה נקרא Hoisting.
Hoisting הרבה פעמים עוזר, וברוב המקרים הגדול לא יהיו איתו בעיות. אבל ברגע שהבעיות יקרו, הן יהיו מאוד קשה לאיתור. עצם העובדה שניתן לכתוב הרבה קוד שעובד כהלכה ללא המודעות לתהליך ה hoisting - מטשטשת את החושים לסכנות שהוא מציג.
התבוננו על דוגמת הקוד הבאה:
var n = 1;
function foo() {
if (false) { var n = 10; }
console.log(n);
}
foo();
function foo() {
if (false) { var n = 10; }
console.log(n);
}
foo();
מה אתם מצפים שתהייה התוצאה שתכתב ללוג?
זו כמובן דוגמה מופשטת שיכולה לתאר מצב אמיתי ומורכב הרבה יותר. דמיינו את הקושי לאתר תקלות שכאלו בקוד של עשרות של שורות עם לוגיקה מורכבת.
אני מניח שהעובדה שללוג נכתב הערך "undefined" - עלולה להיות מבלבלת. "undefined" הוא ה null של ג'אווהסקריפט (יש גם null, אבל לא משתמשים בו הרבה. הוא כמו "ערך נוסף" שמשתמשים בו בכמה מקרים מסוימים - אך כ"קונבנציה". נראה שניתן היה להשתמש ב "undefined" במקרים אלו באותה המידה [ב]).
הקוד שבעצם רץ, לאחר פעולת ה hoisting, הוא זה:
זו כמובן דוגמה מופשטת שיכולה לתאר מצב אמיתי ומורכב הרבה יותר. דמיינו את הקושי לאתר תקלות שכאלו בקוד של עשרות של שורות עם לוגיקה מורכבת.
אני מניח שהעובדה שללוג נכתב הערך "undefined" - עלולה להיות מבלבלת. "undefined" הוא ה null של ג'אווהסקריפט (יש גם null, אבל לא משתמשים בו הרבה. הוא כמו "ערך נוסף" שמשתמשים בו בכמה מקרים מסוימים - אך כ"קונבנציה". נראה שניתן היה להשתמש ב "undefined" במקרים אלו באותה המידה [ב]).
הקוד שבעצם רץ, לאחר פעולת ה hoisting, הוא זה:
var n;
function foo() {
var n;
if (false) { n = 10; }
console.log(n);
}
n = 1;
foo();
function foo() {
var n;
if (false) { n = 10; }
console.log(n);
}
n = 1;
foo();
הגדרת המשתנה n, גם בתוך הפונקציה, עולה למעלה. זכרו שהסוגרים המסולסלים של משפט ה if לא מגדירים Scope חדש. כחלק מחוקי ה hoisting, ההשמה נשארת במקומה.
n מוגדר ב scope של הפונקציה ולכן הוא "מחביא" את n שהוגדר ב global scope.
כיוון שהתנאי "false" לא מתרחש במקרה זה, n נשאר undefined עד לנקודה בה הערך נכתב ללוג.
מה עושים?
שומרים במשמעת ברזל על הכלל הבא:
- כל הגדרות ה var וה function יהיו תמיד בראש ה scope הנוכחי, אם זה הקובץ (גלובלי) או הפונקציה.
אחד הטיעונים שאני זוכר שהועלו כנגד שפת פאסקל הוא "למה אני צריך להגדיר את כל המשתנים בראש הפונקציה? למה הקומפיילר לא יכול לעשות את זה בשבילי?!". על טיעון זה בלבד אני זוכר כמה אנשים שהתרחקו מהשפה.
הכלל של הגדרת משתנים מראש הוא מובנה בשפה - ומתכנתים לומדים אותו בשיעורים הראשונים על השפה.
בג'אווהסקריפט ניתן להגדיר משתנים בכל מקום - רק אחרי חודשים רבים של עבודה בשפה מגיעים להבנה שפשוט לא כדאי. השפה לא מספקת שום אזהרה או עצה. בכל זאת, לא שמעתי מתכנת ג'אווהסקריפט אחד מתלונן על הנושא הזה.
מה ההבדל? האם יכול להיות שההבדל הוא שבפאסקל נתקלים בעניין אחרי חצי שעה (אפס השקעה אישית) ובג'אווהסקריפט אחרי חודשים של השקעה אישית? אחרי שיש לכם כבר קוד ולמדתם כמה דברים?
האם שווה "למכור תמונה יפה" ולהשאיר את הצדדים הפחות יפים לאח"כ?
אם כן - זה יכול להיות Case Study טוב ב Pre-Sale. אני מניח שלא מעט תוכנות ארגוניות נהנות מהמנגנון הזה.
הכלל של הגדרת משתנים מראש הוא מובנה בשפה - ומתכנתים לומדים אותו בשיעורים הראשונים על השפה.
בג'אווהסקריפט ניתן להגדיר משתנים בכל מקום - רק אחרי חודשים רבים של עבודה בשפה מגיעים להבנה שפשוט לא כדאי. השפה לא מספקת שום אזהרה או עצה. בכל זאת, לא שמעתי מתכנת ג'אווהסקריפט אחד מתלונן על הנושא הזה.
מה ההבדל? האם יכול להיות שההבדל הוא שבפאסקל נתקלים בעניין אחרי חצי שעה (אפס השקעה אישית) ובג'אווהסקריפט אחרי חודשים של השקעה אישית? אחרי שיש לכם כבר קוד ולמדתם כמה דברים?
האם שווה "למכור תמונה יפה" ולהשאיר את הצדדים הפחות יפים לאח"כ?
אם כן - זה יכול להיות Case Study טוב ב Pre-Sale. אני מניח שלא מעט תוכנות ארגוניות נהנות מהמנגנון הזה.
סביבת הרצה לג'אווהסקריפט אליה ניתן לגשת בקלות
כמפתחים מנוסים, אתם בוודאי יודעים שאם רק תקראו על שפת תכנות מבלי לנסות אותה - הידע לא יושרש היטב. חשוב "ללכלך את הידיים".
אני יודע (ע"פ סטטיסטיקות של Google Analytics) שאם אתם קוראים את הבלוג הזה יש סיכוי של 50% שאתם משתמשים בכרום, ועוד כ 20% שאתם משתמשים ב FireFox. לשני הדפדפנים הללו יש סביבת פיתוח לא רעה בכלל לג'אווהסקריפט שניתן להתחיל להשתמש בה מיד. אם אתם עדיין תקועים עם IE - השיגו לכם כרום. הגיע הזמן.
הנה דוגמא פשוטה כיצד להריץ קוד ג'אווהסקריפט אם אתם רצים בכרום. אתם יכולים לעשות זאת מבלי לאתחל את הדפדפן. פשוט פתחו טאב חדש, לחצו על הכפתור הימני של העכבר בתוך הדף, בחרו את "Inspect Element" - כניסה מהירה לכלי הפיתוח של כרום, עברו ל Console והנה יש לכם Interpreter ג'אווהסקריפט איכותי:
הנה דוגמא פשוטה כיצד להריץ קוד ג'אווהסקריפט אם אתם רצים בכרום. אתם יכולים לעשות זאת מבלי לאתחל את הדפדפן. פשוט פתחו טאב חדש, לחצו על הכפתור הימני של העכבר בתוך הדף, בחרו את "Inspect Element" - כניסה מהירה לכלי הפיתוח של כרום, עברו ל Console והנה יש לכם Interpreter ג'אווהסקריפט איכותי:
הנה אני מקליד בו איזו פיסת קוד:
יכולת שאני אוהב היא להתבונן על אובייקטים בתוך ה console בכל פעם שהם חוזרים כערך. למשל אנחנו רואים שלאובייקט שיצרנו יש משתנה (סודי) בשם __proto__. הממ... מעניין מה זה אומר...
אובייקטים ופונקציות
Object Literal
Object Literal
{};
כך יוצרים בג'אווהסקריפט אובייקט. זהו אובייקט ריק.
בפועל אובייקט הוא רשימה של תכונות:
בפועל אובייקט הוא רשימה של תכונות:
var myObj = { a: "Hello", b: "World" };
אם התחביר נראה לכם מוכר, נסו להיזכר מהם ראשי התיבות של JSON. הא!
כבר נתקלנו בפונקציות, אך יש כמה עובדות חשובות נוספות שכדאי להכיר לגביהן.
פונקציות הן אובייקטים
בג'אווהסקריפט, פונקציות הן אובייקטים לכל עניין ודבר. על כן, אין לי מניעה לכתוב את הקוד הבא:
כבר נתקלנו בפונקציות, אך יש כמה עובדות חשובות נוספות שכדאי להכיר לגביהן.
פונקציות הן אובייקטים
בג'אווהסקריפט, פונקציות הן אובייקטים לכל עניין ודבר. על כן, אין לי מניעה לכתוב את הקוד הבא:
function foo(text) { console.log(text); }
foo.x = 4;
foo(foo.x);
foo.x = 4;
foo(foo.x);
שידפיס כמובן את הערך 4.
Anonymous Functions
ג'אווהסקריפט בדפדפן היא שפה עשירה מאוד בשימוש בevents ולכן התחביר של Anonymous Functions הוא קצר ונוח. לדוגמה:
obj.doSomething(function(event) { console.log(event.getMessage()); });
הדרך המומלצת להגדרת פונקציות בג'אווהסקריפט היא להגדיר פונקציות אנונימיות ולבצע השמה למשתנה:
var foo = function(text) { console.log(text); };
בלינק זה תוכלו למצוא כמה דוגמאות מה יכול לקרות לכם אם לא תעשו זאת (מוהא-הא-הא!).
על מנת למנוע דריסה של פונקציות, אפשר להשתמש ב namespace:
var myNS = myNS || {}; // if namespace myNS exists - use it, else create a new namespace.
myNS.foo = function(text) { console.log(text); };
myNS.foo = function(text) { console.log(text); };
Closure
קלוזור הוא מונח שבוודאי שמעתם עליו, או שתשמעו עליו מיד שתתחילו להתעסק בג'אווהסקריפט. Closure בג'אווהסקריפט הוא Scope בתוך Scope, ומכיוון שבג'אווהסקריפט scope מוגדר רק ע"י פונקציה - הרי Closure הוא פונקציה בתוך פונקציה.כאשר יש פונקציה בודדת, המשתנים שלה ימחקו מייד לאחר הרצת הפונקציה:
function simpleFunction() {
var x = 4;
}
// x no longer exists
var x = 4;
}
// x no longer exists
כאשר יש לי פונקציה פנימית, בתוך הפונקציה, שקוראת למשתנים - הם ישארו לחיות לעד. אני מנחש שזה עניין של pointer counting. הנה דוגמה:
function Counter(start) { // closure
var count = start;
return function() { // function with scope
return ++count;
}
}
var c = Counter(4);
// count still exists
c(); // 5
c(); // 6
המשתנה c מחזיק reference לפונקציה הפנימית של Counter, והיא מצידה מחזיקה reference למשתנה count - וכך הוא נשמר בחיים. var count = start;
return function() { // function with scope
return ++count;
}
}
var c = Counter(4);
// count still exists
c(); // 5
c(); // 6
בואו נסתכל בדוגמא לבעיה שיכולה להיפתר ע"י שימוש ב Closure.
התוצאה הצפויה היא, אם כן, ספירה מ 1 עד 15 ל log.
var listeners = []; // array
// fill up some listeners
for (var i = 0; i < 15; i++) {
listeners.push(function() { console.log(i); });
}
// trigger an event to all listeners. Each one counts.
for (var j in listeners) {
listeners[j](); // execute
}
// fill up some listeners
for (var i = 0; i < 15; i++) {
listeners.push(function() { console.log(i); });
}
// trigger an event to all listeners. Each one counts.
for (var j in listeners) {
listeners[j](); // execute
}
התוצאה בפועל היא 15 פעמים המספר "15".
הסיבה לכך היא כזו: הפונקציה שנוצרת בתוך לולאת ה for מתייחסת למשתנה i, אבל i הוא משתנה בסביבה הגלובלית. נזכיר שסוגריים מסולסלים לא מייצרים scope. בנוסף - הפונקציה אינה מחושבת בזמן ההגדרה, אז בפועל נוצרות לנו במערך ה listeners חמש עשר עותקים של פונקציה שתוכנן הוא (console.log(i.
כיוון שהן מופעלות רק בסוף קטע הקוד, i שהוא משתנה גלובלי - בעל הערך 15, כך שיודפס 15 פעמים הערך 15.
גם יצירה של 15 פונקציות בזיכרון הוא בזבוז לא קטן שהיינו מעדיפים להימנע ממנו.
הדרך לפתרון היא להוסיף Closure שישמור (או "יקפיא") את ערך המשתנה i, עבור הרגע בו הוגדרה הפונקציה:
הפונקציה שנשמרת במערך listeners היא כבר הפונקציה הפנימית והרצתה תגרום לכתיבת הערך המצופה, קרי 0 עד 14.
כיצד ניתן להבטיח שלא תפספסו מקרים פשוטים שכאלו? אם ניחנתם בתסמונת אספרגר - הסיכויים שלכם להתבלבל בפרטים טכניים שכאלו היא קטנה למדי. בכל מקרה אחר - אתם יכולים להשתמש ב JSLint שייתן אזהרה ברורה. אומרים שעם הותק בג'אווהסקריפט הטעויות הללו הולכות ונעלמות - אני עוד לא הגעתי לשם.
שימו לב שיצרנו פה מבנה בעל מורכבות, על מנת לרתום את ג'אווהסקריפט לצורכינו.מפתחי OO בטח יזהו פה את הפוטנציאל בייצוג אובייקטים. בפוסט ההמשך אני מתכוון להמשיך ולהציג עוד כמה מבנים כאלו - עבורי ההתמחות בהן היא הפער התפיסתי הגדול במעבר מג'אווה לג'אווהסקריפט.
מקווה שנהניתם!
פוסט ההמשך בו אדבר על מבנים בג'אווהסקריפט ומשמעותם.
---
[א] קצת אכזרי, אך זו מהמציאות: weddingParticipnatsCounter = weddingParticipantsCounter + 1. ג'אווהסקריפט זיהה משתנה לא מוכר ובמקום להטריד את המתכנת - טרח והגדיר אותו בשבילו. איזה יופי!
IDE טוב היה מספק value highlighting שאולי, היה יכול לעצור מוקדם את התקלה.
[ב] האם זו טעות תכנונית שתעלה למשק העולמי 2 מיליארד דולר?
הסיבה לכך היא כזו: הפונקציה שנוצרת בתוך לולאת ה for מתייחסת למשתנה i, אבל i הוא משתנה בסביבה הגלובלית. נזכיר שסוגריים מסולסלים לא מייצרים scope. בנוסף - הפונקציה אינה מחושבת בזמן ההגדרה, אז בפועל נוצרות לנו במערך ה listeners חמש עשר עותקים של פונקציה שתוכנן הוא (console.log(i.
כיוון שהן מופעלות רק בסוף קטע הקוד, i שהוא משתנה גלובלי - בעל הערך 15, כך שיודפס 15 פעמים הערך 15.
גם יצירה של 15 פונקציות בזיכרון הוא בזבוז לא קטן שהיינו מעדיפים להימנע ממנו.
הדרך לפתרון היא להוסיף Closure שישמור (או "יקפיא") את ערך המשתנה i, עבור הרגע בו הוגדרה הפונקציה:
var listeners = []; // array
function eventHandler(i) { // outer function = closure
// fill in the listeners (syntetic)
for (var i = 0; i < 15; i++) {
listeners.push( eventHandler(i) );
}
// trigger an event to all listeners. Each one counts.
for (var j in listeners) {
listeners[j](); // execute listener
}
בדוגמה זו, הפונקציה eventHandler מבוצעת בתוך הלולאה. הערך i עובר אליה כפי שהוא, ואז נלכד "לנצח" בתוך ב closure. טוב, לא ממש לנצח - אולי עד שנגלוש לדף ווב אחר.function eventHandler(i) { // outer function = closure
return function() { // inner function
console.log(i);
}
}// fill in the listeners (syntetic)
for (var i = 0; i < 15; i++) {
listeners.push( eventHandler(i) );
}
// trigger an event to all listeners. Each one counts.
for (var j in listeners) {
listeners[j](); // execute listener
}
הפונקציה שנשמרת במערך listeners היא כבר הפונקציה הפנימית והרצתה תגרום לכתיבת הערך המצופה, קרי 0 עד 14.
כיצד ניתן להבטיח שלא תפספסו מקרים פשוטים שכאלו? אם ניחנתם בתסמונת אספרגר - הסיכויים שלכם להתבלבל בפרטים טכניים שכאלו היא קטנה למדי. בכל מקרה אחר - אתם יכולים להשתמש ב JSLint שייתן אזהרה ברורה. אומרים שעם הותק בג'אווהסקריפט הטעויות הללו הולכות ונעלמות - אני עוד לא הגעתי לשם.
שימו לב שיצרנו פה מבנה בעל מורכבות, על מנת לרתום את ג'אווהסקריפט לצורכינו.מפתחי OO בטח יזהו פה את הפוטנציאל בייצוג אובייקטים. בפוסט ההמשך אני מתכוון להמשיך ולהציג עוד כמה מבנים כאלו - עבורי ההתמחות בהן היא הפער התפיסתי הגדול במעבר מג'אווה לג'אווהסקריפט.
מקווה שנהניתם!
פוסט ההמשך בו אדבר על מבנים בג'אווהסקריפט ומשמעותם.
---
[א] קצת אכזרי, אך זו מהמציאות: weddingParticipnatsCounter = weddingParticipantsCounter + 1. ג'אווהסקריפט זיהה משתנה לא מוכר ובמקום להטריד את המתכנת - טרח והגדיר אותו בשבילו. איזה יופי!
IDE טוב היה מספק value highlighting שאולי, היה יכול לעצור מוקדם את התקלה.
[ב] האם זו טעות תכנונית שתעלה למשק העולמי 2 מיליארד דולר?
כרגיל, פוסט טוב.
השבמחקהיה ראוי להזכיר בהקדמה 2 דברים:
1. Node.js והתמיכה ההולכת וגוברת שיש לו (אפילו Microsoft תומכת בו בAzure)
2. הסיבה ש-JS תופסת היא שהיא הדרך שדורשת הכי פחות מתחנות הקצה. Jeff Atwood ניסח את Atwood Law כבר ב97, שקובע שכל אפליקציה שיכולה להיכתב בJS, בסופו של דבר תיכתב בJS.
לגבי w3schools, תמיד הרגשתי שהביקורת נגדם מוגזמת ונובעת מבחירת השם הלא-אתית שלהם. כמעט כמו כל דבר שעולה בחיפושים לא מושכלים בגוגל, הם טובים אך ורק לsyntax. כל דבר הנוגע בתאוריה, או בדעות מומחה (most developers agree...), רצוי לחפש במקומות קצת יותר רצינים מאשר העמוד הראשון בגוגל.
תודה מוטי על התגובה ועל המידע המעניין.
השבמחקלגבי w3schools - אני נוטה להסכים שיש נגדם הרבה מאוד ביקורת, אבל אחרי שנתקלתי בעצמי בתיאור שגוי של API דיי בסיסי בjavaScript - אני יכול להבין אותה.
,
תודה על המבוא המצויין.
השבמחקעד עכשיו פיתחתי בעיקר ב- C# ומזמן רציתי להיכנס ל-javascript.
מצאתי ספר מומלץ בהרבה פורומים בשם Javascript the Good Parts.
המבוא שלך יחסוך לי הרבה זמן.
מחכה להמשך...
היי רון,
מחקאכן הספר הוא בין הספרים הידועים.
שיהיה בהצלחה!
פ.ס.: אני עובד על החלק השני.. אך ה finishing לוקח זמן. אני מקווה לפרסם סופשבוע הקרוב.
שלום ליאור,
השבמחקפוסט מעולה!
לגבי ההבדל בין JavaScript לבין Pascal: אל פניו הייתי אומר שב- Pascal, החיוב להגדיר את כל הלוקליים בראש הפונקציה נראה כמו החלטה שרירותית אשר נובעת בין השאר מעצלנות של כותבי ה- Compiler, ואילו ב- JavaScript מדובר בהמלצה שנובע מ- Feature של השפה (ה- Hoisting).
החלטות שרירותיות,או כאלה שנראות שרירותיות, הן מרגיזות ובצדק.
מצד שני, וכאן אני מאוד נזהר כי אין לי נסיון כלל ב- JavaScript, נראה לי כותבי ה- Inteperter של JavaScript התעצלו כפליים!
Hoisting לא נראה לי כמו Feature של השפה, כלומר לא מדובר במנגנון שנותן כוח משמעותי. מדובר בעיקר בגיבנת: בסופו של דבר, נראה שהחבר'ה פשוט עשו לעצמם הנחה רצינית בשלב ה- Parsing וה- Lexical Analysis (שיש לשהם זכות קיום אפילו אם אין Compiler)
בעוד שה- Compiler של Pascal ו- C נותן הודעת שגיאה, JavaScript אומרת ש-"כעקרון זה לא טוב, אבל לא היה לנו כוח לממש את זה ב- interperter, בהצלחה :-)"
היי ויטלי,
מחקאני מסכים עם כל מילה! לא יכולתי לתאר זאת בצורה יותר מדוייקת.
תודה!
תודה רבה בדיוק מה שחיפשתי, יש עצות לגבי סביבת פיתוח טובה לעבוד איתה ב -JS?
השבמחקvisual studio לא בדיוק מספק את הסחורה ולא מצאתי תוספים טובים
יש. קרא את סוף פוסט ההמשך. הוספתי לינק בסוף הפוסט הזה.
מחק