מפתח

ברוכים הבאים!

פרויקט זה מיועד ללימוד Node.js למתחילים. הפרויקט כולל תיעוד מפורט בעברית ודוגמאות קוד מעשיות עם דגש על סינטקס וצורת כתיבה.


📋 תוכן עניינים

תיעוד (docs/)

  1. רקע והיסטוריה - מהו Node.js ומאיפה הוא הגיע
  2. יסודות - מושגים בסיסיים שחייבים לדעת
  3. סינטקס וצורת כתיבה - איך כותבים קוד ב-Node.js
  4. מודולים ו-npm - ניהול קוד וחבילות
  5. תכנות אסינכרוני - Promises ו-async/await

דוגמאות קוד (examples/)

  1. 01-basics/ - יסודות: משתנים, פונקציות ותנאים
  2. 02-modules/ - עבודה עם מודולים
  3. 03-fs/ - קריאה וכתיבה לקבצים
  4. 04-http/ - יצירת שרת HTTP
  5. 05-async/ - תכנות אסינכרוני
  6. 06-events/ - מנגנון האירועים

🔧 דרישות מקדימות

לפני שמתחילים, וודאו שמותקן אצלכם:

  • Node.js (גרסה 18 ומעלה) - להורדה
  • עורך קוד (מומלץ: VS Code)

בדיקת התקנה:

node --version      # צריך להציג v18.0.0 או יותר
npm --version       # צריך להציג 9.0.0 או יותר

🚀 התקנה והפעלה

שלב 1: מעבר לתיקיית הדוגמאות

cd examples

שלב 2: הרצת דוגמה

node 01-basics/variables.js

הרצת שרת HTTP

node 04-http/simple-server.js
# פתחו בדפדפן: http://localhost:3000

📚 סדר לימוד מומלץ

  1. התחילו מהרקע - הבינו מה זה Node.js ולמה הוא שונה
  2. למדו את היסודות - הכירו את הסביבה והכלים
  3. תרגלו סינטקס - כתבו קוד בסיסי
  4. הבינו מודולים - למדו לארגן קוד נכון
  5. שלטו באסינכרוניות - המפתח להצלחה ב-Node.js
  6. בנו משהו משלכם - השרת הראשון שלכם!

💡 טיפים למתחילים

טיפ 1: השתמשו ב-console.log() בשפע כדי להבין מה קורה בקוד

טיפ 2: התחילו עם קבצים קטנים לפני שעוברים לפרויקטים גדולים

טיפ 3: קראו הודעות שגיאה בזהירות - הן מסבירות בדיוק מה הבעיה


🔗 משאבים נוספים


בהצלחה בלימוד! 🎓

📜 רקע והיסטוריה של Node.js

מהו Node.js?

Node.js הוא סביבת הרצה (Runtime Environment) שמאפשרת להריץ קוד JavaScript מחוץ לדפדפן.

עד להמצאת Node.js ב-2009, JavaScript יכלה לרוץ רק בתוך דפדפנים (Chrome, Firefox, וכו'). Node.js שינה את זה לחלוטין.

┌─────────────────────────────────────────────────────┐
│                    JavaScript                        │
├──────────────────────┬──────────────────────────────┤
│   בדפדפן (Frontend)  │   ב-Node.js (Backend)        │
├──────────────────────┼──────────────────────────────┤
│ - DOM manipulation   │ - שרתים                      │
│ - אינטראקציה עם משתמש│ - גישה לקבצים               │
│ - אנימציות          │ - מסדי נתונים                │
│ - בקשות AJAX        │ - כלי CLI                    │
└──────────────────────┴──────────────────────────────┘

ההיסטוריה הקצרה

2009 - הלידה

Ryan Dahl יצר את Node.js כי הוא התסכל מהדרך שבה שרתים מטפלים בבקשות מרובות.

הרעיון המרכזי: במקום לחכות לפעולה אחת שתסתיים (כמו קריאה ממסד נתונים), אפשר להמשיך לעבוד על דברים אחרים בינתיים.

2010 - npm נולד

npm (Node Package Manager) הפך את השיתוף של קוד לקל מאוד. היום יש מעל 2 מיליון חבילות ב-npm!

2015 - io.js והאיחוד

היה פיצול קצר לפרויקט בשם io.js, אבל הם התאחדו חזרה ויצרו את Node.js Foundation.

היום

Node.js הוא אחד הכלים הפופולריים ביותר בעולם הפיתוח. חברות כמו Netflix, PayPal, LinkedIn ו-NASA משתמשות בו.


מנוע V8

Node.js משתמש במנוע V8 של Google - אותו מנוע שמריץ JavaScript ב-Chrome.

┌─────────────────────────────────────────┐
│            הקוד שלך (JavaScript)         │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│              מנוע V8                    │
│  (מתרגם JavaScript לקוד מכונה)         │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│          מערכת ההפעלה                    │
│    (קבצים, רשת, תהליכים)                 │
└─────────────────────────────────────────┘

למה זה חשוב? V8 מהיר מאוד. הוא מקמפל JavaScript ישירות לקוד מכונה במקום לפרש אותו שורה-שורה.


למה Node.js מיוחד?

1. Non-blocking I/O (קלט/פלט לא חוסם)

הבעיה המסורתית:

בקשה 1 → חכה לתשובה ממסד נתונים (2 שניות) → המשך
בקשה 2 → חכה לתשובה מקובץ (1 שנייה) → המשך
סה"כ: 3 שניות

הפתרון של Node.js:

בקשה 1 → שלח למסד נתונים
בקשה 2 → שלח לקובץ (לא מחכים לבקשה 1!)
כשהתשובות מגיעות → טפל בהן
סה"כ: ~2 שניות

2. שפה אחת לכל מקום

  • Frontend: JavaScript בדפדפן
  • Backend: JavaScript ב-Node.js
  • Full Stack: מפתח אחד יכול לעבוד על הכל!

3. npm - המערכת האקולוגית הגדולה בעולם

צריכים לשלוח מיילים? יש חבילה לזה. צריכים להתחבר ל-MongoDB? יש חבילה לזה. צריכים כמעט כל דבר? כנראה שיש חבילה לזה.

npm install express    # framework לשרתים
npm install axios      # בקשות HTTP
npm install mongoose   # עבודה עם MongoDB

4. ביצועים מעולים

הודות ל-V8 והארכיטקטורה האסינכרונית, Node.js מתאים לאפליקציות עם הרבה משתמשים בו-זמנית.


שימושים נפוצים

סוג דוגמאות
שרתי API REST APIs, GraphQL
אפליקציות Real-time צ'אט, משחקים, שיתוף פעולה
כלי שורת פקודה (CLI) npm, webpack, eslint
Microservices שירותים קטנים ועצמאיים
Server-Side Rendering Next.js, Nuxt.js

מתי לא להשתמש ב-Node.js?

Node.js לא מתאים ל:

  • חישובים כבדים (עיבוד וידאו, למידת מכונה) - Node.js הוא single-threaded
  • אפליקציות שדורשות real-time response מדויק - עדיף שפות כמו C++

סיכום

מאפיין הסבר
מהו סביבת הרצה ל-JavaScript
נוצר 2009 על ידי Ryan Dahl
מבוסס על מנוע V8 של Chrome
יתרון עיקרי Non-blocking I/O
מערכת חבילות npm

→ המשך ליסודות

🧱 יסודות Node.js

התקנה ובדיקה

התקנת Node.js

הורידו מ- https://nodejs.org/ (מומלץ: גרסת LTS)

בדיקת התקנה

node --version   # v22.0.0 או יותר
npm --version    # 11.0.0 או יותר

שתי דרכים להריץ קוד

1. REPL (Read-Eval-Print-Loop)

שורת פקודה אינטראקטיבית - מעולה לניסויים מהירים:

$ node
Welcome to Node.js v22.0.0.
> 2 + 2
4
> console.log('שלום עולם!')
שלום עולם!
> .exit

פקודות שימושיות ב-REPL: - .help - עזרה - .clear - ניקוי - .exit - יציאה - Ctrl + C (פעמיים) - יציאה

2. הרצת קבצים

צרו קובץ app.js:

console.log('שלום מ-Node.js!');

הריצו:

node app.js

הבדלים מ-JavaScript בדפדפן

אין window, יש global

בדפדפן:

window.alert('שלום');  // עובד
document.getElementById('app');  // עובד

ב-Node.js:

window.alert('שלום');  // ❌ שגיאה! אין window
document.getElementById('app');  // ❌ שגיאה! אין DOM

global.myVar = 'ערך';  // ✅ האובייקט הגלובלי ב-Node
console.log(globalThis);  // ✅ עובד בשניהם

מה יש ב-Node.js שאין בדפדפן?

Node.js דפדפן
fs (קבצים) document (DOM)
http (שרת) window
path (נתיבים) localStorage
process (תהליך) fetch (רשת)
os (מערכת הפעלה) alert, prompt

אובייקט process

process הוא אובייקט גלובלי שמכיל מידע על התהליך הנוכחי.

מידע על הסביבה

// var Node.js
console.log(process.version);  // v22.0.0

// מערכת הפעלה
console.log(process.platform);  // win32, linux, darwin

// תיקייה נוכחית
console.log(process.cwd());  // C:\projects\my-app

ארגומנטים משורת הפקודה

// node app.js שלום עולם
console.log(process.argv);
// ['C:\\...\\node.exe', 'C:\\...\\app.js', 'שלום', 'עולם']

// רק הארגומנטים שלנו
const args = process.argv.slice(2);
console.log(args);  // ['שלום', 'עולם']

משתני סביבה

// read קריאת משתנה סביבה
console.log(process.env.PATH);
console.log(process.env.NODE_ENV);  // development / production

// שימוש נפוץ
const port = process.env.PORT || 3000;

יציאה מהתוכנית

process.exit(0);  // יציאה תקינה
process.exit(1);  // יציאה עם שגיאה

משתנים מיוחדים

__dirname - הנתיב לתיקייה של הקובץ הנוכחי

console.log(__dirname);
// C:\projects\my-app\src

__filename - הנתיב המלא לקובץ הנוכחי

console.log(__filename);
// C:\projects\my-app\src\app.js

שימוש מעשי

const path = require('path');

// בניית נתיב לקובץ בתיקייה שלנו
const configPath = path.join(__dirname, 'config.json');
console.log(configPath);
// C:\projects\my-app\src\config.json

Event Loop - הלב של Node.js

Event Loop הוא מה שמאפשר ל-Node.js לטפל בהרבה פעולות במקביל למרות שהוא single-threaded.

הרעיון הבסיסי

     ┌───────────────────────────┐
  ┌─>│         Call Stack        │  (מבצע קוד סינכרוני)
  │  └───────────┬───────────────┘
  │              │
  │              ▼
  │  ┌───────────────────────────┐
  │  │        Node APIs          │  (טיימרים, I/O, וכו')
  │  └───────────┬───────────────┘
  │              │
  │              ▼
  │  ┌───────────────────────────┐
  │  │      Callback Queue       │  (פונקציות שמחכות)
  │  └───────────┬───────────────┘
  │              │
  └──────────────┘ Event Loop בודק אם ה-Call Stack ריק

דוגמה פשוטה

console.log('1 - התחלה');

setTimeout(() => {
    console.log('2 - מהטיימר');
}, 0);

console.log('3 - סוף');

// פלט:
// 1 - התחלה
// 3 - סוף
// 2 - מהטיימר

למה? למרות ש-setTimeout מוגדר ל-0 מילישניות, הוא נשלח ל-Callback Queue ומתבצע רק אחרי שכל הקוד הסינכרוני סיים.


console - יותר מ-log

// הדפסה רגילה
console.log('מידע רגיל');

// אזהרה (צהוב בטרמינלים תואמים)
console.warn('זו אזהרה!');

// שגיאה (אדום)
console.error('זו שגיאה!');

// טבלה (מעולה לאובייקטים ומערכים)
console.table([
    { שם: 'משה', גיל: 30 },
    { שם: 'שרה', גיל: 25 }
]);

// מדידת זמן
console.time('לולאה');
for (let i = 0; i < 1000000; i++) {}
console.timeEnd('לולאה');  // לולאה: 5.123ms

// קיבוץ הודעות
console.group('פרטי משתמש');
console.log('שם: ישראל');
console.log('גיל: 28');
console.groupEnd();

Buffer - עבודה עם נתונים בינאריים

Buffer מייצג נתונים בינאריים (bytes) - חשוב לעבודה עם קבצים ורשת.

// יצירת Buffer מטקסט
const buf = Buffer.from('שלום עולם', 'utf8');
console.log(buf);  // <Buffer d7 a9 d7 9c d7 95 d7 9d ...>

// המרה חזרה לטקסט
console.log(buf.toString());  // שלום עולם

// יצירת Buffer ריק
const emptyBuf = Buffer.alloc(10);  // 10 bytes של אפסים

// גודל Buffer
console.log(buf.length);  // מספר ה-bytes

סיכום

מושג תיאור
node פקודה להרצת קוד
REPL סביבה אינטראקטיבית
global האובייקט הגלובלי
process מידע על התהליך
__dirname נתיב לתיקייה
__filename נתיב לקובץ
Event Loop מנגנון הטיפול באירועים
Buffer נתונים בינאריים

← חזרה לרקע | → המשך לסינטקס

✍️ סינטקס וצורת כתיבה ב-Node.js

זהו המדריך העיקרי לסינטקס של JavaScript ב-Node.js, עם דגש על צורת כתיבה נכונה ומוסכמות מקובלות.


📦 משתנים

שלוש דרכים להגדיר משתנים

// ❌ var - הדרך הישנה, לא מומלץ להשתמש
var oldWay = 'לא מומלץ';

// ✅ let - משתנה שאפשר לשנות
let counter = 0;
counter = 1;  // תקין

// ✅ const - קבוע שלא ניתן לשנות
const PI = 3.14159;
// PI = 3;  // ❌ שגיאה!

// 🔑 כלל הזהב: השתמשו ב-const כברירת מחדל, let רק כשצריך לשנות

הבדלי Scope

// var - function scope (בעייתי!)
function varExample() {
    if (true) {
        var x = 10;
    }
    console.log(x);  // 10 - עדיין נגיש!
}

// let/const - block scope (טוב יותר)
function letExample() {
    if (true) {
        let y = 10;
    }
    // console.log(y);  // ❌ שגיאה! y לא מוגדר כאן
}

🔤 טיפוסי נתונים

טיפוסים פרימיטיביים

// String - מחרוזת
const name = 'ישראל';
const greeting = "שלום";
const template = `שלום ${name}!`;  // Template Literal

// Number - מספר (שלם או עשרוני)
const age = 30;
const price = 19.99;
const negative = -5;

// Boolean - ערך לוגי
const isActive = true;
const isDeleted = false;

// undefined - לא הוגדר ערך
let notDefined;
console.log(notDefined);  // undefined

// null - ערך ריק במכוון
const emptyValue = null;

// Symbol - ייחודי ובלתי ניתן לשינוי
const uniqueId = Symbol('id');

// BigInt - מספרים גדולים מאוד
const bigNumber = 9007199254740991n;

בדיקת טיפוס

console.log(typeof 'שלום');    // string
console.log(typeof 42);        // number
console.log(typeof true);      // boolean
console.log(typeof undefined); // undefined
console.log(typeof null);      // object (באג היסטורי!)
console.log(typeof {});        // object
console.log(typeof []);        // object
console.log(typeof function(){}); // function

📝 מחרוזות (Strings)

יצירה ופעולות בסיסיות

const str = 'שלום עולם';

// אורך
console.log(str.length);  // 9

// גישה לתו
console.log(str[0]);  // 'ש'
console.log(str.charAt(0));  // 'ש'

// חיפוש
console.log(str.includes('עולם'));  // true
console.log(str.indexOf('עולם'));   // 5
console.log(str.startsWith('שלום')); // true
console.log(str.endsWith('עולם'));   // true

// חיתוך
console.log(str.slice(0, 4));  // 'שלום'
console.log(str.substring(5)); // 'עולם'

// שינוי
console.log(str.toUpperCase());  // 'שלום עולם' (לא משפיע על עברית)
console.log(str.replace('עולם', 'ישראל'));  // 'שלום ישראל'
console.log('  hello  '.trim());  // 'hello'

// פיצול
console.log('a,b,c'.split(','));  // ['a', 'b', 'c']

Template Literals (מחרוזות תבנית)

const name = 'משה';
const age = 30;

// הדרך הישנה
const old = 'שם: ' + name + ', גיל: ' + age;

// ✅ הדרך החדשה והמומלצת
const modern = `שם: ${name}, גיל: ${age}`;

// מספר שורות
const multiLine = `
    שורה ראשונה
    שורה שנייה
    שורה שלישית
`;

// חישובים בתוך ${}
console.log(`2 + 2 = ${2 + 2}`);  // 2 + 2 = 4
console.log(`גיל בעוד 10 שנים: ${age + 10}`);

📊 מערכים (Arrays)

יצירה ופעולות בסיסיות

// Create
const fruits = ['תפוח', 'בננה', 'תפוז'];
const numbers = [1, 2, 3, 4, 5];
const mixed = [1, 'hello', true, null];

// גישה
console.log(fruits[0]);   // 'תפוח'
console.log(fruits.length); // 3

// הוספה והסרה
fruits.push('ענבים');      // הוספה לסוף
fruits.unshift('אבטיח');   // הוספה להתחלה
fruits.pop();              // הסרה מסוף
fruits.shift();            // הסרה מהתחלה

// חיפוש
console.log(fruits.includes('בננה'));  // true
console.log(fruits.indexOf('בננה'));   // 1
console.log(fruits.find(f => f === 'בננה'));  // 'בננה'

מתודות מערך חיוניות

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

// map - יוצר מערך חדש עם תוצאות הפונקציה
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]

// filter - מסנן לפי תנאי
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4]

// reduce - מצמצם לערך אחד
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15

// forEach - לולאה על כל איבר
numbers.forEach(n => console.log(n));

// find - מוצא את האיבר הראשון שעונה לתנאי
const firstEven = numbers.find(n => n % 2 === 0);
// 2

// some/every - בדיקות
console.log(numbers.some(n => n > 3));   // true - האם יש לפחות אחד?
console.log(numbers.every(n => n > 0));  // true - האם כולם?

// sort - מיון
const sorted = [...numbers].sort((a, b) => b - a);  // יורד
// [5, 4, 3, 2, 1]

🗃️ אובייקטים (Objects)

יצירה ושימוש

// Basic Create
const user = {
    name: 'ישראל ישראלי',
    age: 30,
    email: '[email protected]',
    isActive: true
};

// גישה לערכים
console.log(user.name);       // ישראל ישראלי (dot notation)
console.log(user['name']);    // ישראל ישראלי (bracket notation)

// שינוי ערכים
user.age = 31;
user['email'] = '[email protected]';

// הוספת שדות
user.phone = '050-1234567';

// מחיקת שדות
delete user.phone;

// בדיקה אם שדה קיים
console.log('name' in user);           // true
console.log(user.hasOwnProperty('name')); // true

מתודות באובייקטים

const user = {
    firstName: 'ישראל',
    lastName: 'ישראלי',

    // מתודה - פונקציה בתוך אובייקט
    getFullName() {
        return `${this.firstName} ${this.lastName}`;
    },

    // גרסה ישנה יותר (עדיין עובד)
    greet: function() {
        console.log(`שלום, אני ${this.firstName}`);
    }
};

console.log(user.getFullName());  // ישראל ישראלי
user.greet();  // שלום, אני ישראל

פעולות על אובייקטים

const user = { name: 'ישראל', age: 30 };

// קבלת מפתחות/ערכים/זוגות
Object.keys(user);    // ['name', 'age']
Object.values(user);  // ['ישראל', 30]
Object.entries(user); // [['name', 'ישראל'], ['age', 30]]

// מיזוג אובייקטים
const defaults = { theme: 'dark', lang: 'he' };
const settings = { lang: 'en' };
const merged = { ...defaults, ...settings };
// { theme: 'dark', lang: 'en' }

// העתקה (שטחית)
const copy = { ...user };
const copy2 = Object.assign({}, user);

⚡ פונקציות

סוגי פונקציות

// 1. Function Declaration - הגדרת פונקציה
function greet(name) {
    return `שלום ${name}!`;
}

// 2. Function Expression - ביטוי פונקציה
const greet2 = function(name) {
    return `שלום ${name}!`;
};

// 3. Arrow Function - פונקציית חץ (הכי נפוצה)
const greet3 = (name) => {
    return `שלום ${name}!`;
};

// Arrow Function מקוצרת (לשורה אחת)
const greet4 = (name) => `שלום ${name}!`;

// Arrow Function עם פרמטר אחד (בלי סוגריים)
const greet5 = name => `שלום ${name}!`;

פרמטרים וערכי ברירת מחדל

// Default Parameters
function greet(name = 'אורח') {
    console.log(`שלום ${name}!`);
}
greet();        // שלום אורח!
greet('משה');  // שלום משה!

// Rest parameters - מספר לא ידוע של פרמטרים
function sum(...numbers) {
    return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3, 4));  // 10

// Destructuring בפרמטרים
function printUser({ name, age }) {
    console.log(`${name} בן ${age}`);
}
printUser({ name: 'ישראל', age: 30 });

Arrow Functions ו-this

// This ⚠️ הבדל חשוב!
const obj = {
    name: 'ישראל',

    // פונקציה רגילה - this מצביע על האובייקט
    regularFunc: function() {
        console.log(this.name);  // ישראל
    },

    // Arrow function - this מצביע על ההקשר החיצוני
    arrowFunc: () => {
        console.log(this.name);  // undefined!
    }
};

// 🔑 כלל הזהב: למתודות באובייקט, השתמשו בפונקציות רגילות
// ל-callbacks, השתמשו ב-Arrow Functions

🔀 תנאים

if / else if / else

const age = 18;

if (age < 13) {
    console.log('ילד');
} else if (age < 20) {
    console.log('נער');
} else if (age < 60) {
    console.log('מבוגר');
} else {
    console.log('קשיש');
}

Ternary Operator (אופרטור תנאי)

const age = 18;
const status = age >= 18 ? 'מבוגר' : 'קטין';
console.log(status);  // מבוגר

// אפשר לשרשר (אבל לא מומלץ - קשה לקריאה)
const category = age < 13 ? 'ילד' : age < 20 ? 'נער' : 'מבוגר';

switch

const day = 'שני';

switch (day) {
    case 'ראשון':
        console.log('תחילת השבוע');
        break;
    case 'שישי':
    case 'שבת':
        console.log('סוף שבוע!');
        break;
    default:
        console.log('יום רגיל');
}

Nullish Coalescing (??)

// Nullish ?? - מחזיר את הערך הימני רק אם השמאלי הוא null או undefined
const userInput = null;
const defaultValue = userInput ?? 'ערך ברירת מחדל';
console.log(defaultValue);  // 'ערך ברירת מחדל'

// הבדל מ-||
const count1 = 0 || 10;   // 10 (כי 0 הוא falsy)
const count2 = 0 ?? 10;   // 0 (כי 0 הוא לא null/undefined)

Optional Chaining (?.)

const user = {
    name: 'ישראל',
    address: {
        city: 'תל אביב'
    }
};

// בלי Optional Chaining - יכול לזרוק שגיאה
// console.log(user.contact.phone);  // ❌ Error!

// עם Optional Chaining - בטוח
console.log(user.contact?.phone);  // undefined
console.log(user.address?.city);   // 'תל אביב'

// שילוב עם ??
const phone = user.contact?.phone ?? 'לא צוין';

🔄 לולאות

for הקלאסית

for (let i = 0; i < 5; i++) {
    console.log(i);  // 0, 1, 2, 3, 4
}

// לולאה הפוכה
for (let i = 4; i >= 0; i--) {
    console.log(i);  // 4, 3, 2, 1, 0
}

for...of (למערכים וiterables)

const fruits = ['תפוח', 'בננה', 'תפוז'];

for (const fruit of fruits) {
    console.log(fruit);
}
// תפוח
// בננה
// תפוז

// עם אינדקס
for (const [index, fruit] of fruits.entries()) {
    console.log(`${index}: ${fruit}`);
}

for...in (לאובייקטים)

const user = { name: 'ישראל', age: 30 };

for (const key in user) {
    console.log(`${key}: ${user[key]}`);
}
// name: ישראל
// age: 30

while ו-do...while

// while
let count = 0;
while (count < 3) {
    console.log(count);
    count++;
}

// do...while - מבצע לפחות פעם אחת
let num = 0;
do {
    console.log(num);
    num++;
} while (num < 3);

⚠️ טיפול בשגיאות

try...catch...finally

try {
    // קוד שעלול לזרוק שגיאה
    const result = JSON.parse('לא JSON תקין');
} catch (error) {
    // טיפול בשגיאה
    console.error('שגיאה:', error.message);
} finally {
    // מתבצע תמיד, גם אם הייתה שגיאה וגם אם לא
    console.log('סיום');
}

זריקת שגיאות

function divide(a, b) {
    if (b === 0) {
        throw new Error('לא ניתן לחלק באפס!');
    }
    return a / b;
}

try {
    const result = divide(10, 0);
} catch (error) {
    console.error(error.message);  // לא ניתן לחלק באפס!
}

סוגי שגיאות

// error שגיאה כללית
throw new Error('משהו השתבש');

// שגיאת טיפוס
throw new TypeError('צפוי מספר');

// שגיאת טווח
throw new RangeError('הערך חורג מהטווח');

// שגיאת syntax (לא ניתן לזרוק ידנית בזמן ריצה)
// throw new SyntaxError('syntax שגוי');

🔧 Destructuring (פירוק)

פירוק אובייקטים

const user = { name: 'ישראל', age: 30, city: 'תל אביב' };

// הדרך הישנה
const name1 = user.name;
const age1 = user.age;

// ✅ Destructuring
const { name, age, city } = user;
console.log(name, age, city);  // ישראל 30 תל אביב

// עם שם חדש
const { name: userName, age: userAge } = user;

// עם ערך ברירת מחדל
const { country = 'ישראל' } = user;

// פירוק מקונן
const response = { data: { users: [{ id: 1 }] } };
const { data: { users } } = response;

פירוק מערכים

const colors = ['אדום', 'ירוק', 'כחול'];

// הדרך הישנה
const first1 = colors[0];
const second1 = colors[1];

// ✅ Destructuring
const [first, second, third] = colors;
console.log(first, second);  // אדום ירוק

// דילוג על ערכים
const [, , blue] = colors;  // רק הכחול

// Rest
const [head, ...rest] = colors;
console.log(rest);  // ['ירוק', 'כחול']

// החלפת ערכים
let a = 1, b = 2;
[a, b] = [b, a];  // עכשיו a=2, b=1

🌊 Spread Operator (...)

במערכים

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// איחוד מערכים
const combined = [...arr1, ...arr2];  // [1, 2, 3, 4, 5, 6]

// העתקת מערך
const copy = [...arr1];

// הוספת ערכים
const withExtra = [0, ...arr1, 4];  // [0, 1, 2, 3, 4]

באובייקטים

const defaults = { theme: 'dark', lang: 'he' };
const userPrefs = { lang: 'en' };

// מיזוג (הערכים האחרונים גוברים)
const settings = { ...defaults, ...userPrefs };
// { theme: 'dark', lang: 'en' }

// העתקה והוספה
const newSettings = { ...settings, fontSize: 16 };

בקריאה לפונקציות

const numbers = [1, 2, 3];

// במקום apply
console.log(Math.max(...numbers));  // 3

function greet(a, b, c) {
    console.log(a, b, c);
}
greet(...numbers);  // 1 2 3

📐 מוסכמות שמות

// camelCase - משתנים ופונקציות
const userName = 'ישראל';
function getUserById(id) { }

// קבועים - UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
const API_BASE_URL = 'https://api.example.com';

// קלאסים - PascalCase
class UserAccount { }
class HttpClient { }

// קבצים - kebab-case או camelCase
// user-service.js או userService.js

// פרמטרים פרטיים (מוסכמה) - קו תחתון
class User {
    _privateField = 'פרטי';
}

// שמות משמעותיים
// ❌ לא טוב
const x = users.filter(u => u.a > 18);

// ✅ טוב
const adultUsers = users.filter(user => user.age > 18);

סיכום מהיר

נושא דוגמה
משתנים const x = 1; let y = 2;
פונקציית חץ const fn = (x) => x * 2;
Template Literal `שלום ${name}`
Destructuring const { a, b } = obj;
Spread [...arr1, ...arr2]
Optional Chaining obj?.prop?.value
Nullish Coalescing value ?? 'default'

← חזרה ליסודות | → המשך למודולים

📦 מודולים ו-npm

מודולים הם הדרך לארגן ולשתף קוד ב-Node.js. הם מאפשרים לפצל את הקוד לקבצים קטנים וניתנים לשימוש חוזר.


🧩 מהו מודול?

מודול הוא פשוט קובץ JavaScript שמייצא פונקציונליות.

┌─────────────────────────────────────────────────────┐
│                    app.js (ראשי)                     │
├─────────────────────────────────────────────────────┤
│  require('./utils')    require('http')              │
│         │                    │                      │
│         ▼                    ▼                      │
│  ┌─────────────┐     ┌─────────────┐               │
│  │  utils.js   │     │  http       │               │
│  │  (שלנו)     │     │  (מובנה)    │               │
│  └─────────────┘     └─────────────┘               │
└─────────────────────────────────────────────────────┘

🔄 CommonJS (הדרך המסורתית)

ייצוא (Export)

// utils.js

// ייצוא פונקציה בודדת
function add(a, b) {
    return a + b;
}

function subtract(a, b) {
    return a - b;
}

// אופציה 1: ייצוא נפרד
module.exports.add = add;
module.exports.subtract = subtract;

// אופציה 2: ייצוא כאובייקט (נפוץ יותר)
module.exports = {
    add,
    subtract
};

// אופציה 3: קיצור עם exports
exports.multiply = (a, b) => a * b;

ייבוא (Import)

// app.js

// ייבוא כל המודול
const utils = require('./utils');
console.log(utils.add(2, 3));  // 5

// ייבוא עם destructuring (מומלץ)
const { add, subtract } = require('./utils');
console.log(add(2, 3));  // 5
console.log(subtract(5, 2));  // 3

// ייבוא מודול מובנה
const fs = require('fs');
const path = require('path');

// ייבוא חבילה מ-node_modules
const express = require('express');

ייצוא ערך בודד

// logger.js
class Logger {
    log(message) {
        console.log(`[LOG] ${message}`);
    }
    error(message) {
        console.error(`[ERROR] ${message}`);
    }
}

module.exports = Logger;

// app.js
const Logger = require('./logger');
const logger = new Logger();
logger.log('שלום!');

🆕 ES Modules (הדרך המודרנית)

ES Modules הם התקן החדש יותר, משמשים גם בדפדפנים וגם ב-Node.js.

הפעלת ES Modules

שתי דרכים: 1. שימוש בסיומת .mjs 2. הוספת "type": "module" ל-package.json

ייצוא (Export)

// utils.mjs

// Named exports
export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}

// או בסוף הקובץ
function multiply(a, b) {
    return a * b;
}
export { multiply };

// Default export (אחד לקובץ)
export default class Calculator {
    // ...
}

ייבוא (Import)

// app.mjs

// Named imports
import { add, subtract } from './utils.mjs';
console.log(add(2, 3));

// ייבוא עם שינוי שם
import { add as addition } from './utils.mjs';

// ייבוא default
import Calculator from './utils.mjs';

// ייבוא הכל
import * as utils from './utils.mjs';
console.log(utils.add(2, 3));

// ייבוא מודולים מובנים
import fs from 'fs';
import { readFile } from 'fs/promises';

📊 השוואה: CommonJS vs ES Modules

CommonJS ES Modules
require() import
module.exports export
סינכרוני אסינכרוני
דינמי (ניתן לעשות require בתנאי) סטטי (חייב להיות בראש הקובץ)
ברירת מחדל ב-Node.js תקן JavaScript מודרני
סיומת .js סיומת .mjs או "type": "module"
// CommonJS - דינמי (מותר)
if (condition) {
    const module = require('./module');
}

// ES Modules - סטטי (אסור בראש הקובץ)
// import { something } from './module';  // חייב להיות בראש!

// ES Modules - dynamic import (מותר)
if (condition) {
    const module = await import('./module.mjs');
}

📚 מודולים מובנים (Built-in)

Node.js מגיע עם מודולים מובנים שימושיים:

fs - מערכת קבצים

const fs = require('fs');

// קריאת קובץ (סינכרוני)
const data = fs.readFileSync('file.txt', 'utf8');

// קריאת קובץ (אסינכרוני)
fs.readFile('file.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
});

path - נתיבים

const path = require('path');

path.join('/users', 'israel', 'docs');  // /users/israel/docs
path.basename('/users/file.txt');        // file.txt
path.extname('file.txt');                // .txt
path.dirname('/users/file.txt');         // /users
path.resolve('file.txt');                // נתיב מוחלט

http - שרת HTTP

const http = require('http');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('שלום עולם!');
});

server.listen(3000);

os - מערכת הפעלה

const os = require('os');

console.log(os.platform());    // win32, linux, darwin
console.log(os.cpus().length); // מספר מעבדים
console.log(os.totalmem());    // זיכרון כולל
console.log(os.homedir());     // תיקיית הבית

crypto - הצפנה

const crypto = require('crypto');

// Hash
const hash = crypto.createHash('sha256')
    .update('סיסמה')
    .digest('hex');

// UUID
const uuid = crypto.randomUUID();

url - ניתוח URLs

const url = require('url');

const myUrl = new URL('https://example.com:8080/path?name=ישראל');
console.log(myUrl.hostname);  // example.com
console.log(myUrl.port);      // 8080
console.log(myUrl.pathname);  // /path
console.log(myUrl.searchParams.get('name'));  // ישראל

📚 תיעוד מפורט למודולים מובנים

לכל מודול יש תיעוד מפורט הכולל סינטקס, פרמטרים וערכים מוחזרים:

מודול קישור
fs - קבצים ותיקיות builtin/fs.md
path - נתיבים builtin/path.md
os - מערכת הפעלה builtin/os.md
http - שרת HTTP builtin/http.md
events - EventEmitter builtin/events.md

📦 npm - מנהל החבילות

מהו npm?

npm (Node Package Manager) הוא: 1. מאגר - אתר עם מיליוני חבילות 2. כלי - תוכנה שמנהלת חבילות

פקודות בסיסיות

# Create package.json (תחילת פרויקט)
npm init
npm init -y  # עם ערכי ברירת מחדל

# התקנת חבילה
npm install express    # או npm i express
npm install mongoose lodash  # מספר חבילות

# התקנת חבילת פיתוח בלבד
npm install --save-dev jest  # או npm i -D jest

# התקנה גלובלית
npm install -g nodemon

# הסרת חבילה
npm uninstall express

# התקנת כל התלויות מ-package.json
npm install

# הרצת סקריפטים
npm run start
npm run dev
npm test  # קיצור ל-npm run test

📄 קובץ package.json

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "פרויקט לדוגמה",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "jest"
  },
  "keywords": ["example", "nodejs"],
  "author": "ישראל ישראלי",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.0.0"
  },
  "devDependencies": {
    "jest": "^29.0.0",
    "nodemon": "^3.0.0"
  }
}

הסבר גרסאות

"express": "^4.18.2"
            │ │  │
            │ │  └─ Patch (תיקוני באגים)
            │ └──── Minor (תכונות חדשות תואמות)
            └────── Major (שינויים שוברים)

^4.18.2 - מאפשר עדכון Minor ו-Patch (4.19.0, 4.18.3)
~4.18.2 - מאפשר עדכון Patch בלבד (4.18.3)
4.18.2  - גרסה מדויקת בלבד

📁 node_modules ו-.gitignore

מה זה node_modules?

תיקייה שמכילה את כל החבילות שהתקנתם ואת התלויות שלהן.

⚠️ אף פעם אל תעלו את node_modules ל-Git!

# .gitignore
node_modules/
.env

למה?

  • התיקייה יכולה להכיל אלפי קבצים
  • ניתן לשחזר אותה עם npm install

🔒 package-lock.json

קובץ שנוצר אוטומטית ושומר את הגרסאות המדויקות של כל התלויות.

✅ כן תעלו ל-Git! זה מבטיח שכל המפתחים משתמשים באותן גרסאות.


💡 חבילות פופולריות

חבילה שימוש
express framework לשרתים
mongoose עבודה עם MongoDB
axios בקשות HTTP
lodash פונקציות שימושיות
dotenv משתני סביבה מ-.env
bcrypt הצפנת סיסמאות
jsonwebtoken אימות JWT
nodemon הפעלה מחדש אוטומטית
jest בדיקות
eslint בדיקת קוד

סיכום

נושא CommonJS ES Modules
ייבוא require() import
ייצוא module.exports export
ברירת מחדל ✅ ב-Node.js דורש הגדרה

← חזרה לסינטקס | → המשך לתכנות אסינכרוני

⏳ תכנות אסינכרוני ב-Node.js

תכנות אסינכרוני הוא הלב של Node.js. זה מה שמאפשר לו להיות מהיר ויעיל.


🤔 למה צריך אסינכרוניות?

הבעיה עם קוד סינכרוני

// Code סינכרוני - חוסם!
const data = readFileSync('big-file.txt');  // ממתין 2 שניות
console.log('קובץ נקרא');
processOtherRequest();  // מתחיל רק אחרי שהקובץ נקרא

// בזמן ש-readFileSync עובד, השרת לא יכול לעשות כלום אחר!

הפתרון האסינכרוני

// Code אסינכרוני - לא חוסם!
readFile('big-file.txt', (data) => {
    console.log('קובץ נקרא');
});
processOtherRequest();  // מתחיל מיד!

// בזמן שהקובץ נקרא, השרת ממשיך לעבוד על דברים אחרים
סינכרוני:    [קריאת קובץ......] → [עיבוד בקשה] → [סיום]
                                                   זמן: 3 שניות

אסינכרוני:   [התחלת קריאה]
             [עיבוד בקשה] ← קורה במקביל!
             [קריאה הסתיימה]                       זמן: 2 שניות

📞 Callbacks

הדרך הראשונה (והישנה ביותר) לטפל באסינכרוניות.

מהו Callback?

פונקציה שנשלחת כפרמטר ומופעלת כשהפעולה מסתיימת.

const fs = require('fs');

// קריאת קובץ עם callback
fs.readFile('file.txt', 'utf8', (error, data) => {
    // הפונקציה הזו תרוץ כשהקריאה תסתיים
    if (error) {
        console.error('שגיאה:', error.message);
        return;
    }
    console.log('תוכן הקובץ:', data);
});

console.log('זה יודפס ראשון!');

מוסכמה: Error-First Callback

ב-Node.js, ה-callback תמיד מקבל את השגיאה כפרמטר הראשון:

function doSomethingAsync(callback) {
    // אם הצליח
    callback(null, result);

    // אם נכשל
    callback(new Error('משהו השתבש'));
}

// שימוש
doSomethingAsync((error, result) => {
    if (error) {
        // טיפול בשגיאה
        return;
    }
    // שימוש בתוצאה
});

Callback Hell 😱

כשיש הרבה פעולות אסינכרוניות בזו אחר זו:

// ❌ Callback Hell - קשה לקריאה ולתחזוקה
fs.readFile('file1.txt', 'utf8', (err1, data1) => {
    if (err1) throw err1;
    fs.readFile('file2.txt', 'utf8', (err2, data2) => {
        if (err2) throw err2;
        fs.readFile('file3.txt', 'utf8', (err3, data3) => {
            if (err3) throw err3;
            fs.writeFile('result.txt', data1 + data2 + data3, (err4) => {
                if (err4) throw err4;
                console.log('סיימנו!');
            });
        });
    });
});

הפתרון? Promises!


🤝 Promises

Promise הוא אובייקט שמייצג פעולה אסינכרונית שעוד לא הסתיימה.

שלושה מצבים של Promise

┌─────────────────────────────────────────────────────┐
│                     Promise                          │
├─────────────────────────────────────────────────────┤
│  Pending (ממתין) → Fulfilled (הצליח) ──→ .then()    │
│                  → Rejected (נכשל) ────→ .catch()   │
└─────────────────────────────────────────────────────┘

יצירת Promise

// Create Promise ידנית
const myPromise = new Promise((resolve, reject) => {
    // פעולה אסינכרונית
    setTimeout(() => {
        const success = true;

        if (success) {
            resolve('הפעולה הצליחה!');  // מעביר לתוצאה
        } else {
            reject(new Error('הפעולה נכשלה'));  // מעביר לשגיאה
        }
    }, 1000);
});

שימוש ב-Promise

myPromise
    .then(result => {
        console.log(result);  // 'הפעולה הצליחה!' אחרי שנייה
    })
    .catch(error => {
        console.error(error.message);
    })
    .finally(() => {
        console.log('סיום (תמיד מתבצע)');
    });

שרשור Promises

const fs = require('fs').promises;  // גרסת Promises של fs

// ✅ שרשור קריא ונקי
fs.readFile('file1.txt', 'utf8')
    .then(data1 => {
        console.log('קובץ 1:', data1);
        return fs.readFile('file2.txt', 'utf8');
    })
    .then(data2 => {
        console.log('קובץ 2:', data2);
        return fs.readFile('file3.txt', 'utf8');
    })
    .then(data3 => {
        console.log('קובץ 3:', data3);
    })
    .catch(error => {
        console.error('שגיאה:', error.message);
    });

⭐ async/await

הדרך הכי מודרנית וקריאה לעבוד עם קוד אסינכרוני.

הרעיון הבסיסי

// .then() lots
fetchData()
    .then(data => process(data))
    .then(result => save(result));

// ...אפשר לכתוב כמו קוד סינכרוני!
async function main() {
    const data = await fetchData();
    const result = await process(data);
    await save(result);
}

חוקים חשובים

// 1. await יכול להופיע רק בתוך פונקציית async
async function myFunction() {
    const result = await somePromise();
}

// 2. פונקציית async תמיד מחזירה Promise
async function greet() {
    return 'שלום';
}
greet().then(msg => console.log(msg));  // 'שלום'

// 3. אפשר להשתמש ב-await עם כל Promise
const fs = require('fs').promises;

async function readFiles() {
    const data = await fs.readFile('file.txt', 'utf8');
    console.log(data);
}

טיפול בשגיאות עם try/catch

async function readFile() {
    try {
        const data = await fs.readFile('file.txt', 'utf8');
        console.log(data);
        return data;
    } catch (error) {
        console.error('שגיאה בקריאת הקובץ:', error.message);
        throw error;  // העברת השגיאה הלאה (אופציונלי)
    }
}

דוגמה מלאה

const fs = require('fs').promises;

async function processFiles() {
    try {
        // קריאת שלושה קבצים בזה אחר זה
        const data1 = await fs.readFile('file1.txt', 'utf8');
        const data2 = await fs.readFile('file2.txt', 'utf8');
        const data3 = await fs.readFile('file3.txt', 'utf8');

        // עיבוד
        const result = data1 + data2 + data3;

        // כתיבה
        await fs.writeFile('result.txt', result);
        console.log('הכל הושלם!');

    } catch (error) {
        console.error('שגיאה:', error.message);
    }
}

// הפעלה
processFiles();

⚡ Promise.all ו-Promise.race

Promise.all - הרצה במקביל

ממתין שכל ה-Promises יסתיימו.

const fs = require('fs').promises;

async function readAllFiles() {
    // ❌ לא יעיל - קובץ אחרי קובץ
    const data1 = await fs.readFile('file1.txt', 'utf8');  // 1 שנייה
    const data2 = await fs.readFile('file2.txt', 'utf8');  // 1 שנייה
    const data3 = await fs.readFile('file3.txt', 'utf8');  // 1 שנייה
    // סה"כ: 3 שניות

    // ✅ יעיל - במקביל!
    const [data1, data2, data3] = await Promise.all([
        fs.readFile('file1.txt', 'utf8'),
        fs.readFile('file2.txt', 'utf8'),
        fs.readFile('file3.txt', 'utf8')
    ]);
    // סה"כ: ~1 שנייה
}

⚠️ שימו לב: אם Promise אחד נכשל, כל ה-Promise.all נכשל!

Promise.allSettled - גם אם יש כשלונות

const results = await Promise.allSettled([
    fs.readFile('file1.txt', 'utf8'),
    fs.readFile('file2.txt', 'utf8'),
    fs.readFile('nonexistent.txt', 'utf8')  // יכשל
]);

results.forEach(result => {
    if (result.status === 'fulfilled') {
        console.log('הצליח:', result.value);
    } else {
        console.log('נכשל:', result.reason.message);
    }
});

Promise.race - הראשון שמסיים מנצח

// Usefull ל-timeout
async function fetchWithTimeout(url, timeout) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) => 
            setTimeout(() => reject(new Error('Timeout')), timeout)
        )
    ]);
}

try {
    const response = await fetchWithTimeout('https://api.example.com', 5000);
} catch (error) {
    console.log('הבקשה ארכה יותר מדי זמן');
}

Promise.any - הראשון שמצליח

// Search את השרת הזמין הראשון
const result = await Promise.any([
    fetch('https://server1.com'),
    fetch('https://server2.com'),
    fetch('https://server3.com')
]);
// מחזיר את התגובה מהשרת שענה ראשון

🎯 דפוסים נפוצים

IIFE (Immediately Invoked Function Expression)

// async הפעלה מיידית של פונקציית 
(async () => {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
})();

עיבוד מערך אסינכרוני

const urls = ['url1', 'url2', 'url3'];

// ❌ forEach לא מחכה ל-await!
urls.forEach(async (url) => {
    const data = await fetch(url);  // לא מחכה!
});

// ✅ אופציה 1: for...of (בזה אחר זה)
for (const url of urls) {
    const data = await fetch(url);
    console.log(data);
}

// ✅ אופציה 2: Promise.all (במקביל)
const results = await Promise.all(
    urls.map(url => fetch(url))
);

Sequential vs Parallel

// Sequential (סדרתי) - כל אחד ממתין לקודם
async function sequential() {
    const a = await task1();  // 1 שנייה
    const b = await task2();  // 1 שנייה
    const c = await task3();  // 1 שנייה
    // סה"כ: 3 שניות
}

// Parallel (מקבילי) - כולם יחד
async function parallel() {
    const [a, b, c] = await Promise.all([
        task1(),  // 1 שנייה
        task2(),  // 1 שנייה
        task3()   // 1 שנייה
    ]);
    // סה"כ: ~1 שנייה
}

🔧 המרת Callbacks ל-Promises

שימוש ב-util.promisify

const util = require('util');
const fs = require('fs');

// המרה ידנית
const readFilePromise = util.promisify(fs.readFile);

async function main() {
    const data = await readFilePromise('file.txt', 'utf8');
    console.log(data);
}

// או פשוט להשתמש ב-fs.promises
const fsPromises = require('fs').promises;

עטיפה ידנית

function readFileAsync(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf8', (error, data) => {
            if (error) {
                reject(error);
            } else {
                resolve(data);
            }
        });
    });
}

📝 סיכום

שיטה יתרונות חסרונות
Callbacks פשוט, עובד בכל מקום Callback Hell
Promises שרשור נקי עדיין מעט מסורבל
async/await קריא כמו קוד סינכרוני דורש try/catch

כללי אצבע

  1. השתמשו ב-async/await - הכי קריא ונוח
  2. תמיד טפלו בשגיאות - try/catch או .catch()
  3. השתמשו ב-Promise.all - כשצריך לעשות דברים במקביל
  4. הימנעו מ-forEach עם async - השתמשו ב-for...of או map

← חזרה למודולים | → חזרה לדף הראשי