מפתח
ברוכים הבאים!
פרויקט זה מיועד ללימוד Node.js למתחילים. הפרויקט כולל תיעוד מפורט בעברית ודוגמאות קוד מעשיות עם דגש על סינטקס וצורת כתיבה.
📋 תוכן עניינים
תיעוד (docs/)
- רקע והיסטוריה - מהו Node.js ומאיפה הוא הגיע
- יסודות - מושגים בסיסיים שחייבים לדעת
- סינטקס וצורת כתיבה - איך כותבים קוד ב-Node.js
- מודולים ו-npm - ניהול קוד וחבילות
- תכנות אסינכרוני - Promises ו-async/await
דוגמאות קוד (examples/)
01-basics/- יסודות: משתנים, פונקציות ותנאים02-modules/- עבודה עם מודולים03-fs/- קריאה וכתיבה לקבצים04-http/- יצירת שרת HTTP05-async/- תכנות אסינכרוני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
📚 סדר לימוד מומלץ
- התחילו מהרקע - הבינו מה זה Node.js ולמה הוא שונה
- למדו את היסודות - הכירו את הסביבה והכלים
- תרגלו סינטקס - כתבו קוד בסיסי
- הבינו מודולים - למדו לארגן קוד נכון
- שלטו באסינכרוניות - המפתח להצלחה ב-Node.js
- בנו משהו משלכם - השרת הראשון שלכם!
💡 טיפים למתחילים
טיפ 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 |
כללי אצבע
- השתמשו ב-async/await - הכי קריא ונוח
- תמיד טפלו בשגיאות - try/catch או .catch()
- השתמשו ב-Promise.all - כשצריך לעשות דברים במקביל
- הימנעו מ-forEach עם async - השתמשו ב-for...of או map