الرموز Symbol في JavaScript

من موسوعة حسوب

الدالة Symbol()‎ تُعيد قيمةً من النوع symbol، وتملك هذه الدالة خاصيات ساكنة (static properties) ودوال ساكنة (التي تُستخدَم للوصول إلى سجل الرموز العام، أي global symbol registry، ويسمى أيضًا بالمصطلح symbol table)؛ وهذه الدالة تحاول التشبه بالدوال البانية للكائنات، لكنها ليست دالةً بانيةً لعدم القدرة على استخدام المعامل new معها كما في new Symbol()‎.

كل رمز (symbol) مُعاد من الدالة Symbol()‎ هو رمزٌ فريد؛ ويمكن أن يُستخدَم كمُعرِّف (identifier) لخاصيات الكائنات، وهذا هو الغرض الرئيسي من هذا النوع من البيانات. لاحظ أنَّ نوع البيانات symbol هو نوع بيانات أوليّ (primitive data type).

البنية العامة

Symbol([description])

description

وسيط اختياري، وهو وصفٌ نصيٌّ للرمز الذي يمكن أن يُستخدَم لأغراض تنقيح الأخطاء (debugging) لكن ليس للوصول إلى الرمز نفسه.

الوصف

لإنشاء رمز أوليّ (primitive symbol)، فيمكنك استخدام الدالة Symbol()‎ مع -أو دون- تمرير سلسلة نصية اختيارية إليها:

var sym1 = Symbol();
var sym2 = Symbol('foo');
var sym3 = Symbol('foo');

الشيفرة السابقة تُنشِئ ثلاثة رموز جديدة، لاحظ أنَّ الدالة Symbol("foo")‎ لا تحوِّل السلسلة النصية "foo" إلى رمز، وإنما ستُنشِئ رمزًا جديدًا في كل مرة تستدعى فيها.

Symbol('foo') === Symbol('foo'); // false

لاحظ أنَّ استخدام الدالة Symbol مع المعامل new سيؤدي إلى رمي TypeError:

var sym = new Symbol(); // TypeError

ما سبق يمنع المبرمجين من إنشاء كائن Symbol لتغليف القيم الأوليّة بدلًا من إنشاء رمز جديد، وقد يُفاجئ هذا السلوك البعض لأنَّ إنشاء كائنات تُغلِّف قيمًا أوليّةً هو أمرٌ مسموحٌ في JavaScript (ومثال ذلك استخدام new Boolean، و new String و new Number). لكن إذا أردتَ إنشاء كائن يُغلِّف قيمةً أوليةً من النوع Symbol، فيمكنك استخدام الدالة البانية Object()‎:

var sym = Symbol('foo');
typeof sym;     // "symbol" 
var symObj = Object(sym);
typeof symObj;  // "object"

الرموز المشتركة في سجل الرموز العام

استخدام الدالة Symbol()‎ كما سبق لن يؤدي إلى إنشاء رمز عام الذي يكون متاحًا في كامل تطبيقك. فلإنشاء رموز تتوافر في أكثر من ملف فاستخدم الدالة Symbol.for()‎ و Symbol.keyFor()‎ لضبط والحصول على الرموز من سجل الرموز العام (global symbol registry).

العثور على الخاصيات الرمزية في أحد الكائنات

الدالة Object.getOwnPropertySymbols()‎ تُعيد مصفوفةً تحتوي على جميع الرموز المستخدم في الكائن، لاحظ أنَّ جميع الكائنات تُهيِّئ دون وجود خاصيات رمزية فيها، لذا ستكون هذه المصفوفة فارغةً ما لم تضبط خاصيات رمزية على الكائن.

الخاصيات

Symbol.length

قيمة الخاصية length، وهي 0.

Symbol.prototype

تمثِّل هذه الخاصية الكائن prototype للدالة Symbol.

الرموز المعروفة

إضافة إلى الرموز التي نُعرِّفها باستخدام الدالة Symbol، تملك JavaScript رموزًا مضمَّنة فيها التي تُمثِّل سلوك اللغة الداخلي، والتي لم تكن متاحةً للمطورين في إصدار ECMAScript 5 وما قبله؛ ويمكن الوصول إلى الرموز المعروفة (well-known symbols) عبر الخاصيات تالية الذكر.

رموز الدوران

Symbol.iterator

دالة تُعيد iterator الافتراضي لأحد الكائنات، الذي يُستخدَم من حلقة for...of.

Symbol.asyncIterator

هذه الدالة تُعيد AsyncIterator الافتراضي لأحد الكائنات، الذي يُستخدَم من حلقة for await of. لاحظ أنَّ هذه الخاصية تجريبية حتى الآن.

رموز التعابير النمطية

Symbol.match

دالة تُطابَق مع سلسلة نصية، وتُستخدَم أيضًا لتحديد إذا كان بالإمكان استخدام الكائن كتعبير نمطي. وتُستخدَم من الدالة String.prototype.match()‎.

Symbol.replace

هذا الرمز المعروف يُحدِّد الدالة التي ستستبدل جزءًا من سلسلة نصية؛ وهذه الدالة ستُستدعى من الدالة String.prototype.replace()‎.

لمزيدٍ من المعلومات، راجع صفحة RegExp.prototype[@@replace]()‎ و String.prototype.replace()‎.

Symbol.search

هذا الرمز المعروف يُحدِّد الدالة التي ستعيد فهرسًا (index) ضمن سلسلة نصية الذي يُطابِق التعبير النمطي؛ وهذه الدالة ستُستدعى من الدالة String.prototype.search()‎.

لمزيدٍ من المعلومات، راجع صفحة RegExp.prototype[@@search]()‎ و String.prototype.search()‎.

Symbol.split

هذا الرمز المعروف يُحدِّد الدالة التي ستقسم سلسلة نصية في الفهارس (indexes) التي تُطابِق تعبيرًا نمطيًا؛ وهذه الدالة ستُستدعى من الدالة String.prototype.split()‎.

لمزيدٍ من المعلومات، راجع صفحة RegExp.prototype[@@split]()‎ و String.prototype.split()‎.

الرموز الأخرى

Symbol.hasInstance

دالة تُحدِّد ما إذا كانت الدالة البانية تتعرّف على أحد الكائنات على أنَّه كائنٌ مُنشَأٌ منها. وتُستخدَم من المعامل instanceof؛ ويمكن تخصيص سلوك المعامل instanceof باستخدام هذا الرمز.

يمكنك تخصيص سلوك المعامل instanceof كما في المثال الآتي:

class MyArray {  
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}
console.log([] instanceof MyArray); // true

Symbol.isConcatSpreadable

قيمة منطقية تُحدِّد ما إذا كان يجب «تسطيح» (flatten) الكائن إلى عناصر مصفوفة. ويستخدم هذا الرمز من الدالة Array.prototype.concat()‎.

Symbol.species

دالة تُستخدَم لإنشاء كائنات مشتقة.

Symbol.toPrimitive

دالة تُستخدَم لتحويل الكائن إلى قيمة أوليّة.

Symbol.toStringTag

سلسلة نصية تُستخدَم في الوصف الافتراضي للكائن، ويستخدم هذا الرمز من الدالة Object.prototype.toString()‎.

الدوال

Symbol.for(key)‎

البحث عن رموز لها المفتاح المعيّن وإعادتها إن وجدت؛ وإلا فسينُشَأ رمزٌ جديد في سجل الرموز العام له هذا المفتاح.

Symbol.keyFor(sym)‎

الحصول على مفتاح الرمز المعطي من سجل الرموز العام.

الكائن prototype

جميع الرموز ترث من الكائن Symbol.prototype.

الخاصيات

Symbol.prototype.constructor

تُحديد الدالة التي ستُنشِئ كائن prototype للرمز، وهي الدالة Symbol افتراضيًا.

الدوال

Symbol.prototype.toString()‎

إعادة سلسلة نصية تحتوي على وصف الرمز، وهي تُعيد تعريف الدالة Object.prototype.toString()‎.

Symbol.prototype.valueOf()‎

إعادة القيمة الأولية لأحد كائنات Symbol، وهي تُعيد تعريف الدالة Object.prototype.valueOf()‎.

Symbol.prototype[@@toPrimitive]‎

تحويل الكائن Symbol إلى قيمة أوليّة.

أمثلة

استخدام المعامل typeof مع الرموز

يمكن استخدام المعامل typeof للتعرف على الرموز:

typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'

تحويل الرموز

هذه بعض الأمور التي يجب أخذها بالحسبان عند محاولة تحويل قيم الرموز إلى أنواع بيانات أخرى:

  • عند محاولة تحويل رمز إلى رقم (مثلًا:‎+sym أو sym | 0) فسيرمى الخطأ TypeError.
  • عند استخدام معامل المساواة (دون مطابقة) مثل Object(sym) == sym فستُعاد القيمة true.
  • التعبير البرمجي Symbol("foo") + "bar"‎ سيرمي الخطأ TypeError، وهذا يمنع إنشاء خاصيات لها مفاتيح نصية مشتقة من رموز.
  • استخدام الدالة String(sym)‎ تماثل استخدام الدالة Symbol.prototype.toString()‎ عند التعامل مع الرموز، لكن لاحظ أنَّ الدالة new String(sym)‎ سترمي الخطأ TypeError.

الرموز وحلقة التكرار for...in

لا يمكن المرور على الرموز باستخدام حلقة التكرار for...in، أضف إلى ذلك أنَّ الدالة Object.getOwnPropertyNames()‎ لا تُعيد الخاصيات الرمزية، إذ يجب استخدام الدالة Object.getOwnPropertySymbols()‎ لإعادة الخاصيات الرمزية:

var obj = {};

obj[Symbol('a')] = 'a';
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c';
obj.d = 'd';

for (var i in obj) {
   console.log(i); // "c" "d"
}

الرموز والدالة JSON.stringify()‎

سيتم تجاهل الخاصيات الرمزية تمامًا عند استخدام الدالة JSON.stringify()‎:

JSON.stringify({[Symbol('foo')]: 'foo'});                 
// '{}'

تغليف الرموز

عند استخدام كائن مُغلِّف لرمز كمفتاح لخاصيةٍ ما، فسيحوّل الكائن إلى الرمز الذي يُغلِّفُه:

var sym = Symbol('foo');
var obj = {[sym]: 1};
obj[sym];            // 1
obj[Object(sym)];    // 1

دعم المتصفحات

الميزة Chrome Firefox Internet Explorer Opera Safari
الدعم الأساسي 38 36 غير مدعومة 25 9

على الرغم من أنَّ متصفح IE لا يدعم هذه الميزة، لكن متصفح Edge يدعمها بدءًا من الإصدار 12.

مصادر ومواصفات