מושגים בסיסיים ב-JS: סְקוֹפּ Scope
- תוכן העניינים:
⊙ שני אבות טיפוס של סקופים
⊙ הסקופ המקומי
⊙ סגירת מעגל - closure
⊙ לסיכום
הנושא של סקופים בג'אווהסקריפט הוא בסיסי ועם השלכות על כל שלב ברמת הכתיבה בה. הפוסט הזה לא מכסה את הנושא בכלל, אבל יכול לתת כיוון טוב להתחלה >>>
סקופ. בעברית - סוג של 'תחום' או 'מרחב'.
סקופים הם - בעיקר - הדרך בה JS מחלקת את הגישה של קטעים (בלוקים) בקוד - למשאבים מסוימים כגון משתנים (או פונקציות או אובייקטים וכו') בשעת הריצה שלהם.
כלומר לכל בלוק שהוא - ישנם כללים אחרים הקובעים לאילו משתנים (שנמצאים באיזו רמה בקוד) תהיה לו גישה כשהוא רץ.
הרעיון שמאחורי החלוקה של הקוד לסקופים שלכל אחד יש גישה מוגבלת למשאבים היא בראש ובראשונה כמובן בטיחות. כמו בכל דבר בתכנה ובמחשב - כמה שפחות בידיים של מישהו אחד - יותר טוב.. הצורה הנכונה של חלוקת גישה היא שלכל בעל תפקיד תהיה גישה אך ורק למשאבים הקשורים לתפקיד שלו. וכך ב-JS כל פונקציה/סקופ אמורים לקבל גישה רק למשאבים שקשורים אליהם.
החלוקה הזו לסקופים גם עוזרת לגלות שגיאות בקוד, כאשר המפתח יודע לאיזה קטע קוד יש גישה למשתנה המסוים שממנו נובעת התקלה.
שני אבות טיפוס של סקופים
מעל כל החלוקה עומדים שני הסוגים הראשיים:
- סקופ גלובלי
- סקופ 'מקומי' (לוקלי - local)
הסקופ הגלובלי הוא בעצם כל מה שקורה ונעשה מחוץ לכל פונקציה שהיא - בקטע הקוד הראשי. או שהוצהר כך ידנית - אם בדפדפן שם האובייקט הגלובלי נקרא window
אז אפשר להצהיר על משתנה גלובלי לדוגמא - 'window.name = 'moshe
או ב-nodeJS שבה אפשר לבצע globalThis
.
הסקופ המקומי שייך לסקופ הפנימי בו הוא רץ. והוא לא רלוונטי ולא ידוע לסקופים האחרים (גם לא לגלובלי - אלא אם כן הוצהר כך בכוונה).
דוגמא פשוטה למשתנה בסקופ פנימי של פונקציה שלא מוכר לסקופ אחר >
function naming() {
var name = 'moshe'
console.log(name) // 'moshe'
}
console.log(name) // <empty string>
המשתנה name
אינו מוכר כלל לסקופ הגלובלי בעוד אם היינו מריצים את הפונקציה naming
הוא היה מדפיס 'moshe'
.
הסקופ המקומי
לכל בלוק של פונקציה נוצר סקופ אישי מקומי כאשר היא רצה (כלומר כאשר היא נקראת. בכל פעם שהפונקציה נקראת מחדש - נוצר סקופ חדש לריצה הנוכחית שלה).
העניין הוא חלק מהנושא של Hoisting - הדרך בה הקוד של JS רץ (כלומר איך המנוע שמריץ את JS מפרש אותו, יוצר את האובייקטים בזיכרון ומריץ את הפרוצדורה).
Hoisting הוא נושא מורכב יחסית ומסובך אבל בנוגע אלינו - כחלק מהמודל ההרצה הזה, לכל פונקציה בקוד נוצר סקופ כאשר היא רצה - כאשר הפונקציה נקראת.
כך שפונקציה שנקראת כמה פעמים ובתוכה יש משתנים - המשתנים שונים זה מזה וזמינים בכל פעם רק לפונקציה הנוכחית שהם רצים בה. היא הסקופ המקומי שלהם.
במקרה של בלוק שיוצר סקופ מקומי, שנמצא בתוך סקופ מקומי (לדוגמא פונקציה שנכתבת בתוך פונקציה) - לסקופ הפנימי תהיה גישה למשתנים ואובייקטים מהסקופ שמעליו - אבל לחיצוני לא תהיה גישה לפנימי. דוגמא >
function out() {
var firstname = 'moshe'
function inn() {
console.log(firstname) // 'moshe'
var lastname = 'cohen'
}
console.log(lastname) // undefined
}
הרעיון באופן כללי הוא שלא תהיה גישה של המידע מבחוץ, אלא מבפנים. ככל שנעמיק יותר מבחינת סקופים - ככל שהסקופ יהיה יותר פנימי - תהיה לו יותר גישה למשתנים, וסקופים שמעליו לא יוכלו לגשת אל משתנים שבתוכו.
סקופ של בלוק: גם לבלוק שלא נמצא בתוך פונקציה (פשוט קטע קוד תחום בסוגריים מסולסלים
{}
) יש סקופ עם משמעות כלפי משתנים שהוצהרו ב-let
אוconst
.
הם יהיו זמינים לבלוק הנוכחי בלבד.
כאן כתבתי קצת עלlet
ו-const
.
סגירת מעגל - closure
התהליך בו פונקציות פנימיות יותר מקבלות את המשאבים של הפונקציות החיצוניות להן, בעוד החיצוניות מודרות מהמידע של הפנימיות - ובאופן כללי התהליך בו המידע מועבר לפונקציות פנימיות - מכונה 'סגירת מעגל' זוהי בצורה פשוטה לניסוח: שיטה בה כאשר נוצר בלוק נוסף בתוך בלוק קיים (לא גלובלי) הבלוק הפנימי יותר יהיה בעל גישה למידע של החיצוני.
לסיכום
הנושא של סקופים ב-JS הוא לא כזה מסובך כשמבינים את העיקרון.
יש עוד מה ללמוד בנושא - כגון הסטטוס של האובייקט this
בכל מצב, וגם השליטה בסקופ של פונקציה באמצעות call
או bind
.
הערות ושאלות למטה...