4.3 פרוטוטייפים ומחלקות תרגול
תרגול - פרוטוטייפים ומחלקות¶
תרגיל 1 - מחלקת Stack¶
צרו מחלקה Stack שמממשת מחסנית (LIFO - last in, first out):
push(item)- מוסיף פריט לראש המחסניתpop()- מוציא ומחזיר את הפריט העליון (זורק שגיאה אם ריקה)peek()- מחזיר את הפריט העליון בלי להוציא אותוisEmpty()- מחזירtrueאם המחסנית ריקהsize- getter שמחזיר את מספר הפריטיםclear()- מרוקן את המחסניתtoArray()- מחזיר עותק של המחסנית כמערך
המערך הפנימי צריך להיות פרטי (#).
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.peek()); // 3
console.log(stack.pop()); // 3
console.log(stack.size); // 2
console.log(stack.toArray()); // [1, 2]
תרגיל 2 - ירושה - צורות גיאומטריות¶
צרו היררכיית מחלקות:
Shape (מחלקת בסיס):
- constructor(color) - צבע
- area() - זורקת שגיאה ("must be implemented")
- describe() - מחזירה מחרוזת: "<ClassName> with area <area>"
Rectangle (יורש מ-Shape):
- constructor(width, height, color)
- area() - שטח מלבן
- perimeter - getter להיקף
Circle (יורש מ-Shape):
- constructor(radius, color)
- area() - שטח עיגול
- perimeter - getter להיקף
Square (יורש מ-Rectangle):
- constructor(side, color)
- רק שדה אחד - side
const rect = new Rectangle(10, 5, "red");
console.log(rect.area()); // 50
console.log(rect.perimeter); // 30
console.log(rect.describe()); // "Rectangle with area 50"
const square = new Square(4, "blue");
console.log(square.area()); // 16
console.log(square.perimeter); // 16
console.log(square instanceof Rectangle); // true
console.log(square instanceof Shape); // true
תרגיל 3 - מחלקה עם שדות פרטיים ו-validation¶
צרו מחלקה Temperature שמנהלת טמפרטורה:
constructor(celsius)- מקבלת ערך בצלזיוס#celsius- שדה פרטיget celsius/set celsius- getter ו-setter (סטר מוודא שהערך מעל -273.15)get fahrenheit/set fahrenheit- getter ו-setter שממירים אוטומטיתget kelvin/set kelvin- getter ו-setter שממירים אוטומטיתstatic fromFahrenheit(f)- factory methodstatic fromKelvin(k)- factory methodtoString()- מחזירה"<celsius>C / <fahrenheit>F / <kelvin>K"
נוסחאות:
- F = C * 9/5 + 32
- K = C + 273.15
const t = new Temperature(100);
console.log(t.fahrenheit); // 212
console.log(t.kelvin); // 373.15
console.log(t.toString()); // "100C / 212F / 373.15K"
t.fahrenheit = 32;
console.log(t.celsius); // 0
const t2 = Temperature.fromKelvin(0);
console.log(t2.celsius); // -273.15
תרגיל 4 - מחלקה LinkedList¶
צרו מחלקת LinkedList (רשימה מקושרת):
צרו קודם מחלקת עזר Node:
- constructor(value) - ערך ו-next שמצביע על הצומת הבא (ברירת מחדל null)
מחלקת LinkedList:
- append(value) - מוסיף צומת בסוף
- prepend(value) - מוסיף צומת בתחילה
- find(value) - מחזיר את הצומת הראשון עם הערך, או null
- remove(value) - מסיר את הצומת הראשון עם הערך
- toArray() - מחזיר מערך של כל הערכים
- size - getter שמחזיר את מספר הצמתים
- toString() - מחזיר מחרוזת כמו "1 -> 2 -> 3 -> null"
const list = new LinkedList();
list.append(1);
list.append(2);
list.append(3);
list.prepend(0);
console.log(list.toString()); // "0 -> 1 -> 2 -> 3 -> null"
console.log(list.size); // 4
list.remove(2);
console.log(list.toString()); // "0 -> 1 -> 3 -> null"
תרגיל 5 - מערכת הרשאות עם ירושה¶
צרו מערכת משתמשים:
User (בסיס):
- constructor(name, email)
- login() - מדפיסה הודעת כניסה
- getPermissions() - מחזירה ["read"]
- describe() - מחזירה "User: <name> (<email>)"
Moderator (יורש מ-User):
- constructor(name, email, department)
- getPermissions() - מחזירה ["read", "edit", "delete"]
- ban(user) - מדפיסה הודעה שהמשתמש הורחק
- describe() - מוסיפה את ה-department
Admin (יורש מ-Moderator):
- constructor(name, email)
- getPermissions() - מחזירה ["read", "edit", "delete", "admin"]
- createUser(name, email) - יוצרת User חדש ומחזירה אותו
- static superAdmin(email) - factory שיוצרת admin עם השם "Super Admin"
const user = new User("Alice", "alice@mail.com");
const mod = new Moderator("Bob", "bob@mail.com", "Support");
const admin = Admin.superAdmin("admin@mail.com");
console.log(user.getPermissions()); // ["read"]
console.log(mod.getPermissions()); // ["read", "edit", "delete"]
console.log(admin.getPermissions()); // ["read", "edit", "delete", "admin"]
console.log(admin instanceof User); // true
תרגיל 6 - מחלקה Iterable¶
צרו מחלקה Range שמייצגת טווח מספרים ותומכת ב-for...of:
constructor(start, end, step = 1)- צריכה לעבוד עם
for...of(צריך לממש[Symbol.iterator]()) includes(n)- בודקת אם מספר בטווחtoArray()- מחזירה מערך של כל המספרים בטווחget length- כמה מספרים בטווח
רמז: כדי לתמוך ב-for...of, צריך לממש מתודה [Symbol.iterator]() שמחזירה אובייקט עם מתודת next().
const range = new Range(1, 10, 2);
for (const n of range) {
console.log(n); // 1, 3, 5, 7, 9
}
console.log(range.includes(5)); // true
console.log(range.includes(4)); // false
console.log(range.toArray()); // [1, 3, 5, 7, 9]
console.log(range.length); // 5
console.log([...range]); // [1, 3, 5, 7, 9]
תרגיל 7 - תבנית Observer¶
ממשו את תבנית העיצוב Observer עם מחלקות:
EventEmitter:
- on(event, listener) - רושם listener
- off(event, listener) - מסיר listener
- emit(event, ...data) - מפעיל את כל ה-listeners לאירוע
Store (יורש מ-EventEmitter):
- constructor(initialState) - מקבל state התחלתי (אובייקט)
- getState() - מחזיר עותק של ה-state
- setState(updates) - מעדכן את ה-state ומפעיל אירוע "change" עם ה-state החדש
- subscribe(listener) - קיצור ל-on("change", listener), מחזירה פונקציית unsubscribe
const store = new Store({ count: 0, name: "App" });
const unsubscribe = store.subscribe((state) => {
console.log("State changed:", state);
});
store.setState({ count: 1 });
// "State changed: { count: 1, name: "App" }"
store.setState({ count: 2, name: "Updated" });
// "State changed: { count: 2, name: "Updated" }"
unsubscribe();
store.setState({ count: 3 }); // no log - unsubscribed