לדלג לתוכן

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");

הפלט:

Start
End
One second later...
Two seconds later...
Three seconds later...

"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 בסיסי:

(function() {
    console.log("Hello from 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.

תשובות לשאלות

  1. function declaration עוברת hoisting ואפשר לקרוא לה לפני ההגדרה בקוד. function expression נשמרת במשתנה ולא עוברת hoisting - חייבים להגדיר אותה לפני השימוש.

  2. arrow function טובה לפונקציות קצרות, callbacks, ו-inline functions. לא כדאי להשתמש בה כ-method של אובייקט (בגלל this) ולא כפונקציה שצריכה hoisting.

  3. ...args ב-JS יוצר מערך אמיתי. *args בפייתון יוצר tuple. שניהם אוספים מספר לא ידוע של ארגומנטים. ב-JS ה-rest parameter חייב להיות אחרון ויכול להיות רק אחד, בדיוק כמו בפייתון.

  4. callback היא פונקציה שמועברת כארגומנט ונקראת מאוחר יותר. דוגמה מהחיים: כשמזמינים פיצה (קוראים לפונקציה), ומשאירים מספר טלפון (callback) - כשהפיצה מוכנה הם מתקשרים (קוראים ל-callback).

  5. פונקציה בלי return ב-JS מחזירה undefined. בפייתון מחזירה None. שני הערכים אומרים "אין ערך מוחזר" אבל הם לא אותו דבר.

  6. IIFE (Immediately Invoked Function Expression) היא פונקציה שנוצרת ומורצת מיד. היא שימושית ליצירת scope פרטי שבו משתנים לא דולפים ל-scope הגלובלי. בעבר השתמשו בה הרבה, היום פחות בזכות let/const ומודולים.