[עמוד ראשי]   [נושא קודם]
אוביקטים
1. כללי
אוביקטים הם אחד מסוגי הנתונים הבסיסים והחשובים ביותר ב-JS.

אוביקטים ותכונות

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

יצירת אוביקטים

יצירת אוביקט נעשית ע"י האופרטור new ואחריו שם של פונקצית בנאי אשר מאתחלת את האוביקט.
לדוגמא, ניתן ליצור אוביקט ריק ( אוביקט ללא תכונות) כך :
var o = new Object();
JS תומכת בפונקציות בנאי אחרות, המאתחלות אוביקטים חדשים בדרכים , פחות טריויאליות, אחרות.
לדוגמא, כמו שניתן ליצור אוביקט פונקציה ע"י בנאי ה-( )Function.
דרך נוספת ליצירת אוביקט היא ע"י object literal .
object literal מאפשר לשבץ את תיאור האוביקט באופן מילולי בקוד ה-JS בדרך דומה לזו שמשבצים נתון טקסטואלי בקוד, כמחרוזת במרכאות.
object literal מורכב מרשימת תכונות, מופרדות ע"י פסיקים בתוך סוגריים מסולסלים.
כל תכונה מורכבת משם , נקודותיים וערך. לדוגמא :
var circle = { x:0, y:0, radius:2 }
var  homer =
   {
     name: "homer simpson",
     age: 34,
     married: true,
     occupation: "plant operator",
     email: homer@simpson.com
   };
גישה לתכונות האוביקט

גישה לערך של תכונה של אוביקט נעשית ע"י אופרטור נקודה(.).
הערך מצד שמאל של הנקודה צריך להיות reference (מצביע) לאוביקט.
הערך מצד ימין של הנקודה צריך להיות השם של התכונה, שחייב להיות מזהה, לא מחרוזת או ביטוי.
למשל : גישה לתכונה p באוביקט o נעשית ע"י o.p.
תכונות האוביקט עובדות באופן דומה למשתנים: ניתן לאחסן ערכים בתוכם ולקרוא ערכים מהם. לדוגמא :
יצירת אוביקט. איחסון reference לאוביקט במשתנה.
var book = new Object();
קביעת תכונה של האוביקט
book.title = "JavaScript: The Definitive Guide";
קביעת תכונות נוספות
book.chapter1 = new Object();
book.chapter1.title = "Introduction" ;
book.chapter1.pages = 25;                                                                              
book.chapeter2 = {title: "Lexical Structure", pages:10};
קריאת ערכי תכונות מהאוביקט
alert("Outline: "+ book.title + "\n\t" +
      "Chapter 1 " + book.chapter1.title + "\n\t" +
      "Chapter 2 " + book.chapter2.title);


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

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

במידה ונעשה ניסיון לקרוא ערך של תכונה שלא קימת יתקבל ערך JS מיוחד : undefined.
בגרסא 1.2 ניתן להשתמש באופרטור delete על מנת למחוק תכונה של אוביקט :
delete book.chapter2;
בגרסאות קודמות ל – 1.2 לא היתה דרך למחוק תכונה.
1.0 ו- 1.1 אופרטור ה-delete מציב null בתכונה, דבר שניתן לעשות ע"י השמה :
book.chapter2 = null;
ניתן להציב בתכונה גם את הערך undefined ע"י הצבה :
book.chapter2 = book.no_such_property;
יש לשים לב שהצבה של הערך undefined אינה זהה למחיקה, התכונה עדין קימת, היא רק מכילה את הערך undefined.
2. בנאים
בתכנות מונחה עצמים נוהגים לעבוד עם סוגי אוביקטים שמוגדרים ע"י התוכנית.
לדוגמא, בתוכנית שעובדת עם מלבנים , המלבנים ייוצגו ע"י סוג מיוחד או מחלקה של אוביקט.
לכל מלבן ממחלקה זו יהיו תכונות: width ו-height הנחוצות להגדרת מלבן.
על מנת ליצור אוביקטים עם תכונות אלו יש לכתוב פונקצית בנאי ליצירה ואתחול התכונות באוביקט חדש.
פונקצית הבנאי ב-JS היא פונקציה עם שני מאפינים מיוחדים :
פונקצית הבנאי נקראת ע"י האופרטור new.
הפונקציה מקבלת מצביע לאוביקט חדש ריק ע"י מילת המפתח this, ואחראית לאתחול נכון של האוביקט החדש.

דוגמא :
הגדרת פונקצית בנאי
Function Rectangle(w,h)
{
   this.width = w;
   this.height = h;
}
קריאה לפונקצית הבנאי ליצירת שני אוביקטים מסוג מלבן
var rect1 = new Rectangle(2,4);
var rect2 = new Rectangle(8.5,11);
יש לשים לב שפונקצית הבנאי משתמשת בארגומנטים, על מנת לאתחל את תכונות האוביקט .
הוגדרה מחלקה של אוביקטים ע"י פונקצית הבנאי המתאימה, כל האוביקטים נוצרים ע"י הבנאי ( )Rectangle, עובדה המבטיחה שיתבצע איתחול התכונות width ו- height.
בשל העובדה כי כל פונקצית בנאי מגדירה מחלקה של אוביקטים, חשוב לתת לפונקצית הבנאי שם המתאר את מחלקת האוביקטים שהיא מיצרת.
פונקצית הבנאי בדר"כ אינה מחזירה ערך, ואם כן, האוביקט המוחזר מהווה את ערכו של ביטוי ה-new. במקרה כזה האוביקט בעל הערך this נמחק.
3. מתודות
מתודה היא פונקציה של JS הנקראת דרך אוביקט.
אם קימת פונקציה f ואוביקט o ניתן להגדיר מתודה בשם m כך : o.m = f
ניתן לקרוא למתודה כך : o.m( )
או לחילופין, אם m מקבלת שני ארגומנטים o.m(x, x+2)
האוביקט דרכו נקראת המתודה מתפקד כערך של מילת המפתח this בגוף המתודה. כל פונקציה המשמשת כמתודה מעבירה ארגומנט נוסף, האוביקט דרכו היא נקראת.
בדר"כ מתודה מבצעת פעולות קצרות על האוביקט , לכן תחביר הקריאה למתודה הוא דרך לבטא את העובדה שהפונקציה פועלת על אוביקט. דוגמא :
rect.setSize(x,y);
setRectSize(rect,x,y);
שתי השורות הנ"ל מבצעות את אותן פעולות על האוביקט rect , אך תחביר הקריאה למתודה מראה יותר את הרעיון כי האוביקט rect הוא המטרה של הפעולה.
למעשה, אין הבדל גדול בין מתודות לפונקציות.
כאשר קוראים לפונקציה, למעשה קוראים למתודה של אוביקט כללי. בתוך פונקציה כזו, מילת המפתח this מתיחסת לאוביקט כללי, לכן, איו הבדל טכני בין מתודות לפונקציות.
ההבדל האמיתי הוא בעיצוב ובתכלית, מתודות נכתבות על מנת לפעול בצורה כלשהי על אוביקט ה-this, בעוד שפונקציות בדר"כ עומדות בעד עצמן ואינן משתמשות באוביקט this. דוגמא :
יצירת פונקציה
function compute_area()
{
  return this.width * this.height;
}
יצירת אוביקט חדש מסוג Rectangle- שימוש בבנאי שהוגדר קודם
var page = new Rectangle(8.5,11);
הגדרת מתודה ע"י הצבת הפונקציה לתוך תכונה של אוביקט
page.area = compute_area();
קריאה למתודה החדשה
var a = page.area();
לפני הקריאה למתודה area( ) עבור האוביקט rect , יש להציב את המתודה כתכונה של האוביקט. לאחר הצבה זו, לא ניתן לקרוא למתודה עבור אוביקטים אחרים, לפני שהתבצעה ההצבה הזו עבורם.
4. אבטיפוס וירושה
1.1 JS - ב מופיע לראשונה המושג prototype object.
לכל אוביקט יש אבטיפוס, כאשר האוביקט יורש את כל תכונותיו ממנו. כלומר, כל התכונות של אוביקט האבטיפוס יהוו תכונות של האוביקטים שיורשים אותו.
על מנת להגדיר את אוביקט האבטיפוס עבור מחלקה של אוביקטים, מציבים את הערך של תכונת prototype של פונקצית הבנאי להיות האוביקט המתאים, ואז, כאשר אוביקט חדש מאותחל ע"י הבנאי , JS באופן אוטומטי משתמשת באוביקט שהוגדר כאבטיפוס עבור האוביקט החדש.

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

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

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

נניח ומגדירים פונקצית בנאי Circle( ) ליצירת אוביקטים המציגים מעגלים. אוביקט האבטיפוס של מחלקה זו הוא : Circle.prototype ,
לכן ניתן להגדיר קבוע שרלוונטי לכל האוביקטים מסוג מחלקה זו :
Circle.prototype.pi = 3.14159;
אוביקט האבטיפוס של בנאי נוצר באופן אוטומטי ע"י JS . ברוב גרסאות JS, כל פונקציה באופן אוטומטי יוצרת אבטיפוס ריק , למקרה שהיא משמשת כבנאי. דוגמא:

הגדרת מתודת בנאי למחלקה, איתחול תכונות שיהיו שונות בין האוביקטים השונים
function Circle(x,y,r)
{
   this.x = x;
   this.y = y;
   this.r = r;
}
יצירת אוביקט
new Circle(0,0,0);
הגדרת קבוע : תכונה שתהיה משותפת לכל האוביקטים
Circle.prototype.pi = 3
הגדרת מתודה לחישוב היקף המעגל
function Circle_circumference()
{
  return 2* this.pi* this.r;
}
Circle.prototype.circumference = Circle_circumference;
הגדרת מתודה נוספת. הפעם ע"י בנאי ה- Function( ) להגדיר את הפונקציה והצבתה בתכונת אבטיפוס
Circle.prototype.area = 
 new Function("return this.pi * this.r * this.r;");
יצירת מופעים וקריאה למתודות
var c = new Circle(0.0,0.0,1.0);
var a = c.area();
var p = c.circumference();
5. שפה מונחית עצמים
למרות ש-JS תומכת בסוג נתונים של אוביקטים, אין בה מושג פורמלי של מחלקה (class). התפיסה המקובלת בשפות מונחות עצמים היא שהן תומכות בירושה של מחלקות, וכך היא JS . מצד שני, JS עושה שימוש "כבד" באוביקטים, ויש לה סוג אבטיפוס משלה מבוסס על ירושה.
JS היא בעצם כמו שפה מונחת עצמים, היא שואבת השראה משפות מונחות עצמים אחרות שמשתמשות באב טיפוס המבוסס על ירושה במקום מחלקות המבוססות על ירושה. למרות זאת, היא מצליחה לחקות את המאפינים של שפות מבוססות מחלקות יורשות כמו java ו - c++.

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

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

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

משתני מופע-instance variables
כל אובייקט הוא בעל משתנים של ההעתקים שלו, הנוצרו ממנו.
במילים אחרות, אם יש עשרה אוביקטים של מחלקה מסוימת , יש עשרה העתקים לכל משתנה מופע.
בדוגמא של המעגלים לכל אוביקט יש את התכונה r שמגדירה את הרדיוס של המעגל. במקרה זה r הוא משתנה מופע.
מאחר שלכל אוביקט יש את העותק של משתני המופע שלו, הגישה למשתנים אלו נעשית דרך האוביקטים האינדיבידואלים. אם c הוא אוביקט שהוא מופע של מחלקה Circle נתיחס לרדיוס שלו כך : c.r.
כברירת מחדל, כל תכונה של אוביקט ב-JS היא משתנה מופע. על מנת לחקות היטב את תכנות מונחה העצמים, נאמר שמשתני מופע ב-JS אלו התכונות שנוצרו או אותחלו באוביקט ע"י פונקצית הבנאי.

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

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

משתני מחלקה
משתנה מחלקה ב-java הוא משתנה המקושר למחלקה, יותר מאשר לכל מופע שלה. לא משנה כמה מופעים של המחלקה יווצרו, יש עותק אחד לכל משתנה מחלקה.
כמו שניגשים למשתני מופע דרך מופע של מחלקה, ניגשים למשתני מחלקה דרך המחלקה עצמה.
דוגמא למשתנה מחלקה ב-JS היא Number.MAX_VALUE לתכונה MAX_VALUE ניגשים דרך המחלקה Number .
מכיוון שיש רק עותק אחד לכל משתנה מחלקה, משתני מחלקה הם ביסודם משתנים גלובליים.
הם מקושרים למחלקה ויש להם נישה לוגית. מדמים משתנה מחלקה ב-JS ע"י הגדרת תכונה של פונקצית הבנאי בעצמה.
לדוגמא, יצירת משתנה מחלקה Circle.PI :
Circle.PI = 3.14;

מתודות מחלקה
מתודת מחלקה היא מתודה המקושרת למחלקה יותר מאשר למופע שלה. מתודות מחלקה נקראות ע"י המחלקה, יותר מאשר ע"י מופע ספציפי.
מכיוון שמתודות מחלקה לא נקראות ע"י אוביקט, השימוש במילת המפתח this אינו משמעותי.
כמו משתני מחלקה, מתודות מחלקה הן גלובליות. עצם קישורן למחלקה נותן להן נישה לוגית.
על מנת להגדיר מתודת מחלקה, עושים את הפונקציה המתאימה לתכונה של הבנאי.
דוגמא: המחלקה Circle
הגדרת מופעי Circle, משתני מחלקה ומתודות.
בנאי זה מגדיר את המחלקה עצמה
r הוא מופע משתנה. מוגדר ומאותחל בבנאי
function Circle(radius)
{ 
   this.r = radius;
}
Circle.PI הוא משנתה מחלקה, הוא תכונה של פונקצית הבנאי
Circle.PI = 3.14159;
פונקציה המחשבת שטח מעגל
function Circle_area() 
{ return Circle.PI * this.r; }
כאן עושים את הפונקציה למתודת מופע ע"י הצבתה לאובייקט האבטיפוס של הבנאי
function Circle_max(a,b)
{
  if(a.r>b.r)
    return a;
  else 
    return b;
}
מאחר ופונקציה זו מחשבת שני אובייקטים מסוג Circle, לא הגיוני שתהיה מתודת מופע הפועלת על אובייקט יחיד. אך איננו רוצים שתהיה מתודה העומדת בזכות עצמה, לכן נהפוך אותה למתודת מחלקה
Circle.max = Circle_max;
var c = new Circle(1.0); 
    		            // יצירת מופע של המחלקה 
c.r = 2.2;                
  		            // r איתחול משתנה המופע 
var a = c.area();         
  		            // area() קריאה למתודת המופע 
var x = Math.exp(Circle.PI);
 		            // שימוש במשתנה מחלקה בחישוב 
var d = new Circle(1.2);

var bigger = Circle.max(c,d);
		           // max() שימוש במתודת המחלקה