ירושה אב טיפוסית
מלבד null ו-undefined, לכל סוג נתונים פרימיטיבי יש אב טיפוס, מעטפת אובייקט תואמת שמספקת שיטות לעבודה עם ערכים. כשמפעילים חיפוש של method או של מאפיין בפרימיטיב, JavaScript עוטפת את הפרימיטיב מאחורי הקלעים ומפעילה את ה-method או מבצעת את חיפוש המאפיין באובייקט העטיפה במקום זאת.
לדוגמה, למחרוזת ליסטרית אין שיטות משלה, אבל אפשר להפעיל עליה את השיטה .toUpperCase() באמצעות מעטפת האובייקט המתאימה String:
"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL
התהליך הזה נקרא ירושה פרוטוטיפית – ירושה של מאפיינים ושיטות מה-constructor התואם של הערך.
Number.prototype
> Number { 0 }
> constructor: function Number()
> toExponential: function toExponential()
> toFixed: function toFixed()
> toLocaleString: function toLocaleString()
> toPrecision: function toPrecision()
> toString: function toString()
> valueOf: function valueOf()
> <prototype>: Object { … }
אפשר ליצור פרימיטיבים באמצעות המשתנים האלה, במקום רק להגדיר אותם לפי הערך שלהם. לדוגמה, שימוש ב-constructor String יוצר אובייקט מחרוזת, ולא מחרוזת לטיטרלית: אובייקט שמכיל לא רק את ערך המחרוזת שלנו, אלא גם את כל המאפיינים והשיטות שעברו בירושה מה-constructor.
const myString = new String( "I'm a string." );
myString;
> String { "I'm a string." }
typeof myString;
> "object"
myString.valueOf();
> "I'm a string."
ברוב המקרים, האובייקטים שמתקבלים מתנהגים כמו הערכים שבהם השתמשתם כדי להגדיר אותם. לדוגמה, למרות שהגדרת ערך מספרי באמצעות ה-constructor new Number יוצרת אובייקט שמכיל את כל השיטות והמאפיינים של אב הטיפוס Number, אפשר להשתמש באופרטורים מתמטיים באובייקטים האלה בדיוק כמו שמשתמשים בהם בספרות מספריות:
const numberOne = new Number(1);
const numberTwo = new Number(2);
numberOne;
> Number { 1 }
typeof numberOne;
> "object"
numberTwo;
> Number { 2 }
typeof numberTwo;
> "object"
numberOne + numberTwo;
> 3
בדרך כלל לא תצטרכו להשתמש בקונסטרוקטורים האלה, כי ב-JavaScript יש ירושה פרוטוטיפית מובנית, ולכן אין להם יתרון מעשי. יצירת פרימיטיבים באמצעות קונסטרוקטורים עלולה גם להוביל לתוצאות לא צפויות, כי התוצאה היא אובייקט ולא לטרל פשוט:
let stringLiteral = "String literal."
typeof stringLiteral;
> "string"
let stringObject = new String( "String object." );
stringObject
> "object"
המצב הזה עלול לסבך את השימוש באופרטורים מחמירים להשוואה:
const myStringLiteral = "My string";
const myStringObject = new String( "My string" );
myStringLiteral === "My string";
> true
myStringObject === "My string";
> false
הוספת פסיק אוטומטית (ASI)
במהלך ניתוח הסקריפט, מפרשי JavaScript יכולים להשתמש בתכונה שנקראת הוספה אוטומטית של פסיקיים (ASI) כדי לנסות לתקן מקרים של פסיקיים שהושמטו. אם מנתח ה-JavaScript נתקל באסימון שאסור, הוא מנסה להוסיף פסיק לפני האסימון הזה כדי לתקן את שגיאת התחביר הפוטנציאלית, כל עוד אחד או יותר מהתנאים הבאים מתקיימים:
- הטוקן הזה מופרד מהטוקן הקודם באמצעות קו שבר.
- הטוקן הזה הוא
}. - הטוקן הקודם הוא
), והנקודה-הפסיק שתתווסף תהיה נקודה-הפסיק הסופית של משפטdo…while.
מידע נוסף זמין בכללים של ASI.
לדוגמה, השמטת נקודות-פסיק אחרי ההצהרות הבאות לא תגרום לשגיאת תחביר בגלל ASI:
const myVariable = 2
myVariable + 3
> 5
עם זאת, ASI לא יכול להביא בחשבון כמה הצהרות באותה שורה. אם כותבים יותר מטענה אחת באותה שורה, חשוב להפריד ביניהן באמצעות נקודתיים:
const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier
const myVariable = 2; myVariable + 3;
> 5
ASI הוא ניסיון לתיקון שגיאות, ולא סוג של גמישות תחבירית שמובנית ב-JavaScript. חשוב להשתמש בנקודות פסיק במקומות המתאימים כדי לא להסתמך על הכלי כדי ליצור קוד תקין.
מצב קפדני
הסטנדרטים ששולטים באופן שבו כותבים JavaScript התפתחו הרבה מעבר לכל מה שנדון בתכנון המוקדם של השפה. כל שינוי חדש בהתנהגות הצפויה של JavaScript חייב להימנע מיצירת שגיאות באתרים ישנים יותר.
ב-ES5 פותרות בעיות קיימות בסמנטיקה של JavaScript בלי לשבור הטמעות קיימות, באמצעות 'מצב קפדני' – דרך לבחור קבוצה מגבילה יותר של כללי שפה לתסריט שלם או לפונקציה בודדת. כדי להפעיל את המצב הקפדני, משתמשים במחרוזת המילולית "use strict", ואחריה נקודה-פסיק, בשורה הראשונה של סקריפט או פונקציה:
"use strict";
function myFunction() {
"use strict";
}
המצב המחמיר מונע פעולות מסוימות 'לא בטוחות' או תכונות שהוצאו משימוש, גורם להצגת שגיאות מפורשות במקום שגיאות 'שקויות' נפוצות, ואוסר על שימוש בסינטקס שעלול להתנגש עם תכונות עתידיות של השפה. לדוגמה, החלטות עיצוב מוקדמות לגבי היקף המשתנה הגדילו את הסבירות לכך שמפתחים יגרמו בטעות ל'זיהום' ההיקף הגלובלי כשהם מכריזים על משתנה, ללא קשר להקשר שמכיל אותו, על ידי השמטת מילת המפתח var:
(function() {
mySloppyGlobal = true;
}());
mySloppyGlobal;
> true
סביבות זמן ריצה מודרניות של JavaScript לא יכולות לתקן את ההתנהגות הזו בלי להסתכן בקריסה של כל אתר שמסתמך עליה, בטעות או בכוונה. במקום זאת, ב-JavaScript מודרני אפשר למנוע זאת על ידי מתן אפשרות למפתחים להפעיל מצב קפדני לעבודה חדשה, והפעלת מצב קפדני כברירת מחדל רק בהקשר של תכונות שפה חדשות שלא יפריעו להטמעות מדור קודם:
(function() {
"use strict";
mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to