ToCode
Open in Telegram
1 420
Subscribers
No data24 hours
+37 days
-430 days
Posts Archive
1 421
# מתי עוברים לשעון חורף (ועוד כמה טיפים על אזורי זמן)
מכונות יוניקס - ולכן גם לינוקס ומק, שומרות בסיס נתונים של אזורי זמן בתיקיה מיוחדת על המחשב שנקראת
/usr/share/zoneinfo. הקבצים שם הם בינאריים שמתארים את ההפרש של אזור הזמן שמתאים לקובץ, וגם את כל השינויים באזור הזמן ובמיוחד בהיבט של מעבר בין שעון קיץ לחורף. בואו נצא לחקור קצת את הקבצים שם ולשחק עם אזורי זמן.
תחילה רשימת הקבצים בתיקייה, לפחות אצלי על המק:
$ ls -ld /usr/share/zoneinfo/
lrwxr-xr-x 1 root wheel 38 Apr 28 23:44 /usr/share/zoneinfo/ -> /var/db/timezone/tz/2023c.1.0/zoneinfo
$ ls -F /var/db/timezone/tz/2023c.1.0/zoneinfo/
+VERSION CET Eire GMT0 Japan Navajo Turkey leapseconds
Africa/ CST6CDT Etc/ Greenwich Kwajalein PRC UCT posixrules
America/ Canada/ Europe/ HST Libya PST8PDT US/ zone.tab
Antarctica/ Chile/ Factory Hongkong MET Pacific/ UTC
Arctic/ Cuba GB Iceland MST Poland Universal
Asia/ EET GB-Eire Indian/ MST7MDT Portugal W-SU
Atlantic/ EST GMT Iran Mexico/ ROC WET
Australia/ EST5EDT GMT+0 Israel NZ ROK Zulu
Brazil/ Egypt GMT-0 Jamaica NZ-CHAT Singapore iso3166.tab
ואנחנו רואים שהתיקיה מכילה קבצים ותיקיות, לפי המדינה והיבשת. אני לא בטוח למה לנו אין יבשת, אבל לפחות אנחנו לא היחידים. כשננסה להציג את תוכן הקובץ אנחנו עשויים להתאכזב שכן אלה קבצים בינאריים. אפשר להשתמש ב xxd:
$ xxd /usr/share/zoneinfo/Israel| head -4
00000000: 545a 6966 3200 0000 0000 0000 0000 0000 TZif2...........
00000010: 0000 0000 0000 0008 0000 0008 0000 0000 ................
00000020: 0000 0094 0000 0008 0000 0011 9e30 4588 .............0E.
00000030: c859 cf00 c8fa a600 c938 9c80 cce5 eb80 .Y.......8......
הפורמט מתואר בפירוט בתיעוד כאן. הדבר החשוב משם הוא לראות שקובץ כזה כולל הרבה מעבר להפרש השעות. הפקודה zdump מדפיסה את הזמן הנוכחי באזור זמן שמתאים לקובץ מהמאגר:
$ zdump Israel
Israel Fri Sep 8 14:14:43 2023 IDT
$ zdump Europe/Paris
Europe/Paris Fri Sep 8 13:15:03 2023 CEST
$ zdump Chile/Continental
Chile/Continental Fri Sep 8 08:15:45 2023 -03
ועם המתג המתאים היא יכולה להציג את השינויים שקובץ zoneinfo כולל, כלומר המעברים בין שעון חורף לקיץ. זה המצב אצלנו:
$ zdump -v Israel
Israel Fri Dec 13 20:45:52 1901 UTC = Fri Dec 13 23:06:32 1901 JMT isdst=0
Israel Sat Dec 14 20:45:52 1901 UTC = Sat Dec 14 23:06:32 1901 JMT isdst=0
Israel Mon Dec 31 21:39:19 1917 UTC = Mon Dec 31 23:59:59 1917 JMT isdst=0
Israel Mon Dec 31 21:39:20 1917 UTC = Mon Dec 31 23:39:20 1917 IST isdst=0
Israel Fri May 31 23:59:59 1940 UTC = Sat Jun 1 01:59:59 1940 IST isdst=0
Israel Sat Jun 1 00:00:00 1940 UTC = Sat Jun 1 03:00:00 1940 IDT isdst=1
Israel Mon Sep 30 23:59:59 1940 UTC = Tue Oct 1 02:59:59 1940 IDT isdst=1
Israel Tue Oct 1 00:00:00 1940 UTC = Tue Oct 1 02:00:00 1940 IST isdst=0
Israel Sat Nov 16 23:59:59 1940 UTC = Sun Nov 17 01:59:59 1940 IST isdst=0
Israel Sun Nov 17 00:00:00 1940 UTC = Sun Nov 17 03:00:00 1940 IDT isdst=1
Israel Sat Oct 31 23:59:59 1942 UTC = Sun Nov 1 02:59:59 1942 IDT isdst=1
Israel Sun Nov 1 00:00:00 1942 UTC = Sun Nov 1 02:00:00 1942 IST isdst=0
Israel Wed Mar 31 23:59:59 1943 UTC = Thu Apr 1 01:59:59 1943 IST isdst=0
Israel Thu Apr 1 00:00:00 1943 UTC = Thu Apr 1 03:00:00 1943 IDT isdst=1
Israel Sun Oct 31 23:59:59 1943 UTC = Mon Nov 1 02:59:59 1943 IDT isdst=1
Israel Mon Nov 1 00:00:00 1943 UTC = Mon Nov 1 02:00:00 1943 IST isdst=01 421
# איך לזהות הודעה באנגלית תקנית
שאלה 59 בפרויקט אוילר מציגה את האתגר הבא - נתונה הודעה מוצפנת במפתח הצפנה המורכב מ-3 אותיות קטנות בלבד, והמשימה שלנו היא למצוא את הטקסט המקורי.
ברור שבמספרים כל כך קטנים אפשר להריץ בקלות חיפוש Brute Force על כל האפשרויות למפתח ולמצוא את התשובה. האתגר היותר גדול הוא לזהות איזה אפשרות היא נכונה כלומר לזהות מתי ההודעה המפוצחת היא אכן ההודעה המקורית.
הניסיון הראשון שלי (שנכשל) היה לחפש הודעות שמורכבות רק מאותיות. זה נכשל כי ההודעה המקורית כללה גם מספרים ותווים מיוחדים.
בסופו של דבר נזכרתי שיש לי רשימה של כל המילים באנגלית ממש אצלי על המחשב, וגם אצלכם, בקובץ
/usr/share/dict/words. משם הדרך היתה קצרה לחיפוש הודעות בהן לפחות 40% מהמילים הן מילים חוקיות באנגלית מה שהוביל להודעה הנכונה. הפיתרון המלא בפייתון היה:
from pathlib import Path
import string
import sys
from itertools import cycle
english_words = set([c.removesuffix('\n').lower() for c in Path('/usr/share/dict/words').open().readlines()])
message = [int(i) for i in Path('./0059_cipher.txt').open().read().split(',')]
key = [97, 98, 99]
def decrypt(message, key):
return ''.join([chr(i) for i in [p[0] ^ p[1] for p in zip(message, cycle(key))]])
for i in range(ord('a'), ord('z') + 1):
for j in range(ord('a'), ord('z') + 1):
for k in range(ord('a'), ord('z') + 1):
key = [i, j, k]
plaintext = decrypt(message, key)
words = plaintext.split()
valid_words = [w for w in plaintext.split() if w in english_words]
if len(valid_words) / len(words) > 0.4:
print(plaintext)
print(sum(ord(ch) for ch in plaintext))1 421
# בואו נבנה מנגנון הרשאות פשוט עם Decorators בפייתון
ל Decorators יש שתי תכונות טובות - הם מאוד בולטים בחתימה של הפונקציה וקל לזהות אם הפעילו אותם או לא. לכן נוכל להשתמש בהם כדי לבנות מערכת הרשאות פשוטה ולכתוב קוד פייתון מאובטח יותר.
## קצת עבודת תשתית
מערכת ההרשאות שלי צריכה לעשות שני דברים: קודם כל להכריח את המתכנתים והמתכנתות להגדיר איזה הרשאות כל פונקציה בתוכנית צריכה, ודבר שני להגדיר באיזה מקרים או הרצות יש את ההרשאות האלה.
אז נתחיל עם Enum שמגדיר את כל ההרשאות האפשריות, ובשביל המשחק הוא יכלול רק שני סוגים בינתיים:
from enum import Enum, auto
class Permissions(Enum):
NETWORK = auto()
FILESYSTEM = auto()
ועוד פונקציה שבודקת אם יש או אין לתוכנית הרשאות לפעולה מסוימת:
def has_permission(permission: Permissions):
match permission:
case Permissions.NETWORK:
return "--network" in sys.argv
case Permissions.FILESYSTEM:
return False
המימוש גם של רשימת ההרשאות וגם של מנגנון הבדיקה יהיה שונה בתוכניות אמיתיות ופה בחרתי מימוש פשוט רק בשביל הדוגמה.
## הממשק שאני רוצה
עכשיו מגיעים לחלק המעניין והוא להגדיר איך תיראה פונקציה. זה הקוד שהייתי רוצה לכתוב:
@requires_permission(Permissions.NETWORK)
def find_my_ip():
with urllib.request.urlopen('https://api.ipify.org') as response:
data = response.read()
return data.decode('utf8')
שימו לב איך ברור לגמרי איזה הרשאה או הרשאות הפונקציה צריכה כדי לעבוד כבר מצפיה בחתימה של הפונקציה.
## מימוש פונקציית הגדרת ההרשאות
המימוש של ה Decorator הוא די פשוט אחרי ששמנו את הממשק במקום:
def requires_permission(permission: Permissions):
def decorator(f):
def inner(*args, **kwargs):
if not has_permission(permission):
raise Exception("Not Authorized")
return f(*args, **kwargs)
inner.permissions = f.__dict__.get('permissions', []) + [permission]
return inner
return decorator
צריך רק לבדוק לפני כל הפעלה אם יש או אין למשתמש את ההרשאות ורק אם יש הרשאות להריץ את הפונקציה המקורית. בזכות תשתית ה Decorators של פייתון אין בעיה להפעיל מספר Decorators אחד אחרי השני וכך להגדיר כמה סוגים של הרשאות (למרות שאפשר היה בקלות גם לעדכן את הפונקציה כך שתקבל רשימה של הרשאות).
## וידוא הגדרת הרשאות לכל פונקציה
החלק האחרון במערכת מטפל בפונקציות ששכחתי להגדיר להן הרשאות. לא הייתי רוצה שמישהו יכתוב בטעות קוד כזה:
def list_users():
with open('/etc/passwd') as f:
print(f.read())
ואז כולם יוכלו להדפיס תוכן של קובץ בלי הרשאות גישה למערכת הקבצים.
דרך אחת להכריח הרשאות על כל פונקציה היא לרוץ על כל הפונקציות בקובץ ולבדוק שהוגדרו הרשאות עבורן. אני מזכיר שבקוד הדקורייטור שבנה את ההרשאות הוספתי מאפיין permissions לכל פונקציה שכלל רשימה של כל ההרשאות שהיא צריכה. אם מאפיין זה לא קיים אני יודע שמישהו שכח להגדיר לפונקציה הרשאות:
import sys
import inspect
def init():
safe_functions = {'has_permission', 'init', 'requires_permission'}
functions = [(name, obj) for name, obj in inspect.getmembers(sys.modules[__name__])
if inspect.isfunction(obj)
and 'permissions' not in obj.__dict__
and name not in safe_functions]
if len(functions) > 0:
raise Exception(f"Functions {functions} don't define required permissions")
init()
סך הכל בעזרת Decorators הצלחתי להגדיר ממשק פשוט לבניית מערכת הרשאות לתוכנית שלי. אין ספק שהיה עדיף לבנות את המערכת ישירות על הפונקציות המובנות בשפה שניגשות למערכת הקבצים או לרשת, אבל זה כן יכול לתת פיתרון לסקריפטים פשוטים שהרבה אנשים מעדכנים ושרצים בהרבה סביבות שונות.1 421
# למה אתה כל כך פוחד מ Type Hints?
בואו נדבר על שלוש הסתייגויות נפוצות משימוש ב Type Hints בפייתון, ננסה לפרק אותן ולראות למה בכל זאת Type Hints הם הכלי הכי חשוב בכתיבת קוד קל יותר לתחזוקה.
## טייפ הינטס דורשים המון התעסקות
טענה ראשונה נגד Type Hints שאני חושב שגורמת להרבה אנשים לוותר עליהם מראש היא הפחד מהתעסקות יתר - הפחד שבמקום לכתוב קוד אנחנו נתעסק רק בחיפוש הטיפוס הנכון. רגע, זה list או Sequence? ומה ההבדל בין Sequence ל Iterable? ואיך מגדירים Type Hint ל Decorator? ומה עושים עם הפונקציה שצריכה להחזיר דברים מטיפוסים שונים?
מערכת הטיפוסים של פייתון היא די מורכבת. נכון זה לא TypeScript, אבל עדיין יש מה ללמוד. הרבה מאיתנו מרגישים ש Type Hints זה חשוב אבל עכשיו הוא לא זמן טוב ללמוד אותם, או שהם לא שווים את ההשקעה.
אם אתם מדברים עם אנשים שמרגישים כך, שווה להזכיר שאפשר להתחיל להוסיף Type Hints ממש בקטנה, ורק את אלה שאתם מכירים וקל לכם איתם, ולגדול לאט. כשיצטרכו להגדיר טיפוס יותר מתוחכם ויהיה להם זמן, הם יוכלו לחפש עליו (או להתייעץ עם איזה Chat GPT), ועד אז אין בעיה לפזר Type Hints רק במקומות שלא דורשים המון מאמץ. אפילו זה לבד ישפר את ההשלמות האוטומטיות ב IDE וייתן שכבת הגנה נגד טעויות בטיפוסים.
## טייפ הינטס מקלקלים את ה"דינמיות" של השפה
פה אנחנו כבר מגיעים לטיעונים יותר פילוסופיים. יהיו אנשים שירגישו ש Type Hints מפריעים להם לכתוב קוד, כי הם לא רוצים להחליט מראש מה יהיו הטיפוסים של המשתנים. "בואו פשוט נגדיר פונקציה ונראה אחר כך איך יהיה נוח להשתמש בה" הם יגידו.
אבל טיעון כזה לא ממש עובד כי גם בפייתון הטיפוס קיים בעת הגדרת פונקציה בראש של המתכנתים ובקוד המימוש של הפונקציה. להוסיף אותו לחתימה זה רק דרך להיות יותר ברורים לגבי המחשבות שלנו. אם מישהו כותב את הפונקציה:
def sum_digits(n):
sum(int(i) for i in str(n))
גם בלי לכתוב את ה Type Hint הוא הגדיר את הטיפוס של n - זהו משהו שאפשר להמיר אותו למחרוזת עם str ולקבל רצף של דברים שאפשר להמיר כל אחד מהם ל int. הפונקציה לא תעבוד על float-ים או על רשימות, בלי קשר לאם קיים או לא קיים Type Hint שאומר ש n הוא מספר.
## טייפ הינטס רק מסבכים את הקוד בלי לתת ערך
סוג אחרון של טיעונים נגד טייפ הינטס קשור לאופציונאליות שלהם - יהיו אנשים שירגישו שבגלל שאפשר להריץ תוכניות פייתון גם כשאין תאימות לטיפוסים שמוגדרים אז ה Type Hints הם מיותרים, מבזבזים מקום בקובץ ועדיף להשקיע את האנרגיה שלנו בכתיבת תיעוד טוב יותר ובדיקות.
התשובה כאן היא שזה לא סותר והרבה פעמים אפילו הולך יחד. נכון שפייתון עצמה לא אוכפת את תאימות הטיפוסים אבל אנחנו יכולים להחליט לאכוף את זה באמצעות הוספת Commit Hook או מנגנון בדיקה ל CI שלנו שמחייב שהתוכנית תעבור mypy לפני העלאת גירסה. בנוסף ממילא התיעוד והבדיקות יגדירו את הטיפוסים שפונקציות מצפות לקבל, ולכן Type Hints יכולים רק לעזור לכתוב תיעוד טוב יותר.
בשורה התחתונה אני חושב שאחרי שעוברים את עקומת הלמידה סביב Type Hints קשה להסתכל אחורה. הערך שהם נותנים ברור במיוחד ב Refactoring כשאפשר לזהות בקלות את המקומות שמושפעים משינוי חתימה של פונקציה, ובעבודה עם קוד חדש כשאפשר לראות מהר כשמפעילים פונקציה בצורה לא נכונה.
ואם אתם רוצים להתחיל להשתמש ב Type Hints בצורה יותר רצינית בתוכניות שלכם אבל לא בטוחים שיש לכם כח לקרוא את כל התיעוד עליהם או שרוצים לקצר זמנים, תשמחו לשמוע שהקלטתי מיני קורס של שמונה שיעורים על Type Hints שמסכם את כל הטיפים והנקודות החשובות דרך דוגמאות פרקטיות לשימוש ב Type Hints. אפשר לקבל את המיני קורס בקישור הזה במסגרת רכישת מנוי לאתר.1 421
# איך לא לאכול את הלב
״אני לא מאמין שבחרתי באליקסיר לפרויקט ההוא, מה עבר לי בראש? עכשיו אני תקוע ולא מצליח לגייס מתכנתים״
״ידעתי שלא הייתי צריך להסכים לשלב את ה Elastic Search, אני לא מאמין איך איבדנו חצי שנה של פיתוח על בעיות הביצועים שלו״
״פעם אחרונה שאני מוציא פרויקט ל Outsource. ייקח לי נצח להיפטר מכל הקוד הגרוע שקיבלתי״
״איך חבל לי שבזבזתי את הכסף והזמן על קורס פיתוח לאנדרואיד. חצי שנה של לימודים ובסוף לא הצלחתי למצוא בזה עבודה״
לכל אחד ואחת יש חרטות והחלטות שהיינו רוצים שלא יעשו - בקוד וגם בחיים באופן כללי. הבעיה שלפעמים החרטות האלה מתקבעים בתור סיפורים בראש ומונעים מאיתנו להמשיך קדימה או שפוגעים בפרויקטים הבאים. מי לא עבד עם איזה Tech Lead שלא מוכנה לשמוע על בסיסי נתונים גרפיים רק כי בפרויקט קודם כלי כזה הפיל את המערכת בדיוק לפני הדמו הגדול. בעיות קורות, אבל להיתקע איתן זה לא ללמוד מניסיון אלא לחזור עליהן. (ללמוד מניסיון זה להבין מה באמת נשבר באותו פרויקט, שזה בדרך כלל אופן השימוש בכלי או שיטת העבודה ולא בחירה של כלי מסוים).
טריק אחד שעוזר לי לא לאכול את הלב הוא להחליף את הסיפור. במקום להגיד "אני לא מאמין שבחרתי ב X" אני אגיד לעצמי "חשבתי שזה יהיה רעיון מדליק אבל בסוף זה לא היה. מעניין מה קרה שם". סיפור כזה פותח את הדלת לחקירה ולשאלות ומשאיר לי את החופש בבחירת כלים גם לפרויקטים הבאים.
חשבתי שאליקסיר תהיה רעיון מדליק לפרויקט, אבל בסוף היא לא כל כך התאימה.
חשבתי שב Outsource אוכל לחסוך זמן, אבל בסוף התאכזבתי כי לא מצאתי את המפתח הנכון.
חשבתי שזה יהיה רעיון מדליק - אבל בסוף זה לא היה.
1 421
# באיזה פורטים הוא משתמש?
קחו קובץ docker-compose.yml גדול, עם הרבה סרביסים ונסו לענות על השאלה "באיזה פורטים הוא משתמש?". כלומר לא עבור סרביס ספציפי (את זה אני יכול לחפש בקובץ אין בעיה), אלא לכל הסרביסים יחד. חיפוש בעורך הטקסט? אין צורך - אני מכיר yq:
$ yq '.services.[].ports|select(. != null)' < docker-compose.yml| tr -d \" | sed 's/- //' | cut -d: -f1
הפלט הוא רשימה של כל הפורטים ב host שכל הסרביסים בקובץ docker-compose.yml יתפסו.1 421
# בואו נשתמש ב Container Queries כדי לקבוע גודל של טקסט לפי רוחב המיכל
קביעת גודל של דברים באחוזים זה אחד הדברים המבלבלים ב CSS, כי לכל מאפיין המשמעות של האחוזים עשויה להיות שונה. במקרה של
font-size בחירת ערך באחוזים היא יחסית לגודל הגופן של אלמנט המיכל שלו, לכן אם אני כותב:
.text {
font-size: 300%;
}
אני מתכוון שגודל הגופן של אלמנט עם קלאס text הוא פי 3 מגודל הגופן של האלמנט בתוכו הוא נמצא. בלי קשר לרוחב האלמנט בתוכו הוא נמצא.
אם המיכל שלנו ברוחב קבוע ואנחנו יודעים מהו, אפשר לציין את גודל הטקסט ביחידות כמו vw. זה יקבע גודל טקסט ביחס לרוחב המסך, שזה לפעמים מה שאנחנו רוצים. אבל אם המיכל שלי הוא משהו שאפשר להגדיל ולהקטין אותו אז רוחב המסך לא חשוב.
והאמת אפילו אם היתה דרך לקבוע גודל גופן בהתאמה לגודל המיכל, יש עוד הרבה מאוד מאפיינים והתאמות שאולי היינו רוצים לעשות כשהמיכל שלנו משנה את גודלו.
עד לפני מספר חודשים הדרך לעשות את ההתאמות האלה היתה להשתמש ב JavaScript - לתפוס אירועי שינוי גודל על אלמנט המיכל ולשנות את המאפיינים בהתאמה. בעזרת יכולת שנקראת Container Query אנחנו יכולים לקבוע מאפיינים שונים לפריטים לפי גודל המיכל שלהם.
התחביר מאוד דומה ל Media Query ומספק שני פיצ'רים שימושיים:
1. יחידות מידה חדשות, בדומה ל vw ו vh, רק שהפעם יהיו יחסיות לגודל הקונטיינר.
2. שאילתת @container חדשה שמאפשרת להגדיר כללי עיצוב מסוימים אם הקונטיינר שלי מתאים לשאילתה.
בשביל להתחיל לעבוד עם הפיצ'ר אני צריך להגדיר באלמנט הקונטיינר את המאפיין:
container-type: 'size';
או לחלופין עם הערך inline-size כדי להתיחס לגודל ה inline שלו (size מתיחס לגודל שלו בתור בלוק). אחרי הגדרה זו אני יכול להתיחס לרוחב הקונטיינר בתוך העיצוב לדוגמה:
.text {
background: blue;
// cqw -> container width
font-size: 4cqw;
padding: 10px;
}
או להוסיף שאילתה ולהפעיל כלל עיצוב מסוים רק אם הקונטיינר עומד בתנאי:
@container (min-width: 700px) {
.text {
background: orange;
}
}
דוגמה? בטח. נסו את הקודסנדבוקס הבא:
https://codesandbox.io/s/cool-phoebe-kkmpr6
או בהטמעה כאן:
<iframe src="https://codesandbox.io/embed/cool-phoebe-kkmpr6?fontsize=14&hidenavigation=1&theme=dark"
style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
title="cool-phoebe-kkmpr6"
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>
תוכלו לגרור את הפינה הימנית תחתונה של הריבוע כדי להגדיל אותו, ולשים לב ששינוי גודל הריבוע משנה גם את הגודל של הטקסט שבתוכו. אחרי שתעברו את רף ה 700 פיקסלים גם הרקע של הטקסט ישתנה לכתום.1 421
# שימו לב: פלוס שווה ב JavaScript לא באמת מוסיף
הקוד הבא מבלבל:
let x = "hello, "
x += "world"
console.log(x);
הוא עשוי לתת הרגשה שאנחנו שמים מחרוזת בתוך המשתנה x, ואז בשורה השניה משנים את המחרוזת כדי להוסיף לה עוד טקסט. אבל זה לא מה שקורה - בפועל מחרוזות ב JavaScript הן Immutable, ולכן הקוד פשוט מעדכן את x כך שיצביע על המחרוזת החדשה שמורכבת משרשור המחרוזת שקיימת בו לטקסט החדש world.
קל להיווכח בזה באמצעות הוספת משתנה:
> x = "hello, "
'hello, '
> y = x
'hello, '
> x += "world"
'hello, world'
> x
'hello, world'
> y
'hello, '
אם ה += היה משנה את המחרוזת אז היינו רואים את השינוי גם ב y, בדיוק כמו שקורה בעבודה על מערכים:
> x = [1, 2, 3]
[ 1, 2, 3 ]
> y = x
[ 1, 2, 3 ]
> x.push(4)
4
> x
[ 1, 2, 3, 4 ]
> y
[ 1, 2, 3, 4 ]
ולמי אכפת אם += דורס את הערך הקיים במשתנה ומחליף אותו בערך חדש גדול יותר או מוסיף למשהו קיים? זה כבר תלוי במחרוזת. במחרוזת רגילה זה באמת לא משנה, אבל כשהמחרוזת מייצגת אלמנטים ב DOM, שינוי שלה עם += ידרוס את האלמנטים הקיימים ויחליף אותם באלמנטים חדשים. אם היו על האלמנטים הישנים קודי טיפול באירועים הם כבר לא יעבדו (כי האלמנטים "הישנים" כבר לא על המסמך).
דוגמה? תמיד. הקוד הבא מייצר כפתור שבלחיצה עליו יקפיץ הודעת alert:
const main = document.querySelector('main');
main.innerHTML += '<button>button 1</button>';
main.querySelector('button').addEventListener('click', () => {
alert('1');
})
ועכשיו אני לוקח את אותו קוד ומוסיף לו שורה:
const main = document.querySelector('main');
main.innerHTML += '<button>button 1</button>';
main.querySelector('button').addEventListener('click', () => {
alert('1');
});
main.innerHTML += '<p>Click for magic</p>';
לכאורה לא נגעתי בכפתור, אבל למעשה כן החלפתי אותו בכפתור חדש, ועל הכפתור החדש אין קוד טיפול באירוע ולכן לחיצות על הכפתור החדש לא יקפיצו את ההודעה.1 421
# היום למדתי: throw ב JavaScript אינו Expression
רשימת החידושים הצפויים בטייפסקריפט 5.3 לא כוללת שינויים מרחיקי לכת או מלהיבים, אבל ביט אחד תפס את תשומת ליבי. גיליתי שהקוד הבא ב JavaScript (או בטייפסקריפט) אף פעם לא עבד:
const id = searchParams.id || throw new Error("id is required");
הסיבה היא ש throw הוא Statement ולכן אפשר להשתמש בו רק כפקודה מלאה. אבל מה שיותר מעניין הוא כמה המבנה הזה לא חסר לאף אחד. הרי מבנה כזה היה אחד הנפוצים בשפה כמו perl עם שורות כמו:
open FILE, "filename.txt" or die "Cannot open file: $!\n";
או:
chdir('/etc') or die "Can't change directory";
אז למה בעצם לא היינו צריכים את זה ב JavaScript? כמה השערות-
1. אנחנו פחות זורקים שגיאות ב JavaScript - בניגוד ל perl שם כשמנסים להריץ סקריפט מי שהריץ את הסקריפט רואה את השגיאה ועוד יכול לעשות משהו, ב JavaScript זריקה של Exception במצב כזה פשוט תציג שגיאה לקונסול, ורוב הזמן אף אחד לא יראה אותה. לכן רוב הזמן הטיפול בשגיאות ב JavaScript הוא מקומי.
2. לפעמים יותר נוח להשתמש ב Destructuring כי ממילא יהיה לנו עוד מידע להוציא מהאוביקט, משהו כמו:
const { id, page, limit } = searchParams;
if (!id) {
throw new Error("id is required");
}
מה אתכם? יצא לכם להתגעגע למבנה של || throw ב JavaScript? אם כן ספרו מתי, ואם לא ספרו מה עשיתם במקום.1 421
# החלום ושברו
כשחלום פוגש את המציאות הוא אף פעם לא יפגוש מציאות שמתאימה לו במאה אחוז. אם החלום היה יכול לקרות בתור מציאות הוא כבר היה קורה ולא היה חלום.
כשחלום פוגש מציאות הוא עומד מול תהום, והתהום היא הפער בין מה שאנחנו חושבים שצריך לקרות לבין מה שיכול בפועל לקרות. אני חושב שצריך לקחת שנה ללמוד Machine Learning כי אני מכיר חברה שאחרי שנה מצאה עבודה. בפועל אולי החברה התחילה עם רקע יותר טוב ממני, ולי זה ייקח שלוש שנים. אני חושב שבזמן הלימודים אני צריך למצוא משרת סטודנט כי הרבה חברים המליצו לי לחפש עבודה כבר בשנה השנייה, אבל בפועל אני עשוי לגלות שהעבודה לא מאפשרת לי להשקיע מספיק בלימודים ואני צריך לבחור בין אותה משרת סטודנט לציונים מספיק טובים.
אני חולם שאחרי שאבנה פרויקט ואוסיף אותו לקורות חיים כולם יתחילו לחזור אליי, בפועל אני עשוי לגלות שהפרויקט שלי לא היה מספיק טוב וצריך להמשיך לעבוד.
אני חולם שאחרי שאמצא עבודה בהייטק אוכל להתקדם בתוך החברה וללמוד בזמן העבודה מיומנויות חדשות, בפועל אני עשוי לגלות שהחברה תקועה על מוצרים מיושנים וחלק חשוב מהעבודה הוא למצוא זמן לימוד גם מחוץ ליום יום העמוס.
מי שיודע בדיוק איך דברים יעבדו במציאות ופועל כדי להגשים אותם זה פשוט בן אדם שעובד, לא מישהו שמגשים חלום. הגשמת חלום היא בדיוק הקסם הזה של לעשות משהו שאנחנו לא יודעים איך ייגמר, לתפוס את ההזדמנויות שבדרך, להתפשר כשצריך ולהתעקש כשצריך. וכן חלומות הרבה פעמים לא מתגשמים, וזה עוד חלק מהקסם שלהם. ועדיין, העבודה על החלומות שווה את המאמץ.
Available now! Telegram Research 2025 — the year's key insights 
