מתארי נכסים

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

קיימים שני סוגים של תיאורים שמשויכים לנכס כלשהו: data descriptors ו-accessor descriptors. מתאר נתונים כולל צמדי מפתח-ערך שמכילים ערך של נכס, בין אם הוא ניתן לכתיבה, להגדרה או לספירה. מתארי הגישה מכילים פונקציות שמופעלות כשמוגדר נכס, משנים אותו או מתבצעת גישה אליו.

נכס סוג מתאר ערך ברירת המחדל מ-
Object.defineProperty()
תיאור
[[Value]] נתונים undefined מכיל ערך של נכס.
[[Writable]] נתונים false המדיניות קובעת אם אפשר לשנות את ערך הנכס.
[[Get]] אביזר undefined הפונקציה getter של הנכס, שמופעלת כאשר מתבצעת גישה לנכס.
[[Set]] אביזר undefined פונקציית setter של הנכס, שמופעלת כאשר מוגדר או השתנה.
[[Configurable]] שניהם false אם הערך הזה הוא false, לא ניתן למחוק את הנכס וגם כדי לא לשנות את המאפיינים שלהם. אם התאריך הוא false וכן [[Writable]] הוא true, ערך הנכס יכול עדיין עשויות להשתנות.
[[Enumerable]] שניהם false אם הערך הוא true, אפשר לחזור על הנכס באמצעות for...in לולאות או הערך הסטטי Object.keys() .

בכל אחד מהמאפיינים האלה נעשה שימוש באותו קיצור דרך של [[Prototype]], שמציין שלא מתבצעת גישה ישירה לנכסים האלה. במקום זאת, משתמשים שיטה סטטית של Object.defineProperty() להגדרה או לשינוי המאפיינים של לאובייקט. Object.defineProperty() מקבל שלושה ארגומנטים: האובייקט שעליו צריך לפעול, את מפתח המאפיין ליצירה או לשינוי, ואובייקט שמכיל את שמתארים את הנכס שנוצרים או משתנים.

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

const myObj = {};

Object.defineProperty(myObj, 'myProperty', {
  value: true,
  writable: false
});

myObj.myProperty;
> true

myObj.myProperty = false;

myObj.myProperty;
> true

לדוגמה, כאשר ל-[[Writable]] יש ערך false, המערכת מנסה להגדיר ערך חדש עבור הנכס המשויך נכשל באופן שקט מחוץ למצב קפדני, ושולח במצב קפדני:

{
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}
> true

(function () {
    "use strict";
    const myObj = {};

    Object.defineProperty(myObj, 'myProperty', {
    value: true,
    writable: false
    });

    myObj.myProperty = false;
    myObj.myProperty;
}());\
> Uncaught TypeError: "myProperty" is read-only

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

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

const myCustomPrototype = {
  'myInheritedProp': 10
};

const newObject = Object.create( myCustomPrototype );

newObject;
> Object {  }
<prototype>: Object { myInheritedProp: 10 }
  myInheritedProp: 10
  <prototype>: Object { … }

Object.create יכול להשתמש בארגומנט שני שמציין את המאפיינים שלו עבור שנוצר לאחרונה באמצעות תחביר שדומה ל-Object.defineProperty() — כלומר, אובייקט שממפים מפתחות לקבוצה של מאפייני תיאור:

const myCustomPrototype = {
  'myInheritedProp': 10
};

const myObj = Object.create( myCustomPrototype, {
        myProperty: {
            value: "The new property value.",
            writable: true,
            configurable: true
        }
  });

myObj;
> Object { … }
    myProperty: "The new property value."
    <prototype>: Object { myInheritedProp: 10 }

בדוגמה הזו, האובייקט החדש (myObj) משתמש בליטרל של אובייקט. (myCustomPrototype) בתור אב הטיפוס שלו, שמכיל בעצמו את הפונקציה שעברה בירושה Object.prototype, והתוצאה היא סדרה של אבות טיפוס שעברו בירושה שנקראים שרשרת אב טיפוס. לכל אובייקט יש אב טיפוס, מוקצה או עבר בירושה, שיש לו אב טיפוס מוקצה או שעבר בירושה משל עצמו. הרשת מסתיימת ב- אב טיפוס של null שאין לו אב טיפוס משלו.

const myPrototype = {
  'protoProp': 10
};

const newObject = Object.setPrototypeOf( { 'objProp' : true }, myPrototype );

newObject;
> Object { objProp: true }
    objProp: true
    <prototype>: Object { protoProp: 10 }
        protoProp: 10
        <prototype>: Object { … }

המאפיינים שנכללים באב הטיפוס של ערך זמינים 'ברמה העליונה' של אובייקט, ללא צורך בגישה ישירה למאפיין האב טיפוס:

const objectLiteral = {
    "value" : true
};

objectLiteral;
> Object { value: true }
    value: true
    <prototype>: Object { … }

objectLiteral.toString();
"[object Object]"

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

const myCustomPrototype = {
  'protoProp': "Prototype property value."
};

const myObj = Object.create( myCustomPrototype, {
    myProperty: {
        value: "Top-level property value.",
        writable: true,
        configurable: true
    }
});

myObj.protoProp;
> "Prototype property value."

בדיקת ההבנה

אילו ממתארים הם פריטי גישה?

[[Get]]
[[Set]]
[[Writable]]