לדלג לתוכן

2.4 פונקציות הרצאה

הגדרת פונקציה - function declaration

הדרך הבסיסית ביותר ליצור פונקציה ב-JS:

function greet(name) {
    console.log(`Hello, ${name}!`);
}

greet("Alice"); // "Hello, Alice!"
greet("Bob");   // "Hello, Bob!"

בפייתון:

# def greet(name):
#     print(f"Hello, {name}!")

ההבדלים:
- function במקום def
- סוגריים מסולסלים { } במקום הזחה
- אין נקודתיים : בסוף השורה הראשונה

פונקציה עם ערך מוחזר - return

function add(a, b) {
    return a + b;
}

let result = add(3, 5);
console.log(result); // 8
  • return עובד בדיוק כמו בפייתון
  • אם אין return, הפונקציה מחזירה undefined (בפייתון מחזירה None)
function multiply(a, b) {
    return a * b;
    console.log("This will never run"); // dead code - after return
}

function doSomething() {
    console.log("Hello");
    // no return - returns undefined
}

let x = doSomething();
console.log(x); // undefined

הרמה - hoisting של function declarations

יתרון ייחודי של function declaration - אפשר לקרוא לפונקציה לפני שהיא מוגדרת בקוד:

// this works! function declarations are hoisted
sayHello(); // "Hello!"

function sayHello() {
    console.log("Hello!");
}

בפייתון זה לא עובד:

# say_hello()  # NameError! Function not defined yet
# def say_hello():
#     print("Hello!")

זה שימושי כי אפשר לארגן את הקוד ככה שהקוד הראשי למעלה והפונקציות למטה.


ביטוי פונקציה - function expression

דרך נוספת ליצור פונקציה - לשמור אותה במשתנה:

const greet = function(name) {
    console.log(`Hello, ${name}!`);
};

greet("Alice"); // "Hello, Alice!"
  • הפונקציה נשמרת בתוך משתנה, כמו כל ערך אחר
  • הנקודה-פסיק בסוף חשובה כי זה assignment
  • אין hoisting - אי אפשר לקרוא לפונקציה לפני ההגדרה
// this does NOT work - no hoisting for function expressions
// greet("Alice"); // ReferenceError: Cannot access 'greet' before initialization

const greet = function(name) {
    console.log(`Hello, ${name}!`);
};

פונקציה אנונימית - anonymous function

הפונקציה בדוגמה למעלה היא "אנונימית" - אין לה שם. אפשר גם לתת לה שם:

const greet = function myGreeting(name) {
    console.log(`Hello, ${name}!`);
};

greet("Alice");      // works
// myGreeting("Bob"); // ReferenceError - the name is only available inside the function

פונקציות חץ - arrow functions

תחביר מקוצר ומודרני ליצירת פונקציות (נוסף ב-ES6):

// regular function
const add = function(a, b) {
    return a + b;
};

// arrow function
const addArrow = (a, b) => {
    return a + b;
};

// arrow function with implicit return (one-liner)
const addShort = (a, b) => a + b;

כללי הקיצור:
1. מחליפים את function בחץ => אחרי הסוגריים
2. אם יש רק ביטוי אחד, אפשר לוותר על { } ועל return - הערך חוזר אוטומטית
3. אם יש רק פרמטר אחד, אפשר לוותר על הסוגריים:

// one parameter - parentheses optional
const double = x => x * 2;
const square = x => x ** 2;

// no parameters - empty parentheses required
const sayHi = () => console.log("Hi!");

// multiple parameters - parentheses required
const add = (a, b) => a + b;

// multiple lines - braces and return required
const calculate = (a, b) => {
    let sum = a + b;
    let product = a * b;
    return { sum, product };
};

השוואה לפייתון

Arrow functions דומות ל-lambda בפייתון, אבל הרבה יותר חזקות:

// JavaScript arrow function
const double = x => x * 2;

// Python lambda (limited to one expression)
// double = lambda x: x * 2
  • בפייתון lambda מוגבלת לביטוי אחד בלבד
  • ב-JS arrow function יכולה להכיל כמה שורות

מתי להשתמש בכל צורה?

// function declaration - for main/important functions
// hoisted, clear and readable
function processUser(user) {
    // ...
}

// arrow function - for short callbacks and inline functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);

// function expression - less common, when you need to conditionally define
const handler = isAdmin ? function() { /* ... */ } : function() { /* ... */ };

בפרקטיקה, רוב המפתחים משתמשים ב:
- function declaration לפונקציות עיקריות
- arrow functions לכל השאר


הבדלים חשובים בין סוגי הפונקציות

hoisting - הרמה

// function declaration - hoisted (works!)
sayHello();
function sayHello() {
    console.log("Hello!");
}

// function expression - NOT hoisted (error!)
// sayBye(); // ReferenceError
const sayBye = function() {
    console.log("Bye!");
};

// arrow function - NOT hoisted (error!)
// sayHi(); // ReferenceError
const sayHi = () => {
    console.log("Hi!");
};

רק function declarations עוברות hoisting.

this - הקשר

ההבדל הכי חשוב בין פונקציה רגילה ל-arrow function הוא איך הן מתנהגות עם this. נלמד על this לעומק בשיעורים הבאים, אבל בקצרה:

// regular function - has its own 'this'
const person = {
    name: "Alice",
    greet: function() {
        console.log(`Hello, I'm ${this.name}`);
    }
};
person.greet(); // "Hello, I'm Alice"

// arrow function - inherits 'this' from surrounding scope
const person2 = {
    name: "Bob",
    greet: () => {
        console.log(`Hello, I'm ${this.name}`); // 'this' is NOT person2!
    }
};
person2.greet(); // "Hello, I'm undefined" (wrong!)

כלל אצבע: אל תשתמשו ב-arrow functions בתור methods של אובייקט.

arguments - אובייקט הארגומנטים

פונקציות רגילות מקבלות אובייקט מיוחד בשם arguments:

function showArgs() {
    console.log(arguments);
    console.log(arguments.length);
}

showArgs(1, 2, 3); // [1, 2, 3], 3
showArgs("a", "b"); // ["a", "b"], 2

// arrow functions don't have 'arguments'
const showArgsArrow = () => {
    // console.log(arguments); // ReferenceError
};
  • arguments מכיל את כל הארגומנטים שהועברו לפונקציה
  • Arrow functions לא מקבלות arguments - משתמשים ב-rest parameters במקום

פרמטרים - parameters

ערכי ברירת מחדל - default parameters

function greet(name = "World") {
    console.log(`Hello, ${name}!`);
}

greet("Alice"); // "Hello, Alice!"
greet();        // "Hello, World!"

בפייתון:

# def greet(name="World"):
#     print(f"Hello, {name}!")

אותו רעיון בדיוק. אפשר גם בcombination:

function createUser(name, age = 0, role = "user") {
    return { name, age, role };
}

createUser("Alice", 25, "admin"); // { name: "Alice", age: 25, role: "admin" }
createUser("Bob", 30);            // { name: "Bob", age: 30, role: "user" }
createUser("Charlie");            // { name: "Charlie", age: 0, role: "user" }

פרמטרי rest - rest parameters

function sum(...numbers) {
    let total = 0;
    for (const num of numbers) {
        total += num;
    }
    return total;
}

console.log(sum(1, 2, 3));       // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum());               // 0
  • ...numbers אוסף את כל הארגומנטים למערך
  • זה המקבילה של *args בפייתון
# Python equivalent
# def sum(*numbers):
#     total = 0
#     for num in numbers:
#         total += num
#     return total

אפשר לשלב rest parameters עם פרמטרים רגילים:

function log(level, ...messages) {
    console.log(`[${level}]`, ...messages);
}

log("INFO", "Server started", "on port 3000");
// [INFO] Server started on port 3000

log("ERROR", "Something went wrong");
// [ERROR] Something went wrong
  • ה-rest parameter חייב להיות האחרון ברשימת הפרמטרים
  • יכול להיות רק rest parameter אחד

ערך מוחזר - return value

// returning a single value
function square(x) {
    return x * x;
}

// returning an object (note the parentheses with arrow functions!)
const createPoint = (x, y) => ({ x, y });
// without () it would be treated as function body { }, not object { }

// returning nothing (void)
function logMessage(msg) {
    console.log(msg);
    // no return - returns undefined
}

// early return (guard clause)
function divide(a, b) {
    if (b === 0) {
        console.log("Cannot divide by zero!");
        return null;
    }
    return a / b;
}

שימו לב לטריק עם arrow functions ואובייקטים:

// WRONG - JS thinks {} is function body
const makeObj = (x) => { value: x }; // returns undefined!

// CORRECT - wrap in () to indicate it's an object
const makeObj = (x) => ({ value: x }); // returns { value: x }

פונקציות כערכים מדרגה ראשונה - first-class functions

בדיוק כמו בפייתון, פונקציות ב-JS הן ערכים לכל דבר - אפשר לשמור אותן במשתנים, להעביר אותן כארגומנטים, ולהחזיר אותן מפונקציות אחרות:

// storing a function in a variable
const operation = add;
console.log(operation(3, 5)); // 8

// storing functions in an array
const operations = [
    (a, b) => a + b,
    (a, b) => a - b,
    (a, b) => a * b,
    (a, b) => a / b
];

console.log(operations[0](10, 3)); // 13 (add)
console.log(operations[2](10, 3)); // 30 (multiply)

// storing functions in an object
const math = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b
};

console.log(math.add(5, 3));      // 8
console.log(math.multiply(5, 3)); // 15

פונקציות חוזרות - callback functions

callback היא פונקציה שמעבירים כארגומנט לפונקציה אחרת, והיא "נקראת חזרה" בזמן מאוחר יותר:

function doSomething(callback) {
    console.log("Doing something...");
    callback(); // calling the function that was passed
}

doSomething(function() {
    console.log("Done!");
});
// prints: "Doing something...", "Done!"

// same thing with arrow function
doSomething(() => {
    console.log("Done!");
});

דוגמה מעשית יותר:

function processArray(arr, callback) {
    const results = [];
    for (const item of arr) {
        results.push(callback(item));
    }
    return results;
}

const numbers = [1, 2, 3, 4, 5];

const doubled = processArray(numbers, x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

const squared = processArray(numbers, x => x ** 2);
console.log(squared); // [1, 4, 9, 16, 25]

const asStrings = processArray(numbers, x => String(x));
console.log(asStrings); // ["1", "2", "3", "4", "5"]

בפייתון זה אותו רעיון:

# def process_array(arr, callback):
#     return [callback(item) for item in arr]
#
# doubled = process_array([1,2,3], lambda x: x * 2)

Callbacks הם הבסיס של תכנות אסינכרוני ב-JS - נראה הרבה מהם בהמשך הקורס.

setTimeout ו-setInterval

שתי פונקציות מובנות שמשתמשות ב-callbacks:

// setTimeout - run once after delay (milliseconds)
setTimeout(() => {
    console.log("This runs after 2 seconds");
}, 2000);

// setInterval - run repeatedly every N milliseconds
let count = 0;
const intervalId = setInterval(() => {
    count++;
    console.log(`Tick ${count}`);
    if (count >= 5) {
        clearInterval(intervalId); // stop the interval
    }
}, 1000);
  • setTimeout מריץ callback פעם אחת אחרי עיכוב
  • setInterval מריץ callback שוב ושוב בכל N מילישניות
  • clearInterval עוצר interval שרץ

IIFE - ביטוי פונקציה שמופעל מיידית

IIFE (Immediately Invoked Function Expression) היא פונקציה שנוצרת ומופעלת מיד:

(function() {
    console.log("This runs immediately!");
})();

// with arrow function
(() => {
    console.log("This also runs immediately!");
})();

// with parameters
(function(name) {
    console.log(`Hello, ${name}!`);
})("Alice");

למה זה שימושי?
- יוצר scope פרטי - משתנים בתוך ה-IIFE לא דולפים החוצה
- בעבר השתמשו בזה הרבה לפני שהיו let/const ומודולים
- היום פחות נפוץ, אבל עדיין שימושי לפעמים

// variables inside IIFE don't leak out
(function() {
    let secret = "password123";
    console.log(secret); // works inside
})();

// console.log(secret); // ReferenceError - not accessible outside

השוואה מסכמת לפייתון

נושא Python JavaScript
הגדרת פונקציה def name(): function name() { }
פונקציה אנונימית lambda x: x * 2 (x) => x * 2
ערך ברירת מחדל def f(x=5): function f(x = 5) { }
ארגומנטים גמישים def f(*args): function f(...args) { }
ערך מוחזר ללא return None undefined
hoisting לא כן (רק function declaration)
first-class כן כן

דוגמה מסכמת

// function declaration
function createGreeting(name, greeting = "Hello") {
    return `${greeting}, ${name}!`;
}

// arrow function for short operations
const toUpperCase = str => str.toUpperCase();
const add = (a, b) => a + b;

// function that takes a callback
function repeat(n, callback) {
    for (let i = 0; i < n; i++) {
        callback(i);
    }
}

// using callbacks
repeat(3, i => {
    const msg = createGreeting("World");
    console.log(`${i}: ${toUpperCase(msg)}`);
});
// prints:
// "0: HELLO, WORLD!"
// "1: HELLO, WORLD!"
// "2: HELLO, WORLD!"

// function that returns a function
function createMultiplier(factor) {
    return (number) => number * factor;
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

סיכום

  • שלוש דרכים ליצור פונקציות: declaration (function), expression (const f = function), arrow (const f = () => {})
  • רק function declarations עוברות hoisting
  • arrow functions קצרות ונוחות, אבל מתנהגות שונה עם this
  • ערכי ברירת מחדל: function f(x = 5) - כמו בפייתון
  • rest parameters: function f(...args) - כמו *args בפייתון
  • פונקציות הן first-class - אפשר להעביר אותן כארגומנטים ולשמור במשתנים
  • callbacks - העברת פונקציה כארגומנט - בסיס של תכנות אסינכרוני ב-JS
  • IIFE - פונקציה שרצה מיד, יוצרת scope פרטי