المادة ، التي نعرضها على انتباهك ، مكرسة لدراسة ميزات حرفية الأشياء في JavaScript ، على وجه الخصوص ، الابتكارات التي ظهرت في الإصدارات الأخيرة من معيار ECMAScript.
جافا سكريبت لديها قدرة قوية ومريحة لإنشاء كائنات باستخدام حرفية الكائن. يبسط معيار ES2015 (ES6) العمل مع الكائنات عند إنشاء تطبيقات للمتصفحات الحديثة (باستثناء IE) ومنصة Node.js.

الأساسيات
يمكن أن يتطلب إنشاء الكائنات في بعض اللغات الكثير من الموارد ، مما يعني وقت العمل للمبرمج وموارد الحوسبة للأنظمة. على وجه الخصوص ، نحن نتحدث عن حقيقة أنه قبل إنشاء الكائنات ، يجب أن تصف الفئات (على سبيل المثال ، باستخدام الكلمة الأساسية
class
). في JavaScript ، يمكن إنشاء الكائنات بسرعة شديدة وبساطة ، دون الحاجة إلى أي خطوات أولية. فكر في مثال:
// ES5 var myObject = { prop1: 'hello', prop2: 'world', output: function() { console.log(this.prop1 + ' ' + this.prop2); } }; myObject.output(); // hello world
في البرمجة ، غالبًا ما تستخدم كائنات "يمكن التخلص منها". تقوم بتخزين الإعدادات والبيانات الأخرى ، ويتم استخدامها كمعلمات للوظائف ، والقيم التي يتم إرجاعها بواسطة الوظائف ، وفي حالات أخرى. تتحول حرفية كائن جافا سكريبت في مثل هذه الحالات إلى فائدة كبيرة ، ويوسع ES6 قدراته.
تهيئة الكائنات من المتغيرات
غالبًا ما يتم إنشاء خصائص الكائنات من المتغيرات من خلال تعيين نفس الأسماء التي تم تعيينها بالفعل لهذه المتغيرات. على سبيل المثال:
في ES6 ، لم تعد بحاجة إلى تكرار أسماء المتغيرات:
قد تكون هذه التقنية مفيدة للكائنات المرتجعة عند استخدام
نموذج Revealing Module ، والذي يسمح لك بإنشاء مساحات أسماء لأجزاء التعليمات البرمجية المختلفة لتجنب تعارضات الأسماء. على سبيل المثال:
// ES6 const lib = (() => { function sum(a, b) { return a + b; } function mult(a, b) { return a * b; } return { sum, mult }; }()); console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6
ربما تكون قد رأيت كيفية استخدام هذه التقنية في وحدات ES6:
// lib.js function sum(a, b) { return a + b; } function mult(a, b) { return a * b; } export { sum, mult };
بناء الجملة المختصرة للإعلان عن طرق الكائن
عند الإعلان عن طرق الكائن في ES5 ، يجب عليك استخدام الكلمة الأساسية
function
:
// ES5 var lib = { sum: function(a, b) { return a + b; }, mult: function(a, b) { return a * b; } }; console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6
الآن ، في ES6 ، لم يعد بإمكانك القيام بذلك. يُسمح بالطريقة المختصرة التالية للإعلان عن الطرق هنا:
// ES6 const lib = { sum(a, b) { return a + b; }, mult(a, b) { return a * b; } }; console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6
وتجدر الإشارة إلى أنه لا يمكن استخدام دالات السهم ES6 (
=>
) هنا ، حيث يجب أن يكون للطرق أسماء. ومع ذلك ، يمكن استخدام وظائف السهم إذا قمت بتعيين أسماء للطرق (كما هو الحال في ES5). على سبيل المثال:
// ES6 const lib = { sum: (a, b) => a + b, mult: (a, b) => a * b }; console.log( lib.sum(2, 3) ); // 5 console.log( lib.mult(2, 3) ); // 6
مفاتيح ديناميكية
في ES5 ، لا يمكنك استخدام المتغيرات كأسماء مفاتيح ، على الرغم من أنه يمكن إضافة المفتاح الذي تم إعطاء اسمه للمتغير بعد إنشاء الكائن. على سبيل المثال:
// ES5 var key1 = 'one', obj = { two: 2, three: 3 }; obj[key1] = 1; // obj.one = 1, obj.two = 2, obj.three = 3
في ES6 ، يمكن تعيين المفاتيح ديناميكيًا بوضع تعبير اسم بين قوسين معقوفين (
[]
). على سبيل المثال:
// ES6 const key1 = 'one', obj = { [key1]: 1, two: 2, three: 3 }; // obj.one = 1, obj.two = 2, obj.three = 3
لإنشاء مفتاح ، يمكنك استخدام أي تعبير:
// ES6 const i = 1, obj = { ['i' + i]: i }; console.log(obj.i1); // 1
يمكن استخدام المفاتيح الديناميكية لكل من الأساليب والخصائص:
// ES6 const i = 2, obj = { ['mult' + i]: x => x * i }; console.log( obj.mult2(5) ); // 10
سؤال آخر هو ما إذا كان سيتم إنشاء خصائص وأساليب بأسماء تم إنشاؤها ديناميكيًا. قد تنخفض قابلية قراءة التعليمات البرمجية التي تستخدم هذه التقنية. ربما إذا كنت تواجه مواقف تبدو فيها الأسماء الديناميكية مناسبة ، فمن الأفضل التفكير في استخدام وظائف المصنع أو الفئات لإنشاء الكائنات.
إعادة الهيكلة
التدمير هو استخراج خصائص الأشياء وتخصيصها للمتغيرات. غالبًا أثناء تطوير التطبيق ، تحتاج إلى استخراج قيمة خاصية الكائن وكتابتها إلى متغير. في ES5 ، كان عليك وصف هذا على النحو التالي ، باستخدام أوامر الوصول إلى الخاصية:
// ES5 var myObject = { one: 'a', two: 'b', three: 'c' }; var one = myObject.one, // 'a' two = myObject.two, // 'b' three = myObject.three; // 'c'
ES6 يدعم التدمير. يمكنك إنشاء متغير بنفس الاسم يحمل الخاصية المقابلة للكائن والقيام بما يلي:
// ES6 const myObject = { one: 'a', two: 'b', three: 'c' }; const { one, two, three } = myObject; // one = 'a', two = 'b', three = 'c'
يمكن أن يكون للمتغيرات التي تحصل على قيم خصائص الكائن في الواقع أي أسماء ، ولكن إذا كانت تختلف عن أسماء الخصائص ، فيجب عليك استخدام بناء
{ propertyName: newVariable }
:
// ES6 const myObject = { one: 'a', two: 'b', three: 'c' }; const { one: first, two: second, three: third } = myObject; // first = 'a', second = 'b', third = 'c'
يمكن أيضًا استخدام الكائنات ذات البنية المعقدة ، التي يتم فيها تضمين الصفائف والكائنات الأخرى ، في عمليات التخصيص المدمرة:
// ES6 const meta = { title: 'Enhanced Object Literals', pageinfo: { url: 'https://www.sitepoint.com/', description: 'How to use object literals in ES2015 (ES6).', keywords: 'javascript, object, literal' } }; const { title : doc, pageinfo: { keywords: topic } } = meta; /* doc = 'Enhanced Object Literals' topic = 'javascript, object, literal' */
في البداية ، قد يبدو كل هذا معقدًا ، ومع ذلك ، ليس من الصعب اكتشافه ، الشيء الرئيسي هو تذكر ما يلي:
- الجانب الأيمن من التعبير هو مصدر البيانات - صفيف أو كائن يقوم بتخزين البيانات ليتم استردادها.
- الجانب الأيسر من التعبير هو هدف التخصيص المدمر - بنية تصف المتغيرات التي سيتم تعيينها للقيم المستخرجة من الصفيف أو الكائن.
عند استخدام التدمير ، قد تواجه بعض الصعوبات. لذلك ، لا يمكن أن يبدأ التعبير مع قوس متعرج ، لأنه سيبدو بعد ذلك مثل كتلة من التعليمات البرمجية. على سبيل المثال:
{ a, b, c } = myObject;
عادة ما يدرك النظام هذا التصميم عند تعريف المتغيرات:
const = myObject;
إذا تم الإعلان عن المتغيرات بالفعل ، يجب عليك تضمين التعبير بين قوسين:
let a, b, c; ({ a, b, c } = myObject);
ونتيجة لذلك ، عند التعامل مع التدمير ، يجب عليك توخي الحذر بشأن الشفرة وعدم مزج المتغيرات المعلنة وغير المعلنة.
التدمير هو تقنية يمكن أن تكون مفيدة في العديد من المواقف.
معلمات الوظيفة الافتراضية
إذا كانت الدالة تحتاج إلى قائمة طويلة من الوسيطات ، فمن الأسهل عادةً تمرير كائن واحد بمعلمات. على سبيل المثال:
prettyPrint( { title: 'Enhanced Object Literals', publisher: { name: 'SitePoint', url: 'https://www.sitepoint.com/' } } );
في ES5 ، كان من الضروري تحليل الكائنات بالمعلمات من أجل ، إذا لم يكن لهذه الكائنات ما تحتاجه ، لتعيين القيم الافتراضية للمعلمات المقابلة:
// ES5, function prettyPrint(param) { param = param || {}; var pubTitle = param.title || 'No title', pubName = (param.publisher && param.publisher.name) || 'No publisher'; return pubTitle + ', ' + pubName; }
في ES6 ، يمكن تعيين القيم الافتراضية لأي معلمات:
// ES6 - function prettyPrint(param = {}) { ... }
ثم يمكنك استخدام التدمير لاستخراج القيم من الكائن ، وإذا لزم الأمر ، لتعيين القيم الافتراضية:
// ES6 function prettyPrint( { title: pubTitle = 'No title', publisher: { name: pubName = 'No publisher' } } = {} ) { return `${pubTitle}, ${pubName}`; }
تجدر الإشارة إلى أن قراءة هذا الرمز قد يكون أكثر صعوبة من القراءة التقليدية ، على الرغم من أن هذه مسألة تتعلق بالتفضيلات الشخصية للمبرمج.
تحليل الكائنات التي تم إرجاعها بواسطة الدالات
يمكن أن ترجع الدالات قيمة واحدة فقط ، ولكن يمكن أن تكون هذه القيمة كائنًا يحتوي على مئات الخصائص أو الأساليب. في ES5 ، كان عليك أولاً الحصول على الكائن الذي تم إرجاعه ، وبعد ذلك يمكنك استخراج القيم منه:
تبسط إعادة الهيكلة هذه العملية. الآن يمكن القيام بكل هذا دون الحاجة إلى حفظ الكائن في متغير منفصل ثم تحليله:
ربما رأيت شيئًا مشابهًا في Node.js. على سبيل المثال ، إذا كنت تحتاج فقط إلى
readFile()
و
writeFile()
للوحدة النمطية
fs
، يمكنك الحصول على روابط لها على
writeFile()
:
// ES6 Node.js const { readFile, writeFile } = require('fs'); readFile('file.txt', (err, data) => { console.log(err || data); }); writeFile('new.txt', 'new content', err => { console.log(err || 'file written'); });
عامل تركيب المعلمة المتبقية ومعامل التمديد ES2018 (ES9)
= في ES2015 ، تم استخدام بنية المعلمات المتبقية وعامل الامتداد (كلاهما يشبه ثلاث نقاط ،
…
) فقط عند العمل مع المصفوفات. في ES2018 ، يمكن استخدام وظائف مماثلة للعمل مع الكائنات:
const myObject = ; const = myObject;
يمكن استخدام نهج مماثل لنقل قيم معينة إلى دالة:
function restParam({ a, ...x }) {
يرجى ملاحظة أنه في مثل هذه المواقف ، يمكنك استخدام تعبير واحد فقط بثلاث نقاط في نهاية القائمة. بالإضافة إلى ذلك ، بالنسبة للكائنات المتداخلة في كائنات أخرى ، لا يعمل هذا.
يمكن استخدام عامل التمديد داخل الكائنات:
const obj1 = , obj2 = ;
يمكن استخدام عامل التمديد لاستنساخ الكائنات (
obj2 = { ...obj1 };
) ، ولكن هنا يجب أن يوضع في الاعتبار أنه باستخدام هذا النهج ، يتم تنفيذ نسخة ضحلة من الكائن. إذا كانت خصائص الكائنات كائنات أخرى ، فسيشير استنساخ الكائن إلى نفس الكائنات المتداخلة.
بناء الجملة من المعلمات المتبقية وعامل التمديد غير معتمدة على نطاق واسع حتى الآن. حاليًا ، يمكن استخدامها في متصفحات Chrome و Firefox دون بذل جهود إضافية ، وعند التطوير لإصدار النظام الأساسي Node.js 8.6 والإصدارات الأحدث.
الملخص
لطالما كانت حرفية الكائن ميزة JavaScript مفيدة. لا تجلب الابتكارات التي تظهر في جافا سكريبت منذ معيار ES2015 تغييرات أساسية ، ولكنها توفر وقت المبرمج وتساعد على كتابة تعليمات برمجية أكثر دقة وإيجازًا.
أعزائي القراء! ما هي طرق إنشاء كائنات JS التي تستخدمها كثيرًا؟
