שפת תכנות זה כמו לאהוד קבוצת כדורגל - זה לא משנה את מי אתם אוהבים, אתם תמיד תתמכו ותצאו להגנת הקבוצה שלכם לא משנה מה(עכשיו שאני חושב על זה, אז אם זה היה נכון אז Java הייתה מכבי חיפה, .NET הייתה הפועל תל אביב, PHP הייתה מכבי תל אביב, ונראה לי שJavascript הייתה מכבי פתח תקוה). באותם ימים רחוקים שהייתי צעיר ויפה (או רק צעיר) אני זוכר את אותם וויכוחים בלתי נגמרים בין ציוותי הפיתוח על מה יותר טוב – C++ או Delphi. אני מניח שזה וויכוח בלתי נגמר שבו מתחלפים האנשים והשפות אבל נשאר בגדול אותו דבר.
מה שמוזר לי זה שאני משוחח עם אנשים מהתחום על Javascriptיש לי הרגשה שיש קונצנזוס על כך שזו השפה הנחותה ביותר ומתייחסים אליה כשפת צעצוע שכל ילד יכול ללמוד ולשחק איתה. הטיעון העיקרי שאני שומע זה שזו שפה פונקציונאלית מאוד פשוטה שלא מתקרבת ליכולות של רוב שפות מונחות עצמים. חבר אחד אפילו אמר לי לא להיכנס לשפה הזו כי זו שפה של בני נוער משועממים.
אני מסכים – Javascript היא אכן שפה פשוטה ללמוד אבל זה לא אומר שהיא שפה פשוטה. אני דווקא רואה את זה כדבר חיובי שאפשר לתכנת בה מהר מאוד מבלי לדעת יותר מידי ולהשיג תוצאות יפות מבלי לדעת יותר מידי על היכולות של השפה. ומצד שני מי שיודע את היכולות שלה מסוגל לכתוב קוד מסודר ויעיל בצורה מהירה וטובה.
החלטתי להראות בפוסט הזה איך אפשר לתכנת בJavascript בשימוש של אובייקטים וקונספט של תכנות מונחה עצמים. אני לא טוען שחייבים לתכנת בJavascript תמיד עם שימוש באובייקטים וירושות, אבל זה בהחלט חושב לדעת את היכולות ולהחליט מתי ליישם בהתאם לצרכים.
בפוסט הזה אני אתרכז בעיקר על מחלקות, מתודות ציבוריות ופרטיות ומתודות סטאטיות.
הגדרת מחלקה
בJavascript כל דבר אובייקט, אפילו פונקציות. על מנת להגדיר מחלקה חדשה אנחנו רק צריכים להגדיר פונקציה:
{syntaxhighlighter brush:js} function Player(url){} {/syntaxhighlighter}
עכשיו, על מנת ליצור מופע של המחלקה הזאת אנחנו משתמשים במילת המפתח new:
{syntaxhighlighter brush:js} var player1 = new Player("http://youtube.com/..."); {/syntaxhighlighter}
שורת הקוד הנ"ל תיצור מופע חדש של הפונקציה ותחזיר מצביע לאובייקט. על מנת למנוע בלבול, אם הפונקציה הייתה מחזירה ערך הוא לא היה מושם ב player1. בואו ונביט בדוגמא הבאה:
{syntaxhighlighter brush:js}
function f1(){ return 100;}
var a = f1();
var b = new f1();
{/syntaxhighlighter}
ההבדל בין שתי השורות הוא ש a יהיה שווה 100 בעוד ש b יצביע על מופע חדש של הפונקציה f1/
מתודות ציבוריות
כמו בכל שפה מונחת עצמים, גם ב Javascript ניתן להגדיר מתודות ציבוריות. על מנת לעשות כך אנחנו משתמשים ב prototype:
{syntaxhighlighter brush:js}
function Player(url){}
Player.prototype.start = function(){
//...do some stuff here...
}
Player.prototype.stop = function(){
//...do some stufff here...
}
{/syntaxhighlighter}
לאל מכם שאוהבים להגדיר מחלקה בצורה שכל הקוד נמצא בהגדרה אחת, ניתן גם להגדיר את המחלקה והמתודות כך:
{syntaxhighlighter brush:js}
function Player(url){}
Player.prototype = {
start: function(){
//...do some stuff here...
} ,
stop: function(){
//...do some stuff here...
}
};
{/syntaxhighlighter}
שתי ההגדרות משיגות את אותה התוצאה. כעת אפשר להשתמש במתודות בצורה הבאה:
{syntaxhighlighter brush:js}
var player1 = new Player("http://youtube.com/...");
player1.start();
player1.stop();
{/syntaxhighlighter}
משתנים ציבוריים
על מנת תיצור משתנים ציבוריים נשתמש במילה this על מנת להגדיר אותם בצורה הבאה:
{syntaxhighlighter brush:js}
function Player(url){ this.url = url; }
{/syntaxhighlighter}
כעת ניתן להשתמש בהם באופן הבא:
{syntaxhighlighter brush:js}
var player1 = new Player("http://youtube.com/...");
player1.url = "http://youtube.com/differenturl";
player1.start();
{/syntaxhighlighter}
בדרך כלל וביישום נכון של פיתוח מונחה עצמים עדיף כמה שיותר להימנע ממשתנים ציבוריים, ולהגדיר משתנים פרטיים שהגישה אליהם היא דרך ממשק מבוקר.
משתנים ומתודות פרטיים
על מנת להגדיר משתנה ומתודה פרטיים נפשוט נגדיר אותם בפונקצית הבנאי (constructor נשמע מוזר בעברית, לא?):
{syntaxhighlighter brush:js}
function Player(url){
var m_url = url;
function getDomain(url){
//do some private stuff
}
}
{/syntaxhighlighter}
העניין זה שהם לא נגישים לאף פונקציה מחוץ לבנאי – אין בזה הגיון, לא? אנחנו רוצים לאפשר לפונקציות לגשת אליהם. על מנת לאפשר את זה נשתמש ב closures( יש גבול ליכולת התרגום שלי לעברית – למי שיש הצעה איך אומרים closureבעברית מוזמן לשלוח לי מייל או לרשום פה בתגובות). Closureהינו איזור מוגן של משתנים שנוצר על ידי קינון של פונקציות. על מנת להבין את זה יותר טוב אני אסביר קצת על הגדרת פונקציות.
פונקציות בJavascript מבוססות על שני קונספטים עיקריים – lexically scopeו function-level scope. הראשון מגדיר כי פונקציות רצות בקונטקסט שהם מוגדרות ולא בקונטקסט שהן מבוצעות בו, בשונה משפות רבות אחרות. Function-levelהכוונה שמשתנה שהוגדר בפונקציה לא נגיש לקוד שרץ מחוץ לפונקציה. זו תכונה שכמעט כל השפות ממשות. אם נשלב בין שתי התכונות שהגדרנו כעט נוכל לממש מתודה ומשתנה פרטי ומתודות שיש להן גישה אליהן:
{syntaxhighlighter brush:js}
function Player(url){
var m_url = url;
function getDomain(url){
//do some private stuff
}
//public privileged method
this.isYouTube(){
if( getDomain(m_url) != "youtube.com" ) return false;
return true;
}
}
//public non privileged method
Player.prototype.pause = function(){
//this method cannot access getDomain and m_url
}
{/syntaxhighlighter}
חשוב לדעת את ההבדל בין פונקציות שהוגדרו על ידי prototypeלבין כאלו שהוגדרו בתוך הבנאי. כאשר Javascript רואה פונקציה שהוגדרה עם prototype נוצר רק מופע אחד של הפונקציה בזיכרון והקונטקסט של הפונקציה משתנה באופן דינאמי – כלומר this יהיה שונה בהתאם למי שקרא לפונקציה. לעומת זאת, פונקציות שהוגדרו בבנאי יוצרו כל פעם מחדש שיוצר מופע של המחלקה. כלומר מצד אחד יש לנו אפשרות לממש מתודות ומשתנים פרטיים, אבל מצד שני יש לנו עלות של ביצועים וזיכרון, כך שחשוב שנדע בדיוק איפה ממש חשוב ונחוץ לשמור על משתנים פרטיים.
אמיר הראל הוא Javascript Ninja אשר מייעץ ומפתח לחברות וסטארטפים בכל מה שקשור לפיתוח מתקדם בצד הלקוח (Rich Internet Application).
