أساسيات JavaScript

التفكيك: المصفوفات والكائنات

45 دقيقة الدرس 18 من 60

ما هو التفكيك؟

التفكيك هو صيغة خاصة قدمت في ES6 (2015) تسمح لك بفك القيم من المصفوفات او الخصائص من الكائنات الى متغيرات مميزة في عبارة واحدة مختصرة. قبل التفكيك، كان استخراج قيم متعددة من مصفوفة او كائن يتطلب اسطرا متعددة من الكود مع انماط وصول متكررة. التفكيك يبسط هذه العملية بشكل كبير، مما يجعل كودك اقصر وانظف واكثر تعبيرا. انها واحدة من اكثر الميزات استخداما في JavaScript الحديثة وستواجهها في كل قاعدة كود واطار عمل ومكتبة حديثة تقريبا.

التفكيك لا يعدل المصفوفة او الكائن الاصلي. انه ببساطة ينشئ متغيرات جديدة ويعين قيما لها بناء على بنية مصدر البيانات. فكر فيه كعملية مطابقة انماط: تصف شكل البيانات التي تتوقعها، وJavaScript تستخرج القطع المطابقة لك.

تفكيك المصفوفات: الاساسيات

تفكيك المصفوفات يستخدم صيغة الاقواس المربعة على الجانب الايسر من التعيين لاستخراج القيم حسب موضعها (فهرسها) في المصفوفة. المتغير الاول يحصل على العنصر الاول، والمتغير الثاني يحصل على العنصر الثاني، وهكذا.

مثال: تفكيك المصفوفات الاساسي

// بدون تفكيك (الطريقة القديمة)
const colors = ['red', 'green', 'blue'];
const first = colors[0];    // 'red'
const second = colors[1];   // 'green'
const third = colors[2];    // 'blue'

// مع التفكيك (الطريقة الحديثة)
const [red, green, blue] = ['red', 'green', 'blue'];
console.log(red);    // 'red'
console.log(green);  // 'green'
console.log(blue);   // 'blue'

// تفكيك مصفوفة موجودة
const scores = [95, 87, 72, 64, 91];
const [highest, secondHighest, thirdHighest] = scores;
console.log(highest);        // 95
console.log(secondHighest);  // 87
console.log(thirdHighest);   // 72
ملاحظة: لا تحتاج لاستخراج كل العناصر من المصفوفة. يمكنك تفكيك اي عدد تحتاجه من القيم. اذا كنت تحتاج فقط اول عنصرين من مصفوفة من عشرة عناصر، فقط قدم اسمين للمتغيرات. العناصر المتبقية يتم تجاهلها ببساطة.

تخطي العناصر في تفكيك المصفوفات

يمكنك تخطي عناصر في مصفوفة باستخدام فواصل بدون اسماء متغيرات. كل فاصلة تمثل موضعا متخطى واحدا. هذا مفيد عندما تحتاج فقط عناصر محددة من مصفوفة وتريد تجاهل البقية.

مثال: تخطي العناصر

const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];

// تخطي اول شهرين والحصول على مارس
const [, , march] = months;
console.log(march);  // 'Mar'

// الحصول على العنصر الاول والثالث فقط
const [first, , third] = months;
console.log(first);  // 'Jan'
console.log(third);  // 'Mar'

// الحصول على الاول والاخير من مصفوفة معروفة الحجم
const [january, , , , , june] = months;
console.log(january);  // 'Jan'
console.log(june);     // 'Jun'

نمط الباقي مع المصفوفات

نمط الباقي (...) يجمع كل العناصر المتبقية في مصفوفة جديدة. يجب ان يكون العنصر الاخير في نمط التفكيك. هذا مفيد للغاية عندما تريد فصل العناصر الاولى عن البقية.

مثال: نمط الباقي في تفكيك المصفوفات

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// الحصول على الاولين وجمع الباقي
const [first, second, ...remaining] = numbers;
console.log(first);      // 1
console.log(second);     // 2
console.log(remaining);  // [3, 4, 5, 6, 7, 8, 9, 10]

// تخطي واحد والحصول على واحد وجمع الباقي
const [, secondItem, ...others] = numbers;
console.log(secondItem);  // 2
console.log(others);      // [3, 4, 5, 6, 7, 8, 9, 10]

// مثال عملي: فصل الراس عن الذيل
const queue = ['urgent-task', 'task-1', 'task-2', 'task-3'];
const [currentTask, ...pendingTasks] = queue;
console.log(currentTask);    // 'urgent-task'
console.log(pendingTasks);   // ['task-1', 'task-2', 'task-3']
خطا شائع: عنصر الباقي يجب ان يكون دائما العنصر الاخير في نمط التفكيك. كتابة const [...rest, last] = array; ستطلق SyntaxError. اذا كنت تحتاج العنصر الاخير، فكر في استخدام array.at(-1) او array[array.length - 1] بشكل منفصل.

القيم الافتراضية في تفكيك المصفوفات

اذا لم تحتوي المصفوفة على عناصر كافية لنمط التفكيك، ستكون المتغيرات الاضافية undefined. يمكنك تعيين قيم افتراضية تستخدم عندما يكون عنصر المصفوفة المقابل undefined او مفقودا.

مثال: القيم الافتراضية

// بدون افتراضيات: القيم المفقودة تكون undefined
const [a, b, c] = [1, 2];
console.log(a);  // 1
console.log(b);  // 2
console.log(c);  // undefined

// مع افتراضيات: قيم احتياطية للعناصر المفقودة
const [x = 10, y = 20, z = 30] = [5, 15];
console.log(x);  // 5  (مقدمة من المصفوفة)
console.log(y);  // 15 (مقدمة من المصفوفة)
console.log(z);  // 30 (الافتراضية مستخدمة لان العنصر مفقود)

// مثال عملي: اعدادات مع افتراضيات
const userSettings = ['dark'];
const [theme = 'light', fontSize = 16, language = 'en'] = userSettings;
console.log(theme);     // 'dark' (من المصفوفة)
console.log(fontSize);  // 16 (افتراضي)
console.log(language);  // 'en' (افتراضي)

تبديل المتغيرات بالتفكيك

واحد من اجمل استخدامات تفكيك المصفوفات هو تبديل متغيرين بدون الحاجة لمتغير مؤقت. قبل ES6، كان التبديل يتطلب متغيرا ثالثا للاحتفاظ مؤقتا باحدى القيم. التفكيك يجعل هذا سطرا واحدا مقروءا.

مثال: تبديل المتغيرات

// الطريقة القديمة: استخدام متغير مؤقت
let a = 'hello';
let b = 'world';
let temp = a;
a = b;
b = temp;
console.log(a, b);  // 'world' 'hello'

// الطريقة الحديثة: تبديل بالتفكيك
let x = 'hello';
let y = 'world';
[x, y] = [y, x];
console.log(x, y);  // 'world' 'hello'

// يعمل مع اي انواع بيانات
let first = 1;
let second = 2;
let third = 3;
[first, second, third] = [third, first, second];
console.log(first, second, third);  // 3 1 2
نصيحة احترافية: نمط تبديل التفكيك شائع جدا في خوارزميات الترتيب وتطوير الالعاب واي سيناريو تحتاج فيه لاعادة ترتيب القيم. يعمل لان الجانب الايمن ينشئ مصفوفة مؤقتة جديدة قبل ان يفككها الجانب الايسر.

تفكيك المصفوفات المتداخلة

عندما تحتوي المصفوفات على مصفوفات اخرى (مصفوفات متداخلة او متعددة الابعاد)، يمكنك استخدام انماط تفكيك متداخلة لاستخراج القيم في اي عمق. ببساطة استخدم مجموعة اخرى من الاقواس المربعة في الموضع الذي توجد فيه المصفوفة المتداخلة.

مثال: تفكيك المصفوفات المتداخلة

// مصفوفة ثنائية الابعاد (مصفوفة رياضية)
const matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// تفكيك المصفوفات المتداخلة
const [[a, b, c], [d, e, f], [g, h, i]] = matrix;
console.log(a, e, i);  // 1 5 9 (القطر)

// تفكيك جزئي للمصفوفات المتداخلة
const [firstRow, , thirdRow] = matrix;
console.log(firstRow);   // [1, 2, 3]
console.log(thirdRow);   // [7, 8, 9]

// استخراج قيم متداخلة محددة
const [[topLeft], , [, , bottomRight]] = matrix;
console.log(topLeft);       // 1
console.log(bottomRight);   // 9

// مثال عملي: ازواج الاحداثيات
const coordinates = [[10, 20], [30, 40], [50, 60]];
const [[x1, y1], [x2, y2]] = coordinates;
console.log(x1, y1);  // 10 20
console.log(x2, y2);  // 30 40

تفكيك الكائنات: الاساسيات

تفكيك الكائنات يستخدم صيغة الاقواس المعقوصة لاستخراج الخصائص باسمائها. على عكس تفكيك المصفوفات حيث الموضع مهم، تفكيك الكائنات يطابق باسم الخاصية. اسماء المتغيرات يجب ان تطابق اسماء الخصائص في الكائن (الا اذا اعدت تسميتها، وهو ما سنغطيه لاحقا).

مثال: تفكيك الكائنات الاساسي

// بدون تفكيك (الطريقة القديمة)
const person = {
    name: 'Alice',
    age: 28,
    city: 'New York',
    occupation: 'Engineer'
};
const name = person.name;
const age = person.age;
const city = person.city;

// مع التفكيك (الطريقة الحديثة)
const { name, age, city, occupation } = person;
console.log(name);        // 'Alice'
console.log(age);         // 28
console.log(city);        // 'New York'
console.log(occupation);  // 'Engineer'

// الترتيب لا يهم في تفكيك الكائنات
const { occupation, name, city, age } = person;  // نفس النتيجة
ملاحظة: فرق رئيسي بين تفكيك المصفوفات والكائنات هو ان تفكيك المصفوفات يعتمد على الموضع (الترتيب مهم) بينما تفكيك الكائنات يعتمد على الاسم (الترتيب لا يهم). يمكنك ادراج الخصائص باي ترتيب عند تفكيك كائن.

اعادة تسمية المتغيرات في تفكيك الكائنات

احيانا اسماء الخصائص في كائن لا تطابق اسماء المتغيرات التي تريد استخدامها، او تتعارض مع متغيرات موجودة. يمكنك اعادة تسمية الخصائص اثناء التفكيك باستخدام صيغة النقطتين: propertyName: newVariableName.

مثال: اعادة التسمية اثناء التفكيك

const apiResponse = {
    user_name: 'john_doe',
    user_email: 'john@example.com',
    created_at: '2024-01-15',
    is_active: true
};

// اعادة تسمية snake_case الى camelCase
const {
    user_name: userName,
    user_email: userEmail,
    created_at: createdAt,
    is_active: isActive
} = apiResponse;

console.log(userName);   // 'john_doe'
console.log(userEmail);  // 'john@example.com'
console.log(createdAt);  // '2024-01-15'
console.log(isActive);   // true

// تجنب تعارض الاسماء
const name = 'Global Name';
const user = { name: 'User Name', age: 25 };
const { name: userName2, age: userAge } = user;
console.log(name);       // 'Global Name' (لم يتغير)
console.log(userName2);  // 'User Name'

القيم الافتراضية في تفكيك الكائنات

تماما مثل تفكيك المصفوفات، يمكنك توفير قيم افتراضية لخصائص الكائن التي قد تكون undefined او مفقودة. الافتراضيات مفيدة بشكل خاص عند العمل مع كائنات اعدادات اختيارية او استجابات API التي قد تحتوي على بيانات غير مكتملة.

مثال: القيم الافتراضية في تفكيك الكائنات

const config = {
    host: 'localhost',
    port: 3000
};

// database و timeout تستخدم الافتراضيات لانها غير موجودة في config
const {
    host,
    port,
    database = 'mydb',
    timeout = 5000,
    retries = 3
} = config;

console.log(host);      // 'localhost' (من الكائن)
console.log(port);      // 3000 (من الكائن)
console.log(database);  // 'mydb' (افتراضي)
console.log(timeout);   // 5000 (افتراضي)
console.log(retries);   // 3 (افتراضي)

// دمج اعادة التسمية والافتراضي
const settings = { bg: '#fff' };
const { bg: backgroundColor = '#000', fg: foregroundColor = '#333' } = settings;
console.log(backgroundColor);  // '#fff' (من الكائن)
console.log(foregroundColor);  // '#333' (افتراضي لان fg مفقودة)
نصيحة احترافية: يمكنك دمج اعادة التسمية والقيم الافتراضية في نفس التفكيك: const { oldName: newName = defaultValue } = obj;. تقرا كالتالي: استخرج خاصية oldName، سمها newName، واذا لم تكن موجودة، استخدم defaultValue. هذا النمط شائع للغاية في التعامل مع الاعدادات.

نمط الباقي مع الكائنات

تماما كما مع المصفوفات، يمكنك استخدام نمط الباقي مع الكائنات لجمع كل الخصائص المتبقية في كائن جديد. هذه تقنية قوية لفصل الخصائص المعروفة عن البقية.

مثال: نمط الباقي في تفكيك الكائنات

const user = {
    id: 1,
    name: 'Alice',
    email: 'alice@example.com',
    age: 28,
    role: 'admin',
    department: 'Engineering'
};

// استخراج id و name وجمع الباقي
const { id, name, ...details } = user;
console.log(id);       // 1
console.log(name);     // 'Alice'
console.log(details);  // { email: 'alice@example.com', age: 28, role: 'admin', department: 'Engineering' }

// عملي: ازالة خاصية من كائن (بشكل غير قابل للتغيير)
const { email, ...userWithoutEmail } = user;
console.log(userWithoutEmail);
// { id: 1, name: 'Alice', age: 28, role: 'admin', department: 'Engineering' }

// عملي: فصل الخصائص في نمط يشبه المكونات
const props = { className: 'card', onClick: () => {}, children: 'Hello', id: 'card-1', style: {} };
const { className, onClick, children, ...htmlAttributes } = props;
console.log(htmlAttributes);  // { id: 'card-1', style: {} }

تفكيك الكائنات المتداخلة

الكائنات غالبا تحتوي على كائنات اخرى كخصائص، مشكلة بنية متداخلة. يمكنك تفكيك الكائنات المتداخلة بمحاكاة نمط التداخل في صيغة التفكيك. هذا شائع للغاية عند العمل مع استجابات API المعقدة او كائنات الاعدادات.

مثال: تفكيك الكائنات المتداخلة

const company = {
    name: 'TechCorp',
    address: {
        street: '123 Main St',
        city: 'San Francisco',
        state: 'CA',
        zip: '94102'
    },
    ceo: {
        name: 'Jane Doe',
        contact: {
            email: 'jane@techcorp.com',
            phone: '555-0123'
        }
    }
};

// تفكيك الخصائص المتداخلة
const {
    name: companyName,
    address: { city, state, zip },
    ceo: { name: ceoName, contact: { email: ceoEmail } }
} = company;

console.log(companyName);  // 'TechCorp'
console.log(city);         // 'San Francisco'
console.log(state);        // 'CA'
console.log(ceoName);      // 'Jane Doe'
console.log(ceoEmail);     // 'jane@techcorp.com'
خطا شائع: عند تفكيك الكائنات المتداخلة، اذا كانت الخاصية الام غير موجودة (undefined او null)، ستحصل على TypeError. تاكد دائما من وجود الكائن الام، او وفر كائنا فارغا افتراضيا: const { address: { city } = {} } = obj;. هذا يمنع الانهيار عند التعامل مع بيانات غير مكتملة.

تفكيك معاملات الدوال

واحد من اقوى واكثر الاستخدامات العملية للتفكيك هو في معاملات الدوال. بدلا من تمرير كائن كبير والوصول الى خصائصه داخل الدالة بنقطة التدوين، يمكنك تفكيك المعامل مباشرة في توقيع الدالة. هذا يجعل واجهة الدالة موثقة ذاتيا ويقلل الكود النمطي داخل جسم الدالة.

مثال: تفكيك معاملات الدوال

// بدون تفكيك: الكثير من وصول obj.property
function createUserOld(options) {
    const name = options.name;
    const email = options.email;
    const role = options.role || 'user';
    const active = options.active !== undefined ? options.active : true;
    console.log(name + ' (' + email + ') - ' + role);
}

// مع التفكيك: نظيف وموثق ذاتيا
function createUser({ name, email, role = 'user', active = true }) {
    console.log(name + ' (' + email + ') - ' + role);
    console.log('Active: ' + active);
}

createUser({
    name: 'Alice',
    email: 'alice@example.com',
    role: 'admin'
});
// Alice (alice@example.com) - admin
// Active: true

// تعمل بشكل رائع مع دوال السهم ايضا
const formatAddress = ({ street, city, state, zip }) => {
    return street + ', ' + city + ', ' + state + ' ' + zip;
};

console.log(formatAddress({
    street: '456 Oak Ave',
    city: 'Portland',
    state: 'OR',
    zip: '97201'
}));
// 456 Oak Ave, Portland, OR 97201

مثال: معاملات اختيارية مع كائن فارغ افتراضي

// جعل المعامل بالكامل اختياريا
function initApp({ debug = false, verbose = false, port = 3000 } = {}) {
    console.log('Debug: ' + debug);
    console.log('Verbose: ' + verbose);
    console.log('Port: ' + port);
}

// استدعاء مع خيارات
initApp({ debug: true, port: 8080 });
// Debug: true
// Verbose: false
// Port: 8080

// استدعاء بدون وسائط على الاطلاق (تستخدم كل الافتراضيات)
initApp();
// Debug: false
// Verbose: false
// Port: 3000
ملاحظة: الافتراضي = {} في نهاية المعامل ({ debug, verbose } = {}) حاسم. بدونه، استدعاء الدالة بدون وسائط سيطلق TypeError لان JavaScript ستحاول تفكيك undefined. الكائن الفارغ الافتراضي يضمن ان الدالة تعمل حتى عند استدعائها بدون وسائط.

التفكيك في الحلقات

التفكيك يعمل بسلاسة مع حلقات for...of، مما يسهل العمل مع مصفوفات الكائنات او مدخلات من Map. هذا النمط يلغي الحاجة للمتغيرات المؤقتة ويجعل اجسام الحلقات انظف بكثير.

مثال: التفكيك في حلقات for...of

const users = [
    { name: 'Alice', age: 28, role: 'admin' },
    { name: 'Bob', age: 34, role: 'editor' },
    { name: 'Charlie', age: 22, role: 'user' }
];

// تفكيك كل كائن في الحلقة
for (const { name, age, role } of users) {
    console.log(name + ' is ' + age + ' years old and is an ' + role);
}
// Alice is 28 years old and is an admin
// Bob is 34 years old and is an editor
// Charlie is 22 years old and is an user

// التفكيك مع Object.entries()
const scores = { math: 95, science: 88, english: 92, history: 78 };

for (const [subject, score] of Object.entries(scores)) {
    console.log(subject + ': ' + score);
}
// math: 95
// science: 88
// english: 92
// history: 78

// التفكيك مع مدخلات Map
const userMap = new Map([
    ['u001', { name: 'Alice', active: true }],
    ['u002', { name: 'Bob', active: false }]
]);

for (const [id, { name, active }] of userMap) {
    console.log(id + ': ' + name + ' (active: ' + active + ')');
}
// u001: Alice (active: true)
// u002: Bob (active: false)

التفكيك المختلط: مصفوفات الكائنات

البيانات الواقعية غالبا تجمع بين المصفوفات والكائنات في تكوينات مختلفة. يمكنك مزج تفكيك المصفوفات والكائنات بحرية لاستخراج القيم التي تحتاجها بالضبط من هياكل متداخلة معقدة. هذا واحد من الانماط التي ستستخدمها اكثر في الممارسة العملية.

مثال: انماط التفكيك المختلطة

// مصفوفة كائنات: الحصول على عناصر محددة وخصائصها
const products = [
    { id: 1, name: 'Laptop', price: 999, category: 'Electronics' },
    { id: 2, name: 'Book', price: 29, category: 'Education' },
    { id: 3, name: 'Headphones', price: 149, category: 'Electronics' }
];

// تفكيك اسم وسعر المنتج الاول
const [{ name: firstName, price: firstPrice }] = products;
console.log(firstName);   // 'Laptop'
console.log(firstPrice);  // 999

// تخطي الاول وتفكيك الثاني
const [, { name: secondName, category }] = products;
console.log(secondName);  // 'Book'
console.log(category);    // 'Education'

// الحصول على العنصر الاول وجمع الباقي
const [featured, ...otherProducts] = products;
console.log(featured.name);          // 'Laptop'
console.log(otherProducts.length);   // 2

// كائن يحتوي على مصفوفات
const dashboard = {
    title: 'Analytics',
    metrics: [120, 340, 560, 230],
    users: ['Alice', 'Bob', 'Charlie']
};

const {
    title,
    metrics: [first, second, ...restMetrics],
    users: [primaryUser, ...otherUsers]
} = dashboard;

console.log(title);        // 'Analytics'
console.log(first);        // 120
console.log(second);       // 340
console.log(restMetrics);  // [560, 230]
console.log(primaryUser);  // 'Alice'
console.log(otherUsers);   // ['Bob', 'Charlie']

مثال واقعي: تفكيك استجابة API

واحد من اكثر التطبيقات الواقعية شيوعا للتفكيك هو معالجة استجابات API. واجهات API غالبا تعيد بيانات JSON متداخلة بعمق، والتفكيك يتيح لك استخراج البيانات التي تحتاجها بالضبط بطريقة نظيفة وقابلة للقراءة. دعنا نلقي نظرة على عدة امثلة عملية تعكس ما ستواجهه في التطبيقات الانتاجية.

مثال: تفكيك استجابة API مستخدم REST

// استجابة API محاكاة
const apiResponse = {
    status: 200,
    data: {
        user: {
            id: 42,
            profile: {
                firstName: 'Sarah',
                lastName: 'Ahmed',
                avatar: 'https://example.com/avatar.jpg'
            },
            settings: {
                theme: 'dark',
                notifications: {
                    email: true,
                    push: false,
                    sms: true
                }
            },
            posts: [
                { id: 101, title: 'Hello World', likes: 42 },
                { id: 102, title: 'JavaScript Tips', likes: 128 },
                { id: 103, title: 'CSS Tricks', likes: 95 }
            ]
        }
    },
    meta: {
        requestId: 'abc-123',
        timestamp: '2024-01-15T10:30:00Z'
    }
};

// تفكيك عميق للحصول على ما نحتاجه بالضبط
const {
    status,
    data: {
        user: {
            id: userId,
            profile: { firstName, lastName, avatar },
            settings: {
                theme,
                notifications: { email: emailNotifications, push: pushNotifications }
            },
            posts: [latestPost, ...olderPosts]
        }
    },
    meta: { requestId }
} = apiResponse;

console.log(status);              // 200
console.log(userId);              // 42
console.log(firstName);           // 'Sarah'
console.log(lastName);            // 'Ahmed'
console.log(theme);               // 'dark'
console.log(emailNotifications);  // true
console.log(pushNotifications);   // false
console.log(latestPost.title);    // 'Hello World'
console.log(olderPosts.length);   // 2
console.log(requestId);           // 'abc-123'

مثال: تفكيك استجابة Fetch مع معالجة الاخطاء

// نمط شائع للتعامل مع استجابات API
async function fetchUserData(userId) {
    try {
        const response = await fetch('/api/users/' + userId);
        const json = await response.json();

        // تفكيك مع افتراضيات للسلامة
        const {
            data: {
                name = 'Unknown',
                email = 'N/A',
                role = 'user',
                permissions = []
            } = {},
            error = null,
            message = ''
        } = json;

        if (error) {
            console.log('Error: ' + message);
            return null;
        }

        return { name, email, role, permissions };
    } catch (err) {
        console.log('Request failed: ' + err.message);
        return null;
    }
}

// استخدام النتيجة المفككة
async function displayUser() {
    const userData = await fetchUserData(42);
    if (userData) {
        const { name, email, role } = userData;
        console.log(name + ' (' + email + ') - ' + role);
    }
}

مثال: تفكيك استجابة API مرقمة الصفحات

const paginatedResponse = {
    results: [
        { id: 1, title: 'Post One' },
        { id: 2, title: 'Post Two' },
        { id: 3, title: 'Post Three' }
    ],
    pagination: {
        currentPage: 2,
        totalPages: 10,
        perPage: 3,
        totalItems: 30
    },
    links: {
        self: '/api/posts?page=2',
        next: '/api/posts?page=3',
        prev: '/api/posts?page=1',
        first: '/api/posts?page=1',
        last: '/api/posts?page=10'
    }
};

const {
    results: posts,
    pagination: { currentPage, totalPages, totalItems },
    links: { next: nextPageUrl, prev: prevPageUrl }
} = paginatedResponse;

console.log('Page ' + currentPage + ' of ' + totalPages);
console.log('Showing ' + posts.length + ' of ' + totalItems + ' items');
console.log('Next: ' + nextPageUrl);
console.log('Prev: ' + prevPageUrl);

// التكرار عبر النتائج مع التفكيك
posts.forEach(({ id, title }) => {
    console.log('#' + id + ': ' + title);
});

التفكيك مع اسماء الخصائص المحسوبة

يمكنك استخدام اسماء خصائص محسوبة (ديناميكية) في تفكيك الكائنات بتغليف اسم الخاصية في اقواس مربعة. هذا مفيد عندما يكون اسم الخاصية مخزنا في متغير او يتحدد في وقت التشغيل.

مثال: اسماء الخصائص المحسوبة في التفكيك

const data = {
    name: 'Alice',
    age: 28,
    email: 'alice@example.com'
};

// اسم خاصية ديناميكي
const key = 'email';
const { [key]: extractedValue } = data;
console.log(extractedValue);  // 'alice@example.com'

// عملي: استخراج حقل محدد ديناميكيا
function getField(obj, fieldName) {
    const { [fieldName]: value } = obj;
    return value;
}

console.log(getField(data, 'name'));   // 'Alice'
console.log(getField(data, 'age'));    // 28
console.log(getField(data, 'email')); // 'alice@example.com'

تفكيك القيم المعادة

الدوال التي تعيد مصفوفات او كائنات هي مرشحة طبيعية للتفكيك. العديد من ميثودات JavaScript المدمجة ودوال المكتبات الحديثة تعيد بيانات منظمة يمكن تفكيكها فورا في موقع الاستدعاء.

مثال: تفكيك القيم المعادة من الدوال

// دالة تعيد مصفوفة (مثل useState في React)
function useState(initialValue) {
    let value = initialValue;
    const setValue = (newValue) => { value = newValue; };
    return [value, setValue];
}

const [count, setCount] = useState(0);
console.log(count);  // 0

// دالة تعيد كائنا
function getMinMax(numbers) {
    return {
        min: Math.min(...numbers),
        max: Math.max(...numbers),
        range: Math.max(...numbers) - Math.min(...numbers),
        count: numbers.length
    };
}

const { min, max, range } = getMinMax([5, 2, 8, 1, 9, 3]);
console.log(min);    // 1
console.log(max);    // 9
console.log(range);  // 8

// تفكيك نتائج مطابقة التعبيرات المنتظمة
const dateString = '2024-03-15';
const [fullMatch, year, month, day] = dateString.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(year);   // '2024'
console.log(month);  // '03'
console.log(day);    // '15'

ملخص انماط التفكيك الشائعة

اليك مرجع سريع لاهم انماط التفكيك التي ستستخدمها يوميا:

  • اساسيات المصفوفات -- const [a, b, c] = array; تستخرج بالموضع.
  • تخطي العناصر -- const [a, , c] = array; تستخدم الفواصل للتخطي.
  • الباقي (مصفوفة) -- const [first, ...rest] = array; تجمع العناصر المتبقية.
  • القيم الافتراضية -- const [a = 1, b = 2] = array; احتياطي لـ undefined.
  • تبديل المتغيرات -- [a, b] = [b, a]; بدون حاجة لمتغير مؤقت.
  • اساسيات الكائنات -- const { name, age } = obj; تستخرج باسم الخاصية.
  • اعادة التسمية -- const { name: userName } = obj; استخراج واعادة تسمية.
  • الباقي (كائن) -- const { id, ...rest } = obj; تجمع الخصائص المتبقية.
  • متداخل -- const { a: { b } } = obj; تستخرج من هياكل متداخلة بعمق.
  • المعاملات -- function fn({ x, y } = {}) {} تفكيك في توقيعات الدوال.
  • الحلقات -- for (const { name } of array) {} تفكيك في التكرارات.

تمرين عملي

انشئ دالة تسمى processApiResponse تقبل كائن استجابة API محاكاة التالي وتستخدم التفكيك لاستخراج ومعالجة البيانات. الاستجابة تحتوي على status (رقم)، كائن data يحتوي على مصفوفة users (كل مستخدم لديه id و name و email و address مع city و country و orders وهي مصفوفة كائنات مع orderId و total و items)، وكائن pagination مع page و totalPages و hasMore. داخل الدالة: استخدم التفكيك لاستخراج status ومصفوفة users مع افتراضي مصفوفة فارغة؛ استخدم التفكيك في حلقة for...of للتكرار عبر المستخدمين واستخراج name و address: { city } والطلب الاول باستخدام تفكيك المصفوفات [firstOrder, ...otherOrders]؛ لكل مستخدم، اطبع اسمه ومدينته واجمالي الطلب الاول؛ استخدم نمط الباقي لفصل التقسيم من بقية الاستجابة؛ اعد كائنا بالبيانات المعالجة. اختبر دالتك ببيانات عينة تتضمن 3 مستخدمين على الاقل، كل واحد مع طلبين على الاقل، وتحقق من ان جميع انماط التفكيك تعمل بشكل صحيح مع البيانات الكاملة وغير المكتملة باختبار مستخدم بدون طلبات (مصفوفة فارغة) وحقول عنوان مفقودة.