2.4 פונקציות פתרון
פתרון - פונקציות¶
פתרון תרגיל 1¶
א. isEven:
// function declaration
function isEven(num) {
return num % 2 === 0;
}
// function expression
const isEven2 = function(num) {
return num % 2 === 0;
};
// arrow function
const isEven3 = num => num % 2 === 0;
ב. max:
// function declaration
function max(a, b) {
return a > b ? a : b;
}
// function expression
const max2 = function(a, b) {
return a > b ? a : b;
};
// arrow function
const max3 = (a, b) => a > b ? a : b;
ג. capitalize:
// function declaration
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// function expression
const capitalize2 = function(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
};
// arrow function
const capitalize3 = str => str.charAt(0).toUpperCase() + str.slice(1);
פתרון תרגיל 2¶
function createEmail(to, subject = "No Subject", body = "", priority = "normal") {
return {
to,
subject,
body,
priority
};
}
console.log(createEmail("alice@mail.com"));
// { to: "alice@mail.com", subject: "No Subject", body: "", priority: "normal" }
console.log(createEmail("bob@mail.com", "Meeting", "See you at 3pm", "high"));
// { to: "bob@mail.com", subject: "Meeting", body: "See you at 3pm", priority: "high" }
console.log(createEmail("charlie@mail.com", "Hello"));
// { to: "charlie@mail.com", subject: "Hello", body: "", priority: "normal" }
פתרון תרגיל 3¶
א. average:
function average(...numbers) {
if (numbers.length === 0) return 0;
let total = 0;
for (const num of numbers) {
total += num;
}
return total / numbers.length;
}
console.log(average(10, 20, 30)); // 20
console.log(average(5, 10)); // 7.5
console.log(average(100)); // 100
console.log(average()); // 0
ב. joinWith:
function joinWith(separator, ...strings) {
let result = "";
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < strings.length - 1) {
result += separator;
}
}
return result;
}
// or shorter:
const joinWith2 = (separator, ...strings) => strings.join(separator);
console.log(joinWith("-", "a", "b", "c")); // "a-b-c"
console.log(joinWith(" ", "Hello", "World")); // "Hello World"
console.log(joinWith(", ", "one", "two", "three")); // "one, two, three"
פתרון תרגיל 4¶
א. applyToEach:
function applyToEach(arr, callback) {
const results = [];
for (const item of arr) {
results.push(callback(item));
}
return results;
}
console.log(applyToEach([1, 2, 3], x => x * 2));
// [2, 4, 6]
console.log(applyToEach(["hello", "world"], s => s.toUpperCase()));
// ["HELLO", "WORLD"]
console.log(applyToEach([1, 4, 9], Math.sqrt));
// [1, 2, 3]
ב. filterArray:
function filterArray(arr, callback) {
const results = [];
for (const item of arr) {
if (callback(item)) {
results.push(item);
}
}
return results;
}
console.log(filterArray([1, 2, 3, 4, 5, 6], x => x % 2 === 0));
// [2, 4, 6]
console.log(filterArray(["hi", "hello", "hey"], s => s.length > 2));
// ["hello", "hey"]
הערה: הפונקציות האלה קיימות כבר מובנות ב-JS כ-Array.map() ו-Array.filter() - נלמד עליהן בשיעור הבא.
פתרון תרגיל 5¶
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
function applyOperation(a, b, operation) {
return operation(a, b);
}
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
console.log(`5! = ${factorial(5)}`);
console.log(`10 + 3 = ${applyOperation(10, 3, add)}`);
console.log(`10 - 3 = ${applyOperation(10, 3, subtract)}`);
console.log(`10 * 3 = ${applyOperation(10, 3, multiply)}`);
שינויים מפייתון:
- def הפך ל-function
- lambda הפך ל-arrow function
- apply_operation הפך ל-applyOperation (camelCase)
- f"..." הפך ל-`...`
פתרון תרגיל 6¶
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
const anotherCounter = createCounter();
console.log(anotherCounter()); // 1 (independent counter)
console.log(anotherCounter()); // 2
זה נקרא closure - הפונקציה הפנימית "זוכרת" את המשתנה count מה-scope של הפונקציה החיצונית, גם אחרי ש-createCounter סיימה לרוץ. כל קריאה ל-createCounter יוצרת count חדש ועצמאי.
פתרון תרגיל 7¶
// Part A
console.log(add(2, 3)); // 5 - works! function declarations are hoisted
function add(a, b) {
return a + b;
}
// Part B
// console.log(subtract(5, 3)); // ReferenceError! function expressions are NOT hoisted
const subtract = function(a, b) {
return a - b;
};
// Part C
// console.log(multiply(4, 5)); // ReferenceError! arrow functions are NOT hoisted
const multiply = (a, b) => a * b;
- Part A עובד כי function declarations עוברות hoisting - הפונקציה זמינה לשימוש בכל מקום בקוד
- Part B ו-C לא עובדים כי function expressions ו-arrow functions לא עוברות hoisting - הן קיימות רק מרגע ההגדרה ואילך
פתרון תרגיל 8¶
console.log("Start");
setTimeout(() => {
console.log("One second later...");
}, 1000);
setTimeout(() => {
console.log("Two seconds later...");
}, 2000);
setTimeout(() => {
console.log("Three seconds later...");
}, 3000);
console.log("End");
הפלט:
"End" מודפס לפני ההודעות של setTimeout כי JS היא שפה אסינכרונית - setTimeout לא חוסם את הקוד. הוא מתזמן את ה-callback לרוץ מאוחר יותר, וממשיך לשורה הבאה מיד. לכן "End" רץ לפני שעוברת שניה.
פתרון תרגיל 9¶
const calculator = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => {
if (b === 0) {
return "Error: division by zero";
}
return a / b;
}
};
function calculate(a, b, callback) {
if (typeof callback !== "function") {
console.log("Error: third argument must be a function");
return null;
}
return callback(a, b);
}
console.log(calculate(10, 3, calculator.add)); // 13
console.log(calculate(10, 3, calculator.subtract)); // 7
console.log(calculate(10, 3, calculator.multiply)); // 30
console.log(calculate(10, 0, calculator.divide)); // "Error: division by zero"
console.log(calculate(10, 3, "not a function")); // prints error, returns null
פתרון תרגיל 10¶
א. IIFE בסיסי:
ב. IIFE עם פרמטר:
(function(name) {
console.log(`Hello, ${name}!`);
})("Alice");
// or with arrow function
((name) => {
console.log(`Hello, ${name}!`);
})("Bob");
ג. IIFE עם משתנה פרטי:
const module = (function() {
let privateValue = 0; // not accessible from outside!
return {
get: () => privateValue,
set: (newValue) => {
privateValue = newValue;
}
};
})();
module.set(42);
console.log(module.get()); // 42
// console.log(privateValue); // ReferenceError - not accessible!
// console.log(module.privateValue); // undefined - not a property of module
המשתנה privateValue נגיש רק דרך הפונקציות get() ו-set(). אין דרך לגשת אליו ישירות מבחוץ. זה נקרא encapsulation.
תשובות לשאלות¶
-
function declaration עוברת hoisting ואפשר לקרוא לה לפני ההגדרה בקוד. function expression נשמרת במשתנה ולא עוברת hoisting - חייבים להגדיר אותה לפני השימוש.
-
arrow function טובה לפונקציות קצרות, callbacks, ו-inline functions. לא כדאי להשתמש בה כ-method של אובייקט (בגלל
this) ולא כפונקציה שצריכה hoisting. -
...argsב-JS יוצר מערך אמיתי.*argsבפייתון יוצר tuple. שניהם אוספים מספר לא ידוע של ארגומנטים. ב-JS ה-rest parameter חייב להיות אחרון ויכול להיות רק אחד, בדיוק כמו בפייתון. -
callback היא פונקציה שמועברת כארגומנט ונקראת מאוחר יותר. דוגמה מהחיים: כשמזמינים פיצה (קוראים לפונקציה), ומשאירים מספר טלפון (callback) - כשהפיצה מוכנה הם מתקשרים (קוראים ל-callback).
-
פונקציה בלי
returnב-JS מחזירהundefined. בפייתון מחזירהNone. שני הערכים אומרים "אין ערך מוחזר" אבל הם לא אותו דבר. -
IIFE (Immediately Invoked Function Expression) היא פונקציה שנוצרת ומורצת מיד. היא שימושית ליצירת scope פרטי שבו משתנים לא דולפים ל-scope הגלובלי. בעבר השתמשו בה הרבה, היום פחות בזכות
let/constומודולים.