JavaScript object basics

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

Prerequisites: Basic computer literacy, a basic understanding of HTML and CSS, familiarity with JavaScript basics (see First steps and Building blocks).
מטרה: הבנת התיאוריה הבסיסית שעומדת מאחורי תכנות מונחה עצמים, כיצד זה מתקשר ל-JavaScript (״רוב הדברים הם אובייקטים״) והבנה כיצד ניתן לעבוד עם אובייקטים ב-JavaScript.

Object - אובייקטים - עקרונות יסוד

אובייקט הוא אוסף של מידע או פונקציונליות (בדרך מכיל מספר של מתשנים ופונקציות, אשר נקראים - מאפיינים (properties) ו-מתודות (Methodes) כאשר ה נמצאים בתוך אובייקט). נסתכל על דוגמא על מנת להמחיש את הנושא.

על מנת להתחיל, הכינו עותק מקומי של oojs.html. קובץ זה מכיל אלמנט של <script> על מנת שבתוכו נכתוב את הקוד שלנו. אנחנו נשתמש בתצורה הזו על מנת להבין את הסינטקס הבסיסי של אובייקטים. יד עם קובץ זה, פתחו את developer tools JavaScript console.

כמו בהרבה דברים ב-JavaScript, יצירת אובייקט לרוב מתחילים עם הגדרת ואתחול של משתנה. נסו להכניס את הקוד הבא לתוך קוד ה-JavaScript שבקובץ ולאחר מכן שמרו ורעננו את הדף:

const person = {};

כעת פתחו את הקונסולה בדפדפן והזינוו person בתוך הקונסולה, ולאחר מכן לחצו על , Enter/Return. אתם אמורים לקבל תוצאה שנראית כך:

[object Object]
Object { }
{ }

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

const person = {
  name: ['Bob', 'Smith'],
  age: 32,
  gender: 'male',
  interests: ['music', 'skiing'],
  bio: function() {
    alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
  },
  greeting: function() {
    alert('Hi! I\'m ' + this.name[0] + '.');
  }
};

לאחר שמירה ורענון של הדף, נסו להכניס את השורות הבאות לתוך הקונסולה:

person.name
person.name[0]
person.age
person.interests[1]
person.bio()
person.greeting()

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

לתשומת לב: אם אינכם מצליחים לגרום לכך לעבוד, נסו להשוות אל מול הקובץ שלנו oojs-finished.html או ראו את דף האינטרנט ופתחו את הקונסולה בדף זה

אובייקט מורכב ממספר רכיבים, כאשר לכל רכיב יש שם   (name ו- age למעלה), וכן יש לכל רכיב value (e.g. ['Bob', 'Smith'] ו- 32). כל צמד של שם/ערך חייב להיות מופרד באמצעות פסיק , והשם והערך בכל צמד חייבים להיות מופרדים עם :. הסינטקס תמיד ייראה בתבנית הזו:

const objectName = {
  member1Name: member1Value,
  member2Name: member2Value,
  member3Name: member3Value
};

הערך של אובייקט יכול להיות כל דבר - במקרה שלנו, האובייקט person, יש לנו מחרוזת, מספר, שני מערכים, ושתי פונקציות. ארבעת הצמדים הראשונים הם פריטי מידע והם נקראים כ-properties של האובייקט. שני הצמדים האחרונים אלו פונקציות שמאפשרות לאובייקט לעשות משהו עם המידע הזה, והם נקראים ה-methods של האובייקט.

אובייקט כמו זה נקרא object literal — אנחנו באופן ליטראלי )מפורש) יצרנו את האובייקט והתוכן שלו. זה בניגוד לאובייקטים שנוצרו ממחלקות (classs) שאשר נראה בהמשך.

זה נפוץ מאוד ליצור אובייקט באמצעות object literal כאשר אנחנו רוצים להעביר סדרה של פריטי מידע קשורים ובמבנה מסויים, לדוגמא, שליחת בקשה לשרת לצורך השמה במאגר המידע. שליחה של אובייקט יחיד היא יעילה יותר מאשר שליחה של כמה פריטי מידע בנפרד, וזה קל יותר לעבוד עם אובייקט מאשר עם מערך, כאשר אנחנו רוצים לזהות כל פריט באמצעות השם שלךו.

Dot notation שימוש בנקודה-

למעלה, אנחנו יכולים לגשת למתודות ולפרופ׳ של האובייקט באמצעות dot notation. שם האובייקט - person - משמש כ-namespace - הוא חייב להיות מוכנס ראשון, על מנת לקבל גישה לכל מה שמוכמס - encapsulated בתוך האובייקט. לאחר מכן אנחנו רושמים נקודה . ואז את הפריט מידע שאנחנו רוצים לגשת אליו - זה יכול שם של פרופ׳ מסויים, או לקרוא לאחת מהמתודות של האובייקט. לדוגמא:

person.age
person.interests[1]
person.bio()

Sub-namespaces

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

name: ['Bob', 'Smith'],

לקוד זה:

name : {
  first: 'Bob',
  last: 'Smith'
},

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

person.name.first
person.name.last

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

name[0]
name[1]

לתצורה הבאה:

name.first
name.last

אחרת המתודות לא יעבדו.

Bracket notation - שימוש בסוגריים מרובעות

יש דרך נוספת לגשת לפרופ׳ של האובייקט - באמצעות שימוש בסוגריים מרובעות. במקום הקוד הבא:

person.age
person.name.first

אנחנו יכולים להשתמש בקוד זה:

person['age']
person['name']['first']

זה אמנם נראה מאוד דומה לדרך שבה אנחנו ניגשים לאיברים במערך, ובעיקרון זה אותו דבר - אך במקום להשתמש במספר אינדקס על מנת לבחור איבר מסויים, כפי שנאחנו עושים במערך, אנחנו משתמשים בשם המקושר לכל ערך. זה לא פלא שלפעמים אובייקטים נקראים associative arrays — they map strings to values in the same way that arrays map numbers to values.

הגדרת פרופ׳ ומתודות לאובייקט -

עד עכשיו ראינו כיצד לאחזר או לגשת לפריטים באובייקט - אך אנחנו יכולים גם לקבוע או לעדכן את הערכים של אותם פריטים ואף ליצור פרופ ומתודות חדשים, באמצעות הצהרה על אותו פרופ׳ או מתודה שאנחנו רוצים לעדכן או קבוע - (באמצעות שימוש בנקודה אובסוגריים המרובעות)ת כך:

person.age = 45;
person['name']['last'] = 'Cratchit';

נסו להזין את השורות למעלה ואז לגשת לערכים ששיניתם באמצעות הקוד על מנת לראות השינוי:

person.age
person['name']['last']

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

person['eyes'] = 'hazel';
person.farewell = function() { alert("Bye everybody!"); }

וכעת בדקו את הפרופ׳ והמתודה החדשה כך:

person['eyes']
person.farewell()

היבט מאוד שימושי של שימוש בסוגריים מרובעות הוא שזה לא רק מאפשר לנו לקבוע את הערכים של אותו פריט בצורה דינאמית, אלא גם את השם של אותו פרופ׳ . נניח שאנחנו רוצים שהמשתמשים יוכלו לאחסן ערכים שונים בתוך המידע, באמצעות One useful aspect of bracket notation is that it can be used to set not only member values dynamically, but member names too. Let's say we wanted users to be able to store custom value types in their people data, by typing the member name and value into two text inputs. We could get those values like this:

let myDataName = nameInput.value;
let myDataValue = nameValue.value;

We could then add this new member name and value to the person object like this:

person[myDataName] = myDataValue;

To test this, try adding the following lines into your code, just below the closing curly brace of the person object:

let myDataName = 'height';
let myDataValue = '1.75m';
person[myDataName] = myDataValue;

Now try saving and refreshing, and entering the following into your text input:

person.height

הוספת פרופ׳ לאובייקט באמצעות המתודה לעיל אינה אפשרית כאשר אנחנו משתמשים ב-dot notation, כי היא מקבלת רק ערך ליטרלי ולא ערך של משתנה מסויים שמצביע על אותו שם.

מה זה "this"?

אולי שמתם לב למשהו קצת מוזר בתוך המתודות שלנו. ראו את הדוגמא הבאה:

greeting: function() {
  alert('Hi! I\'m ' + this.name.first + '.');
}

אתם בטח שואלים את עצמכם מה זה "this". המילה השומרה this מתייחסת לאובייקט שהקוד נכתב בתוכו - אז במקרה הזה המילה this שווה ל- person. אז מדוע לא לרשום person במקום? כפי שתראו במאמר שלנו בנושאObject-oriented JavaScript מתחילים - כאשר אנחנו יוצרים קונסטרקטורים, המילה this היא מאוד שימושית - היא תבטיח לנו תמיד שהערכים הנכונים מושמים בכל פעם שהמופעים של האובייקט פפפפפ נוצרים - גלומר - אובייקטים שנוצרו - 999999, יהיו בעלי ערכים שונים article, when we start creating constructors and so on, this is very useful — it will always ensure that the correct values are used when a member's context changes (e.g. two different person object instances may have different names, but will want to use their own name when saying their greeting).

ננסה לפשט ולהסביר באמצעות יצירת שני אובייקטים של person:

const person1 = {
  name: 'Chris',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

const person2 = {
  name: 'Brian',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

במקרה הזה, person1.greeting() יציג "Hi! I'm Chris.";ואילו person2.greeting() יציג "Hi! I'm Brian.", למרות שהקוד של המתודה הוא זהה לחלוטין. כפי שאמרנו למעלה, this שוה בעצם לאובייקט שהקוד נמצא בתוכו - this לא משמעותי ושימושי כאשר אנחנו יוצרים אובייקטים באמצעות אובייקט ליטראלי, אבל הוא מאוד שימושי כאשר אנחנו יוצרים אובייקטים באופן דינאמי (באמצעות קונסטרקטורים לדוגמא). זה יהיה ברור יותר בהמשך הדרך, מבטיחים.

השתמשנו באובייקטים לאורך כל הזמן הזה

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

כאשר אנחנו משתמשים במתודות של מחרוזות כאלו לדוגמא:

myString.split(',');

אנחנו בעצם מתשמשים במתודה שזמינה לנו באמצעות האובייקט הגלובלי String. בכל פעם שאנחנו יוצרים מחרוזת בקוד שלנו, מחרוזת זו באופן אוטומטי נוצרת כמופע/מודל/דוגמא של String, ולכן יש לה מספר מתודות ופרופ׳ אשר זמינות לה.

כאשר אנחנו ניגשים ל-document object model אמצעות הקוד הבא:

const myDiv = document.createElement('div');
const myVideo = document.querySelector('video');

אנחנו מתשמשים במתודות שזמינות לנו עבור מופע של ה-Document. לכל דף אינטרנט שמועלה, נוצר מופע של Document, אשר נקרא documentוהוא מייצג את כל המבנה, תוכן ומאפיינים נוספים של אותו דף (כמו לדוגמא ה-URL שלו). וזה אומר שיש לו מספר מתודות ופרופ׳ שזמינים עבור אותו מופע זה.

אותו הדבר בערך קורה עם הרבה אובייקטים מובנים/api שאנחנו משתשמים בהם — Array, Math, וכך הלאה.

שימו לב שbuilt in Objects/APIs לא תמיד יוצרים מופעים של אובייקט באופן אוטומטי. לדוגמא, ה- Notifications API — אשר מאפשר לדפדפנים מודרניים להזרים התראות מערת - דורש מאיתנו לייצר מופע אובייקט חדש באמצעות קונסטקטור, עבור כל התראה שנרצה להזרים. נסו להזין את הקוד הבא בקוסולה: :

const myNotification = new Notification('Hello!');

אנו נסתכל על קונסרקטורים במאמרים הבאים.

לתשומת לב: זה מאוד שימושי לחשוב על הדרך שבה אובייקטים מתקשרים כ- message passing — כאשר אובייקט אחד צריך אובייקט אחד על מנת לבצע פעולה מסויימת, הוא לרוב ישלח הודעה לאותו אובייקט באמצעו תאחת המתודות שלו ויחכה לתגובה, שהיא בעצם ה- return value.

לסיכום

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

במאמר הבא אנחנו נסתכל על תיאוריית object-oriented programming (OOP), וכיצד טכניקות שכאלו יכולות לשמש אותנו ב- JavaScript.

במודול זה