الدالة هي عبارة عن "كتلة من الكود" (Code Block) مصممة لتنفيذ مهمة محددة. بدلاً من كتابة نفس الكود 10 مرات، تكتبه مرة واحدة وتستدعيه وقتما شئت.
دي الطريقة التقليدية لكتابة الدالة في JavaScript.
function sayHello() {
console.log("Hello");
// السطر ده هيطبع كلمة Hello في الـ console
}
📌 هنا:
الدالة لا تعمل أبداً وهي "نائمة" إلا إذا قمت بمناداتها (استدعائها).
sayHello(); // هنا بنقول للبرنامج: نفّذ الدالة الآن!
بدون الأقواس ()، أنت تشير للدالة فقط ولا تشغلها. الأقواس هي "زر التشغيل".
function sayHello() {
console.log("مرحباً بك في موقعنا!");
}
sayHello(); // استدعاء: سيطبع الجملة
الدالة ممكن تستقبل بيانات عشان تكون مرنة وتنفذ مهام مختلفة لنفس الكود.
function greet(name) { // name هنا هو Parameter
console.log("Hello " + name);
}
greet("Eslam"); // "Eslam" هنا هو Argument
📌 الفكرة:
Parameter: هو الكوب الفاضي.
Argument: هو الماية اللي بتحطها في الكوب.
يستخدم للعرض فقط (للمبرمج) لتصحيح الأخطاء.
function sum(a, b) {
console.log(a + b); // عرض فقط
}
❌ المشكلة: لا يمكن تخزين النتيجة أو استخدامها في عملية حسابية أخرى.
يُرجع قيمة حقيقية للكود لكي نستخدمها لاحقاً.
function sum(a, b) {
return a + b; // إرجاع الناتج
}
let result = sum(3, 5); // result = 8
✅ الميزة: النتيجة أصبحت ملكك، يمكنك ضربها، جمعها، أو تخزينها.
بمجرد وصول الدالة لسطر return، تتوقف فوراً وتخرج. أي كود مكتوب بعدها لا يتم تنفيذه نهائياً.
تعال نطبق اللي اتعلمناه في مثال حقيقي: حساب السعر النهائي لمنتج في متجر إلكتروني بعد إضافة الضريبة.
function calculateFinalPrice(price, tax) {
// price = سعر المنتج الحقيقي
// tax = نسبة الضريبة (مثلاً 0.14 للـ 14%)
let finalPrice = price + (price * tax);
return finalPrice; // بنرجع السعر النهائي عشان نستخدمه في الفاتورة
}
let productPrice = calculateFinalPrice(100, 0.14); // price=100, tax=0.14
console.log("السعر النهائي: " + productPrice); // الناتج = 114
Arrow Functions هي طريقة مختصرة لكتابة الدوال.
function sum(a, b) {
return a + b;
}
const sum = (a, b) => {
return a + b;
};
const sum = (a, b) => a + b;
📌 مميزات Arrow Functions:
متغير متعرف بره الدالة.
let siteName = "JS Guide";
function showName() {
console.log(siteName);
// يقدر يشوف المتغير
}
متغير جوه الدالة.
function testScope() {
let x = 10;
console.log(x);
}
// console.log(x); ❌ Error
📌 السبب: المتغير x عايش جوه الدالة بس، بره الدالة مش موجود.
لو المستخدم مبعتش قيمة.
function welcome(name = "Guest") {
return "Welcome " + name;
}
welcome(); // Welcome Guest
welcome("Islam"); // Welcome Islam
✔️ تمنع Errors | ✔️ تجعل الدالة أذكى
Callback = دالة بتتبعت لدالة تانية.
function runTask(task) {
task();
// هنا بنشغّل الدالة اللي جت
}
runTask(function () {
console.log("Task Done");
});
📌 تستخدم في: click events | setTimeout | async code
اكتب دالة مختصرة (Arrow Function) باسم getDiscount تحسب السعر بعد خصم 20%.
// المطلوب منك:
// 1. الدالة تستقبل السعر (price) كـ Parameter.
// 2. ترجع السعر بعد الخصم (السعر مضروب في 0.8).
// 3. استخدم الـ Arrow Function للاختصار.
// مثلاً: const getDiscount = (price) => ...
الـ String هو نص. أي حاجة بين العلامات دي تعتبر String:
" " أو ' ' أو ` `
let name = "Eslam";
let city = 'Cairo';
let message = `Hello`;
مهمة جدًا لأنها تسمح بـ:
let name = "Eslam";
let text = `Hello ${name}`; // دي اسمها: Template Literals
let result = first + " " + second;
let result = `${first} ${second}`;
"10" + 5 // "105"
لو في String → العملية تبقى دمج مش جمع حسابي.
📌 الطول (length):
let name = "Eslam";
console.log(name.length); // 5
📌 الوصول لحرف معين:
let word = "Hello";
console.log(word[0]); // H
console.log(word[1]); // e
💡 أول حرف دايمًا index = 0
1️⃣ toUpperCase() : تحويل النص لحروف كبيرة
2️⃣
toLowerCase() : تحويل لحروف صغيرة
"hello".toUpperCase(); // HELLO // تحويل النص لحروف كبيرة toUpperCase
"HELLO".toLowerCase(); // hello // تحويل لحروف صغيرة toLowerCase
3️⃣ trim(): إزالة المسافات من البداية والنهاية.
let text = " hello ";
console.log(text.trim()); // "hello"
💡 مهم جدًا في: إدخال المستخدم والفورمات.
4️⃣ includes() : يتأكد هل النص يحتوي على جزء معين
let text = "Hello World";
console.log(text.includes("World")); // true
5️⃣ startsWith(): بتتحقق هل النص بيبدأ بجزء معين ولا لأ.
let text = "Hello"; // متغير فيه نص
console.log(text.startsWith("He"));
// بيتأكد هل النص يبدأ بـ "He"
// النتيجة: true
console.log(text.startsWith("he"));
// هنا الحروف مختلفة (H كبيرة)
// JavaScript حساسة لحالة الحروف
// النتيجة: false
💡 مفيدة جدًا في: التحقق من بداية إيميل التأكد من صيغة معينة فلترة بيانات
6️⃣ endsWith(): بتتحقق هل النص بينتهي بجزء معين.
let text = "Hello"; // متغير فيه نص
console.log(text.endsWith("lo"));
// بيتأكد هل النص بينتهي بـ "lo"
// النتيجة: true
console.log(text.endsWith("Lo"));
// اختلاف في حالة الحروف
// النتيجة: false
7️⃣ indexOf(): ترجع رقم مكان أول ظهور لقيمة داخل النص . لو مش موجودة → ترجع -1
let text = "Hello World"; // النص الأساسي
console.log(text.indexOf("o"));
// بيدور على أول حرف o
// أول o موجود في المكان رقم 4
// النتيجة: 4
console.log(text.indexOf("z"));
// الحرف z مش موجود
// النتيجة: -1
8️⃣ lastIndexOf(): ترجع رقم مكان أخر ظهور لقيمة داخل النص لو مش موجودة → ترجع -1
let text = "Hello World";
console.log(text.lastIndexOf("o"));
// فيه o مرتين
// آخر واحدة في المكان رقم 7
// النتيجة: 7
9️⃣ slice(start, end): تستخدم لاستخراج جزء من النص.
let text = "JavaScript"; // النص الأساسي
console.log(text.slice(0, 4));
// يبدأ من index 0
// يقف قبل index 4
// النتيجة: "Java"
console.log(text.slice(4));
// لو حددنا بداية فقط
// هياخد من 4 لآخر النص
// النتيجة: "Script"
🔟 substring(): شبيهة بـ slice لكن لا تدعم الأرقام السالبة وأقل استخدامًا.
let text = "JavaScript";
console.log(text.substring(0, 4));
// نفس فكرة slice
// النتيجة: "Java"
1️⃣1️⃣ replace() : تغير جزء من النص
لكن تغير أول تطابق فقط
replaceAll() : تغير كل التطابقات.
let text = "Hello World World";
console.log(text.replace("World", "Ali"));
// هيغير أول World بس
// النتيجة: "Hello Ali World"
console.log(text.replaceAll("World", "Ali"));
// هيغير كل الكلمات
// النتيجة: "Hello Ali Ali"
1️⃣2️⃣ split(): تحويل النص إلى Array (مهمة جدًا).
let text = "apple,banana,orange";
let result = text.split(","); // ["apple", "banana", "orange"]
1️⃣3️⃣ charAt(): ترجع حرف معين حسب مكانه.
let text = "Hello";
console.log(text.charAt(0));
// يرجع الحرف في المكان 0
// النتيجة: H
1️⃣4️⃣ repeat(): تكرر النص عدد معين من المرات.
console.log("Hi ".repeat(3));
// يكرر النص 3 مرات
// النتيجة: "Hi Hi Hi "
1️⃣ Strings غير قابلة للتعديل (Immutable) : لا يمكن تعديل حرف داخل النص مباشرة. لازم تنشئ نص جديد.
let name = "Ali"; // النص الأصلي
name[0] = "M"; // نحاول نغير أول حرف
console.log(name);
// النتيجة: Ali
// لم يتغير لأنه Immutable
2️⃣ مقارنة النصوص: JavaScript حساسة لحالة الحروف.
console.log("Ali" === "Ali");
// نفس النص → true
console.log("ali" === "Ali");
// اختلاف في حالة الحروف → false
3️⃣ تحويل رقم إلى String
let num = 100; // رقم
let text = String(num); // تحويل إلى نص
console.log(typeof text);
// النتيجة: "string"
4️⃣ تحويل String إلى رقم
let text = "100"; // نص
let num = Number(text); // تحويل إلى رقم
console.log(typeof num);
// النتيجة: "number"
trim() عند استقبال بيانات من المستخدم. index يبدأ من 0. replace وتوقع إنه يغير كل الكلمات (استخدم replaceAll).toLowerCase().
function formatUserData(input) {
// 1️⃣ إزالة المسافات الزائدة من البداية والنهاية
let cleanedInput = input.trim();
// 2️⃣ تقسيم النص باستخدام الفاصلة
let parts = cleanedInput.split(",");
// إزالة المسافات من كل عنصر بعد التقسيم
let name = parts[0].trim();
let age = parts[1].trim();
let job = parts[2].trim();
// 3️⃣ التحقق إن العمر رقم
let numericAge = Number(age);
if (isNaN(numericAge)) {
return "Invalid age value";
}
// 4️⃣ التحقق من طول الاسم
if (name.length < 3) { return "Name is too short" ; }
// 5️⃣ تنسيق الاسم (أول حرف كابيتال والباقي small) let
formattedName=name[0].toUpperCase() + name.slice(1).toLowerCase();
// 6️⃣ تحويل الوظيفة إلى حروف كبيرة let
formattedJob=job.toUpperCase();
// 7️⃣ إرجاع النتيجة النهائية
return `User ${formattedName} is ${numericAge} years old and works as ${formattedJob}.`; }
console.log(formatUserData(" eslam, 21, frontend developer "));
الـ Object في JavaScript هو هيكل بيانات يخزن البيانات في شكل أزواج (مفتاح: قيمة). يمكن إنشاؤه بطريقتين رئيسيتين: القول الحرفي (Literal) وهو الأكثر شيوعاً، أو باستخدام الكلمة new.
// الطريقة الأولى: باستخدام Object Literal (الأكثر شيوعاً)
const person = { // تعريف كائن اسمه person وتخزينه في ثابت const
firstName: "Ahmed", // تعريف خاصية firstName وقيمتها "Ahmed"
lastName: "Mohamed", // تعريف خاصية lastName وقيمتها "Mohamed"
age: 25, // تعريف خاصية age وقيمتها الرقم 25
isStudent: true // تعريف خاصية isStudent وقيمتها true (منطقية)
}; // إغلاق تعريف الكائن بفاصلة منقوطة
// الطريقة الثانية: باستخدام دالة البناء new Object()
const car = new Object(); // إنشاء كائن فارغ جديد باستخدام الكلمة المفتاحية new
car.brand = "Toyota"; // إضافة خاصية brand للكائن car وتعيين قيمتها
car.model = "Corolla"; // إضافة خاصية model للكائن car
car.year = 2022; // إضافة خاصية year للكائن car
للوصول لقيمة خاصية معينة، لدينا طريقتان: النقطة . والأقواس المربعة [ ].
const user = { // تعريف كائن user
name: "Sara", // خاصية name
"user age": 30 // خاصية تحتوي على مسافة (يجب استخدام علامات تنصيص)
};
// الوصول باستخدام Dot Notation (النقطة)
console.log(user.name); // طباعة قيمة الخاصية name (النتيجة: Sara)
// الوصول باستخدام Bracket Notation (الأقواس)
console.log(user["name"]); // طباعة قيمة الخاصية name أيضاً (النتيجة: Sara)
// حالة خاصة: لا يمكن استخدام النقطة مع المفاتيح التي تحتوي على مسافات
console.log(user["user age"]); // طباعة القيمة 30 (صحيح)
// console.log(user.user age); // هذا سيعطي خطأ (SyntaxError) لأن المسافة غير مسموحة في النقطة
// استخدام متغير لاسم المفتاح (Computed Property Name)
let key = "name"; // تعريف متغير يحمل اسم الخاصية
console.log(user[key]); // طباعة القيمة باستخدام المتغير key (النتيجة: Sara)
الكائنات في JavaScript قابلة للتغيير (Mutable)، مما يعني أننا يمكننا تعديل قيمها، إضافة خصائص جديدة، أو حذفها في أي وقت.
let book = { // تعريف كائن book
title: "JS Guide", // خاصية العنوان
pages: 200 // خاصية عدد الصفحات
};
// تعديل خاصية موجودة
book.pages = 250; // تغيير قيمة pages من 200 إلى 250
// إضافة خاصية جديدة
book.author = "John"; // إضافة خاصية author لم تكن موجودة من قبل
book.isAvailable = true; // إضافة خاصية isAvailable
// حذف خاصية
delete book.isAvailable; // حذف الخاصية isAvailable من الكائن تماماً
console.log(book); // طباعة الكائن (لن نجد isAvailable)
الـ Method هي دالة (Function) يتم تخزينها كقيمة لأحد خصائص الكائن. تسمح للكائن بتنفيذ أفعال.
const calculator = { // تعريف كائن آلة حاسبة
add: function(a, b) { // تعريف دالة (Method) اسمها add
return a + b; // إرجاع حاصل الجمع
},
subtract: (a, b) => a - b // تعريف دالة سهمية (Arrow Function) اسمها subtract
};
console.log(calculator.add(5, 3)); // استدعاء الدالة add وطباعة النتيجة (8)
console.log(calculator.subtract(10, 4)); // استدعاء الدالة subtract (6)
كلمة this تشير إلى الكائن الذي "يملك" الكود الذي يتم تنفيذه حالياً. داخل الـ Method، تشير this إلى الكائن نفسه.
const profile = { // تعريف كائن profile
name: "Eslam", // خاصية الاسم
age: 21, // خاصية العمر
introduce: function() { // دالة للتعريف بالنفس
// this.name هنا تشير إلى خاصية name الموجودة في هذا الكائن (profile)
return "My name is " + this.name + " and I am " + this.age;
}
};
console.log(profile.introduce()); // طباعة: My name is Eslam and I am 21
عندما نسخ كائناً إلى متغير آخر، لا يتم نسخ البيانات، بل يتم نسخ "الإشارة" أو "العنوان" في الذاكرة.
const obj1 = { a: 1 }; // تعريف كائن أصلي
const obj2 = obj1; // نسخ المرجع (obj2 يشير لنفس مكان obj1)
obj2.a = 99; // تعديل القيمة باستخدام obj2
console.log(obj1.a); // طباعة obj1 سيعطي 99 (تأثر بالتعديل!)
console.log(obj2.a); // طباعة obj2 سيعطي 99
لحل مشكلة المرجع، نستخدم طرقاً لإنشاء نسخة جديدة، لكن هذه الطرق (مثل Spread أو Object.assign) تنسخ فقط الطبقة الأولى.
const original = { // كائن أصلي
name: "Test", // خاصية بسيطة
details: { // كائن داخلي (Nested)
id: 5
}
};
// النسخ باستخدام Spread Syntax (...)
const shallowCopy = { ...original }; // إنشاء كائن جديد ونسخ خصائص original فيه
shallowCopy.name = "Changed"; // تغيير خاصية بسيطة (لا يؤثر على الأصلي)
shallowCopy.details.id = 100; // تغيير خاصية داخل الكائن الداخلي (سيؤثر على الأصلي!)
console.log(original.name); // الناتج: "Test" (لم يتغير)
console.log(original.details.id); // الناتج: 100 (تغير! لأن details لا يزال مشتركاً)
للحصول على استقلالية تامة، يجب عمل نسخة عميقة تنسخ الكائنات الداخلية أيضاً.
const original = {
name: "Deep",
data: { x: 10 }
};
// تحويل الكائن إلى نص JSON ثم إعادة تحويله لكائن (نسخة مستقلة تماماً)
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.data.x = 999; // تعديل الكائن الداخلي في النسخة العميقة
console.log(original.data.x); // الناتج: 10 (الأصلي لم يتأثر، هذا هو الهدف من النسخ العميق)
يمكننا دمج أكثر من كائن في كائن واحد.
const objA = { a: 1, b: 2 }; // الكائن الأول
const objB = { c: 3, b: 4 }; // الكائن الثاني (لاحظ تكرار المفتاح b)
// استخدام Spread لدمجهم (الكائن الأخير يطابق القيم المتكررة)
const merged = { ...objA, ...objB };
console.log(merged); // الناتج: { a: 1, b: 4, c: 3 } (قيمة b أخذت من objB)
تقنية لاستخراج قيم من الكائن ووضعها في متغيرات بسرعة.
const name = user.name;
نكتب
const { name } = user;
const user = { id: 1, username: "admin", role: "editor" };
// استخراج username و role إلى متغيرات مستقلة
const { username, role } = user;
console.log(username); // الناتج: "admin"
console.log(role); // الناتج: "editor"
// console.log(id); // خطأ لأننا لم نستخرج id
إنشاء مفاتيح للكائن تعتمد على قيم متغيرات.
const keyName = "dynamicKey"; // متغير يحمل اسم المفتاح
const value = 100; // متغير يحمل القيمة
const obj = { // تعريف كائن
[keyName]: value // استخدام المفتاح المحسوب (سيكون اسم الخاصية dynamicKey)
};
console.log(obj.dynamicKey); // الناتج: 100
الوصول الآمن للخصائص لتجنب الأخطاء إذا كانت الخاصية null أو undefined.
const user = { name: "Khaled" }; // كائن ليس لديه خاصية address
console.log(user.address.street); // خطأ! (Cannot read properties of undefined)
console.log(user.address?.street); // آمن: الناتج undefined (بدون خطأ)
توفير قيمة افتراضية فقط إذا كانت القيمة null أو undefined (وليس 0 أو false).
const settings = { volume: 0 }; // الصفر قيمة صالحة
// || تعتبر الصفر قيمة خاطئة (falsy)
const v1 = settings.volume || 10; // الناتج: 10 (خطأ لأننا أردنا 0)
// ?? تعتبر فقط null و undefined قيم مفقودة
const v2 = settings.volume ?? 10; // الناتج: 0 (صحيح، لأن القيمة موجودة)
كل كائن في JavaScript له خاصية مخفية تسمى prototype ترث منها الخصائص.
const animal = { eats: true }; // كائن أب
const rabbit = { jumps: true }; // كائن ابن
// ربط rabbit بـ animal كـ prototype
rabbit.__proto__ = animal; // (طريقة قديمة للشرح فقط)
console.log(rabbit.eats); // الناتج: true (ورثها من animal)
console.log(rabbit.jumps); // الناتج: true (خاصية الأصل)
دوال تُستخدم لإنشاء كائنات متشابهة باستخدام الكلمة new.
function User(name) { // تعريف دالة منشئ (نكتب أول حرف كبير عادة)
this.name = name; // this هنا يشير للكائن الذي سيُنشأ
this.isAdmin = false; // إضافة خاصية افتراضية
}
const user1 = new User("Omar"); // إنشاء كائن جديد
console.log(user1.name); // الناتج: "Omar"
console.log(user1.isAdmin); // الناتج: false
طريقة حديثة وأكثر تنظيماً للكتابة البرمجية كائنية التوجه (OOP)، لكنها في الأساس "سكر نحوي" للـ Prototypes.
class Vehicle { // تعريف كلاس (قالب) اسمه Vehicle
constructor(brand) { // دالة البناء تُنفذ عند إنشاء كائن جديد
this.brand = brand; // تخزين العلامة التجارية في الكائن
}
showBrand() { // دالة (Method) داخل الكلاس
console.log("Brand: " + this.brand);
}
}
const car = new Vehicle("BMW"); // إنشاء كائن (instance) من الكلاس
car.showBrand(); // استدعاء الدالة (النتيجة: Brand: BMW)
استخدام extends لإنشاء كلاس جديد يرث خصائص كلاس قديم، و super لاستدعاء الكلاس الأب.
class Car extends Vehicle { // الكلاس Car يرث من Vehicle
constructor(brand, model) {
super(brand); // استدعاء constructor الأب لإرسال brand
this.model = model; // إضافة خاصية خاصة بالابن
}
showInfo() {
console.log(this.brand + " " + this.model);
}
}
const myCar = new Car("Mercedes", "C200"); // إنشاء كائن من الابن
myCar.showInfo(); // الناتج: Mercedes C200
دوال تسمح لنا بالتحكم في قراءة وتعديل الخصائص (كأنها خصائص عادية).
const userObj = {
firstName: "Ali",
lastName: "Hassan",
// Getter: دالة تُنفذ عند طلب fullName
get fullName() {
return this.firstName + " " + this.lastName;
},
// Setter: دالة تُنفذ عند تعيين قيمة لـ fullName
set fullName(value) {
const parts = value.split(" "); // تقسيم النص
this.firstName = parts[0]; // تحديث الاسم الأول
this.lastName = parts[1]; // تحديث اسم العائلة
}
};
console.log(userObj.fullName); // تنفيذ get (النتيجة: Ali Hassan)
userObj.fullName = "Sara Ahmed"; // تنفيذ set وتحديث البيانات
console.log(userObj.firstName); // النتيجة: Sara
التحكم الدقيق في سلوك الخاصية (قابلة للكتابة؟، تظهر في الحلقات؟، قابلة للحذف؟).
const descriptorObj = { name: "Test" }; // كائن عادي
// منع كتابة (تعديل) الخاصية
Object.defineProperty(descriptorObj, "name", {
writable: false, // جعل الخاصية للقراءة فقط
configurable: false // منع حذف الخاصية
});
descriptorObj.name = "New Name"; // لن ينجح في الوضع الصارم
console.log(descriptorObj.name); // النتيجة: Test
منع تغيير الكائن نهائياً أو جزئياً.
const immutableObj = { a: 1, b: 2 };
// Freeze: يجمد الكائن (لا إضافة، لا حذف، لا تعديل)
Object.freeze(immutableObj);
immutableObj.a = 99; // لن يحدث شيء (صامت في الوضع العادي)
immutableObj.c = 3; // لن يتم الإضافة
console.log(immutableObj); // { a: 1, b: 2 }
// Seal: يغلق الكائن (لا إضافة، لا حذف)، لكن يسمح بتعديل القيم الموجودة
const sealedObj = { x: 10 };
Object.seal(sealedObj);
sealedObj.x = 20; // مسموح (تعديل)
// sealedObj.y = 5; // ممنوع (إضافة)
للمرور على مفاتيح الكائن (Keys).
const data = { a: 10, b: 20, c: 30 };
for (let key in data) { // حلقة تمر على كل مفتاح
console.log(key); // طباعة المفتاح (a, b, c)
console.log(data[key]); // طباعة القيمة باستخدام المفتاح
}
توفر JavaScript طرقاً سريعة لاستخراج البيانات من الكائن وتحويلها إلى مصفوفات (Arrays) ليسهل التعامل معها:
Object.keys()
إرجاع مصفوفة تحتوي على جميع **المفاتيح** (Keys) الموجودة في الكائن.
Object.keys(user); // ["id", "name"]
Object.values()
إرجاع مصفوفة تحتوي على جميع **القيم** (Values) الموجودة في الكائن.
Object.values(user); // [1, "Ahmed"]
Object.entries()
إرجاع مصفوفة من الأزواج، كل زوج عبارة عن مصفوفة صغيرة
[key, value].
Object.entries(user);
// [["id", 1], ["name", "Ahmed"]]
const session = { id: 1, user: "guest" };
const keys = Object.keys(session); // ['id', 'user']
const values = Object.values(session); // [1, 'guest']
const entries = Object.entries(session); // [['id', 1], ['user', 'guest']]
console.log(keys); // طباعة المفاتيح
نستخدم JSON (JavaScript Object Notation) كصيغة عالمية لتحويل الكائنات إلى نصوص لإرسالها عبر الإنترنت إلى الخوادم (Servers)، ثم إعادتها لأصلها عند الاستلام.
const myData = { id: 55, title: "Product A" };
// 1. Serialization: تحويل الـ Object إلى نص JSON
const jsonText = JSON.stringify(myData);
console.log(jsonText);
// الناتج: '{"id":55,"title":"Product A"}' (نص جاهز للإرسال)
// 2. Deserialization: تحويل نص JSON إلى Object مرة أخرى
const backToObject = JSON.parse(jsonText);
console.log(backToObject.title);
// الناتج: "Product A" (كائن يمكن التعامل معه برمجياً)
في عام 2022، أضافت JavaScript طريقة رسمية لعمل خصائص "خاصة" (Private) لا يمكن الوصول إليها من خارج الكائن، باستخدام الرمز # قبل اسم الخاصية.
class BankAccount {
#balance = 0; // خاصية خاصة (Private Field) - لا يمكن الوصول لها من خارج الكلاس
constructor(owner) {
this.owner = owner; // خاصية عامة
}
deposit(amount) {
this.#balance += amount; // مسموح الوصول لها هنا لأننا داخل الكلاس
console.log(`تم الإيداع، الرصيد الحالي: ${this.#balance}`);
}
showBalance() {
return this.#balance; // دالة خاصة لتقديم الرصيد للخارج بشكل آمن
}
}
const myAccount = new BankAccount("Ali");
myAccount.deposit(500);
// console.log(myAccount.#balance); // هذا سيعطي خطأ (SyntaxError) لأن الخاصية خاصة
console.log(myAccount.showBalance()); // الطريقة الصحيحة للوصول (500)
في ES6، يمكننا كتابة الدوال داخل الكائنات (ليس الكلاسات) بطريقة مختصرة دون استخدام الكلمة function.
sayHello: function() {}، نكتب sayHello() {}.
هذا يجعل الكود أنظف.
const user = {
name: "Mona",
// الطريقة القديمة (Old Syntax)
greetOld: function() {
return "Hello";
},
// الطريقة المختصرة الحديثة (Method Shorthand)
greetNew() { // لا نحتاج لكتابة : function
return "Hi, " + this.name;
}
};
console.log(user.greetNew()); // الناتج: Hi, Mona
لدينا طريقتان للتحقق مما إذا كانت خاصية موجودة:
hasOwnProperty
و
in.
key in obj
يعود بـ true إذا وجد المفتاح في الكائن نفسه أو ورثه من أب.
const parent = { inheritedProp: 100 };
const child = Object.create(parent); // child يرث من parent
child.ownProp = 200; // خاصية خاصة بالابن
console.log("inheritedProp" in child); // true (وجدت في الـ prototype)
console.log("ownProp" in child); // true (وجدت في الكائن نفسه)
console.log(child.hasOwnProperty("inheritedProp")); // false (ليست خاصة به)
console.log(child.hasOwnProperty("ownProp")); // true (خاصة به)
في JavaScript، الكائنات تُقارن بالمرجع (Reference) وليس بالقيمة (Value).
أيضاً، هناك دالة Object.is() تختلف قليلاً عن معامل المقارنة
الصارم ===.
obj1 === obj2
تكون true فقط إذا كانا يشيران لنفس المكان في الذاكرة.
Object.is(v1, v2)
تشبه === لكنها تعتبر أن NaN
يساوي نفسه.
const a = { x: 1 };
const b = { x: 1 };
// 1. المقارنة بالمرجع
console.log(a === b); // false (هما كائنان مختلفان في الذاكرة رغم تشابه المحتوى)
// 2. Object.is (للمتغيرات العادية والقيم الخاصة)
console.log(NaN === NaN); // false (غريب لكن صحيح في JS)
console.log(Object.is(NaN, NaN)); // true (تعالج هذه الحالة)
console.log(Object.is(-0, 0)); // false (تفرق بين الصفر الموجب والسالب)
يُستخدم للتحقق مما إذا كان كائن معين قد تم إنشاؤه بواسطة كلاس (Class) معين أو دالة بناء
(Constructor) معينة.
Prototype ليرى ما إذا كان prototype للدالة البناء موجوداً في سلسلة
وراثة الكائن.
class Animal { }
class Dog extends Animal { }
const myDog = new Dog();
console.log(myDog instanceof Dog); // true (myDog كائن من نوع Dog)
console.log(myDog instanceof Animal); // true (Dog يرث من Animal)
console.log(myDog instanceof Object); // true (كل شيء يرث من Object في النهاية)
Symbol هو نوع بيانات بدائي في JS يمكن استخدامه كمفتاح للكائن. مميزته أنه فريد (Unique) ومخفي (غير مرئي) في بعض عمليات التكرار.
for...in أو
Object.keys، استخدم Symbol
const id = Symbol("id"); // إنشاء رمز فريد
const user = {
name: "Hassan",
[id]: 12345 // استخدام الرمز كمفتاح (بين أقواس معقوفة)
};
console.log(user[id]); // 12345 (يمكن الوصول له فقط إذا كان لديك الرمز)
console.log(Object.keys(user)); // ["name"] (المفتاح الرمزي مخفي)
استخدمنا الـ Spread (...) للدمج والنسخ، ولكن
Object.assign له سلوك مختلف فيما يخص
التعديل (Mutation).
{...obj}
يُنشئ دائماً كائناً جديداً (Immutable
approach).
Object.assign(target, source)
يعدل الكائن الأول target مباشرة ويرجعه.
const target = { a: 1 };
const source = { b: 2 };
// Spread: ينشئ كائناً جديداً ولا يلم الأصلي
const newObj = { ...target, ...source };
// Object.assign: يدمج المصدر في الهدف ويعدل الهدف مباشرة
const assignedObj = Object.assign(target, source);
console.log(target); // { a: 1, b: 2 } (تغير لأنه كان الهدف!)
console.log(newObj); // { a: 1, b: 2 } (كائن جديد مستقل)
هي خصائص أو دوال تُستدعى من "الكلاس نفسه" وليس من "الكائن (Instance)".
class User {
static count = 0; // خاصية ساكنة مشتركة
constructor(name) {
this.name = name;
User.count++; // زيادة العداد عند إنشاء كائن جديد
}
static getCount() { // دالة ساكنة
return User.count;
}
}
const u1 = new User("Ali");
const u2 = new User("Sara");
// console.log(u1.count); // undefined (لا يمكن الوصول لها من الكائن)
console.log(User.getCount()); // 2 (نستدعيها من اسم الكلاس مباشرة)
لدينا كائن: const user = { id: 10 };
اكتب كود لتحويله إلى نص JSON وتخزينه في متغير اسمه text.