يوم جيد ، أيها الأصدقاء!
الرمز هو نوع بيانات بدائي تم تقديمه في ECMAScript2015 (ES6) والذي يسمح لك بإنشاء معرفات فريدة: const uniqueKey = Symbol ('SymbolName').
يمكنك استخدام الرموز كمفاتيح لخصائص الكائن. تسمى الرموز التي تتعامل معها JavaScript بطريقة خاصة الرموز المعروفة جيدًا . يتم استخدام هذه الأحرف بواسطة خوارزميات JavaScript مضمنة. على سبيل المثال ، يتم استخدام Symbol.iterator للتكرار على عناصر المصفوفات والسلاسل. يمكن استخدامه أيضًا لتحديد وظائف التكرار الخاصة بك.
تلعب هذه الرموز دورًا مهمًا لأنها تسمح لك بضبط سلوك الكائنات.
كونك فريدًا ، فإن استخدام الرموز كمفاتيح كائن (بدلاً من السلاسل) يجعل من السهل إضافة وظائف جديدة إلى الكائنات. في الوقت نفسه ، لا داعي للقلق بشأن الاصطدامات بين المفاتيح (نظرًا لأن كل حرف فريد من نوعه) ، والذي يمكن أن يصبح مشكلة عند استخدام السلاسل.
ستركز هذه المقالة على الرموز المعروفة مع أمثلة على استخدامها.
من أجل البساطة ، فإن بناء الجملة للرمز المشهور. رموز <name> بتنسيق<name>. على سبيل المثال ، يتم تمثيل Symbol.iterator كـمكرر ، Symbol.toPrimitive كـtoPrimitive ، إلخ.
إذا قلنا أن الكائن لديه طريقةمكرر ، فإن الكائن يحتوي على خاصية تسمى Symbol.iterator ، ممثلة بوظيفة: {[Symbol.iterator]: function () {}}.
1. مقدمة موجزة للرموز
الحرف هو نوع بدائي (مثل رقم أو سلسلة أو منطقية) ، فريد وغير قابل للتغيير (غير قابل للتغيير).
لإنشاء رمز ، اتصل بوظيفة Symbol () باستخدام وسيطة اختيارية - الاسم أو ، بشكل أكثر دقة ، وصف الرمز:
const mySymbol = Symbol()
const namedSymbol = Symbol('myName')
typeof mySymbol // symbol
typeof namedSymbol // symbol
mySymbol و namedSymbol هي رموز بدائية. تم تسمية هذا الرمز باسم "myName" ، والذي يستخدم عادةً لتصحيح التعليمات البرمجية.
تنشئ كل استدعاء لـ Symbol () رمزًا فريدًا جديدًا. حرفان فريدان (أو خاصان) حتى لو كان لهما نفس الاسم:
const first = Symbol()
const second = Symbol()
first === second // false
const firstNamed = Symbol('Lorem')
const secondNamed = Symbol('Lorem')
firstNamed === secondNamed // false
يمكن أن تكون الرموز مفاتيح للأشياء. للقيام بذلك ، يجب عليك استخدام بناء جملة الخاصية المحسوبة ([الرمز]) في تعريف الكائن الحرفي أو تعريف الفئة:
const strSymbol = Symbol('String')
const myObj = {
num: 1,
[strSymbol]: 'Hello World'
}
myObj[strSymbol] // Hello World
Object.getOwnPropertyNames(myObj) // ['num']
Object.getOwnPropertySymbols(myObj) // [Symbol(String)]
لا يمكن استرداد خصائص الرمز باستخدام Object.keys () أو Object.getOwnPropertyNames (). للوصول إليها ، تحتاج إلى استخدام الوظيفة الخاصة Object.getOwnPropertySymbols ().
يتيح لك استخدام الرموز المعروفة كمفاتيح تغيير سلوك الكائنات.
الرموز المعروفة متاحة كخصائص غير قابلة للعد ، وغير قابلة للتغيير ، وغير قابلة للتكوين لكائن الرمز. للحصول عليها ، استخدم تدوين النقطة: Symbol.iterator ، Symbol.hasInstance ، إلخ.
إليك كيفية الحصول على قائمة بالرموز المعروفة:
Object.getOwnPropertyNames(Symbol)
// ["hasInstance", "isConcatSpreadable", "iterator", "toPrimitive",
// "toStringTag", "unscopables", "match", "replace", "search",
// "split", "species", ...]
typeof Symbol.iterator // symbol
يُرجع Object.getOwnPropertyNames (Symbol) قائمة بالخصائص الأصلية لكائن Symbol ، بما في ذلك الرموز المعروفة. Symbol.iterator هو رمز من النوع ، بالطبع.
2.iterator ، الذي يسمح لك بجعل الكائنات قابلة للتكرار (قابلة للتكرار)
ربما يكون Symbol.iterator هو الرمز الأكثر شهرة. يسمح لك بتعريف كيفية تكرار كائن ما باستخدام جملة مقابل جملة أو عامل انتشار (وما إذا كان يجب تكرارها على الإطلاق).
العديد من الأنواع المضمنة مثل السلاسل أو المصفوفات أو الخرائط أو المجموعات أو المجموعات قابلة للتكرار افتراضيًا لأن لها طريقةمكرر:
const myStr = 'Hi'
typeof myStr[Symbol.iterator] // function
for (const char of myStr) {
console.log(char) // : 'H', 'i'
}
[...myStr] // ['H', 'i']
يحتوي المتغير myStr على سلسلة أولية لها خاصية Symbol.iterator. تحتوي هذه الخاصية على وظيفة تستخدم للتكرار على الأحرف في سلسلة.
يجب أن يتوافق الكائن الذي يتم فيه تعريف طريقة Symbol.iterator مع بروتوكول التكرار (المكرر) . بتعبير أدق ، يجب أن تعيد هذه الطريقة كائنًا يتوافق مع البروتوكول المحدد. يجب أن يكون لمثل هذا الكائن طريقة () تالية تُرجع {القيمة: <iterator_value> ، تم: <boolean_finished_iterator>}.
في المثال التالي ، قمنا بإنشاء كائن myMethods قابل للتكرار يسمح لنا بالتكرار على طرقه:
function methodsIterator() {
let index = 0
const methods = Object.keys(this)
.filter(key => typeof this[key] === 'function')
return {
next: () => ({
done: index === methods.length,
value: methods[index++]
})
}
}
const myMethods = {
toString: () => '[object myMethods]',
sum: (a, b) => a + b,
numbers: [1, 3, 5],
[Symbol.iterator]: methodsIterator
}
for (const method of myMethods) {
console.log(method) // toString, sum
}
methodIterator () هي دالة تقوم بإرجاع مكرر {next: function () {}}. يعرّف كائن myMethods خاصية محسوبة [Symbol.iterator] باستخدام قيم methodIterator. هذا يجعل الكائن قابلاً للتكرار باستخدام حلقة for-of. يمكن أيضًا الحصول على طرق الكائن باستخدام [... myMethods]. يمكن تحويل مثل هذا الكائن إلى مصفوفة باستخدام Array.from (myMethods).
يمكن تبسيط إنشاء كائن متكرر باستخدام وظيفة المولد . تقوم هذه الدالة بإرجاع كائن Generator الذي يتوافق مع بروتوكول التكرار.
لنقم بإنشاء فئة فيبوناتشي باستخدام طريقةiterator التي تولد سلسلة من أرقام فيبوناتشي:
class Fibonacci {
constructor(n) {
this.n = n
}
*[Symbol.iterator]() {
let a = 0, b = 1, index = 0
while (index < this.n) {
index++
let current = a
a = b
b = current + a
yield current
}
}
}
const sequence = new Fibonacci(6)
const numbers = [...sequence]
console.log(numbers) // [0, 1, 1, 2, 3, 5]
* يحدد [Symbol.iterator] () {} طريقة الفئة - وظيفة المولد. يتوافق مثيل فيبوناتشي مع بروتوكول القوة الغاشمة. يستدعي عامل الانتشار طريقةiterator لإنشاء مصفوفة من الأرقام.
إذا احتوى نوع أو كائن بدائي علىمكرر ، فيمكن استخدامه في السيناريوهات التالية:
- التكرار فوق العناصر بـ لـ
- إنشاء مصفوفة من العناصر باستخدام عامل الانتشار
- إنشاء مصفوفة باستخدام Array.from (iterableObject)
- في تعبير العائد * لتمريره إلى مولد آخر
- في المنشئات Map () و WeakMap () و Set () و WeakSet ()
- في الطرق الثابتة Promise.all () ، Promise.race () ، إلخ.
يمكنك قراءة المزيد حول إنشاء كائن قابل للتكرار هنا .
3.hasInstance لإعداد مثيل
بشكل افتراضي ، يتحقق مثيل الكائن من عامل المُنشئ من وجود كائن Constructor.prototype في سلسلة النموذج الأولي obj. لنفكر في مثال:
function Constructor() {
// ...
}
const obj = new Constructor()
const objProto = Object.getPrototypeOf(obj)
objProto === Constructor.prototype // true
obj instanceof Constructor // true
obj instanceof Object // true
obj exampleof Constructor يعيد true لأن النموذج الأولي لـ obj هو Constructor.prototype (كنتيجة لاستدعاء المنشئ). يشير المثيل إلى سلسلة النموذج الأولي حسب الحاجة ، لذا فإن كائن مثيل الكائن يعيد صحيحًا أيضًا.
يحتاج تطبيقك أحيانًا إلى فحص أكثر صرامة للمثيلات.
لحسن الحظ ، لدينا القدرة على تحديد طريقةhasInstance لتغيير سلوك exampleof. مثيل كائن النوع يكافئ النوع [Symbol.hasInstance] (obj).
دعنا نتحقق مما إذا كانت المتغيرات قابلة للتكرار:
class Iterable {
static [Symbol.hasInstance](obj) {
return typeof obj[Symbol.iterator] === 'function'
}
}
const arr = [1, 3, 5]
const str = 'Hi'
const num = 21
arr instanceof Iterable // true
str instanceof Iterable // true
num instanceof Iterable // false
تحتوي الفئة القابلة للتكرار على طريقة ثابتةhasInstance. تتحقق هذه الطريقة مما إذا كان الهدف قابلاً للتكرار ، أي ما إذا كان يحتوي على خاصية Symbol.iterator. arr و str قابلة للتكرار ، لكن الأسطوانات ليست كذلك.
4.toPrimitive لتحويل كائن إلى كائن بدائي
استخدم Symbol.toPrimitive لتعريف خاصية تكون قيمتها كائنًا لوظيفة تحويل بدائية. تأخذtoPrimitive معلمة واحدة أو تلميحًا يمكن أن يكون رقمًا أو سلسلة أو افتراضيًا. يشير التلميح إلى نوع القيمة المعادة.
دعنا نحسن تحويل المصفوفة:
function arrayToPrimitive(hint) {
if (hint === 'number') {
return this.reduce((x, y) => x + y)
} else if (hint === 'string') {
return `[${this.join(', ')}]`
} else {
// hint
return this.toString()
}
}
const array = [1, 3, 5]
array[Symbol.toPrimitive] = arrayToPrimitive
// . hint
+ array // 9
// . hint
`array is ${array}` // array is [1, 3, 5]
// . hint default
'array elements: ' + array // array elements: 1,3,5
arrayToPrimitive (تلميح) هي دالة تقوم بتحويل مصفوفة إلى مجموعة أساسية بناءً على قيمة التلميح. يؤدي تعيين المصفوفة [Symbol.toPrimitive] إلى arrayToPrimitive على إجبار المصفوفة على استخدام طريقة التحويل الجديدة. يستدعي إجراء + صفيفtoPrimitive مع تلميح بقيمة رقم. يتم إرجاع مجموع عناصر المصفوفة. المصفوفة هي $ {array} تستدعيtoPrimitive مع تلميح = سلسلة. يتم تحويل المصفوفة إلى السلسلة النصية '[1، 3، 5]'. أخيرًا 'عناصر المصفوفة:' + المصفوفة تستخدم تلميح = افتراضي للتحويل. يتم تحويل المصفوفة إلى "1،3،5".
تُستخدم الطريقةtoPrimitive لتمثيل كائن كنوع أولي:
- عند استخدام عامل المساواة الفضفاض (المجرد): الكائن == بدائي
- عند استخدام عامل الجمع / التسلسل: كائن + بدائي
- عند استخدام عامل الطرح: الكائن - بدائي
- في مواقف مختلفة ، تحويل كائن إلى كائن بدائي: سلسلة (كائن) ، رقم (كائن) ، إلخ.
5.toStringTag لإنشاء وصف قياسي للكائن
استخدم Symbol.toStringTag لتعريف خاصية تكون قيمتها عبارة عن سلسلة تصف نوع الكائن. يتم استخدام الأسلوبtoStringTag بواسطة Object.prototype.toString (). تحدد
المواصفات القيم الافتراضية التي يتم إرجاعها بواسطة Object.prototype.toString () للعديد من الأنواع:
const toString = Object.prototype.toString
toString.call(undefined) // [object Undefined]
toString.call(null) // [object Null]
toString.call([1, 4]) // [object Array]
toString.call('Hello') // [object String]
toString.call(15) // [object Number]
toString.call(true) // [object Boolean]
// Function, Arguments, Error, Date, RegExp ..
toString.call({}) // [object Object]
لا تحتوي هذه الأنواع على خاصية Symbol.toStringTag لأن خوارزمية Object.prototype.toString () تقيمها بطريقة خاصة.
يتم تعريف الخاصية المعنية في أنواع مثل الرموز ووظائف المولد والبطاقات والوعود وما إلى ذلك. ضع في اعتبارك مثالاً:
const toString = Object.prototype.toString
const noop = function() { }
Symbol.iterator[Symbol.toStringTag] // Symbol
(function* () {})[Symbol.toStringTag] // GeneratorFunction
new Map()[Symbol.toStringTag] // Map
new Promise(noop)[Symbol.toStringTag] // Promise
toString.call(Symbol.iterator) // [object Symbol]
toString.call(function* () {}) // [object GeneratorFunction]
toString.call(new Map()) // [object Map]
toString.call(new Promise(noop)) // [object Promise]
في حالة عدم كون الكائن من مجموعة نوع قياسي ولا يحتوي على الخاصيةtoStringTag ، يتم إرجاع الكائن. بالطبع يمكننا تغيير هذا:
const toString = Object.prototype.toString
class SimpleClass { }
toString.call(new SimpleClass) // [object Object]
class MyTypeClass {
constructor() {
this[Symbol.toStringTag] = 'MyType'
}
}
toString.call(new MyTypeClass) // [object MyType]
لا يحتوي مثيل فئة SimpleClass على الخاصيةtoStringTag ، لذا فإن Object.prototype.toString () ترجع [كائن كائن]. يقوم مُنشئ الفئة MyTypeClass بتعيين الخاصيةtoStringTag للمثيل ذي القيمة MyType ، لذلك يقوم Object.prototype.toString () بإرجاع [كائن MyType].
لاحظ أنه تم تقديمtoStringTag لأسباب تتعلق بالتوافق مع الإصدارات السابقة. استخدامه غير مرغوب فيه. من الأفضل استخدام exampleof (معhasInstance) أو typeof لتحديد نوع الكائن.
6.الأنواع لإنشاء كائن مشتق
استخدم Symbol.species لتعريف خاصية تكون قيمتها دالة منشئ تُستخدم لإنشاء كائنات مشتقة.
قيمةالأنواع للعديد من المنشئين هي المنشئون أنفسهم:
Array[Symbol.species] === Array // true
Map[Symbol.species] === Map // true
RegExp[Symbol.species] === RegExp // true
أولاً ، لاحظ أن الكائن المشتق هو كائن يتم إرجاعه بعد إجراء عملية محددة على الكائن الأصلي. على سبيل المثال ، يؤدي استدعاء map () إلى إرجاع كائن مشتق - نتيجة تحويل عناصر المصفوفة.
عادة ، تشير الكائنات المشتقة إلى نفس المُنشئ مثل الكائنات الأصلية. لكن في بعض الأحيان يصبح من الضروري تحديد مُنشئ آخر (ربما يكون أحد الفئات القياسية): هذا هو المكان الذي يمكن أن تساعد فيهtypes.
لنفترض أننا قمنا بتوسيع مُنشئ Array مع فئة MyArray التابعة لإضافة بعض الطرق المفيدة. ومع ذلك ، نريد أن تكون مُنشئ الكائنات المشتقة من مثيل MyArray هي Array. للقيام بذلك ، تحتاج إلى تحديد خاصية محسوبةالأنواع بقيمة صفيف:
class MyArray extends Array {
isEmpty() {
return this.length === 0
}
static get [Symbol.species]() {
return Array
}
}
const array = new MyArray(2, 3, 5)
array.isEmpty() // false
const odds = array.filter(item => item % 2 === 1)
odds instanceof Array // true
odds instanceof MyArray // false
يعرّف MyArray الخاصية الثابتة المحسوبة Symbol.species. وهي تحدد أن منشئ الكائنات المشتقة يجب أن يكون مُنشئ الصفيف. لاحقًا عند تصفية عناصر المصفوفة ، تُرجع الدالة array.filter () Array.
يتم استخدام الخاصيةالأنواع المحسوبة بواسطة أساليب الصفيف والمصفوفات المكتوبة مثل map () و concat () و slice () و splice () ، والتي تُعيد الكائنات المشتقة. يمكن أن يكون استخدام هذه الخاصية مفيدًا في توسيع الخرائط أو التعبيرات العادية أو الوعود مع الحفاظ على المنشئ الأصلي.
7. قم بإنشاء تعبير عادي في شكل كائن:match،replace،بحث وsplit
يحتوي النموذج الأولي للسلسلة على 4 توابع تتخذ التعبيرات النمطية كوسيطة:
- String.prototype.match (regExp)
- String.prototype.replace (regExp ، newSubstr)
- String.prototype.search (regExp)
- String.prototype.split (regExp، limit)
يسمح ES6 لهذه الطرق بقبول أنواع أخرى طالما تم تحديد الخصائص المحسوبة المقابلة:match وreplace وبحث وsplit.
من الغريب أن النموذج الأولي RegExp يحتوي على الطرق المحددة ، والمحددة أيضًا باستخدام الرموز:
typeof RegExp.prototype[Symbol.match] // function
typeof RegExp.prototype[Symbol.replace] // function
typeof RegExp.prototype[Symbol.search] // function
typeof RegExp.prototype[Symbol.split] // function
في المثال التالي ، نحدد فئة يمكن استخدامها بدلاً من التعبير العادي:
class Expression {
constructor(pattern) {
this.pattern = pattern
}
[Symbol.match](str) {
return str.includes(this.pattern)
}
[Symbol.replace](str, replace) {
return str.split(this.pattern).join(replace)
}
[Symbol.search](str) {
return str.indexOf(this.pattern)
}
[Symbol.split](str) {
return str.split(this.pattern)
}
}
const sunExp = new Expression('')
' '.match(sunExp) // true
' '.match(sunExp) // false
' day'.replace(sunExp, '') // ' '
' '.search(sunExp) // 8
''.split(sunExp) // ['', '']
تحدد فئة Expression أساليبmatch وreplace وsearch وsplit. ثم يتم استخدام مثيل من هذه الفئة - sunExp في الطرق المناسبة بدلاً من التعبير العادي.
8.isConcatSpreadable لتحويل الكائن إلى مجموعة
Symbol.isConcatSpreadable هي قيمة منطقية تشير إلى أنه يمكن تحويل كائن إلى مصفوفة باستخدام طريقة Array.prototype.concat ().
بشكل افتراضي ، يسترد التابع concat () عناصر المصفوفة (يفكك المصفوفة إلى العناصر التي تتكون منها) عند ربط المصفوفات:
const letters = ['a', 'b']
const otherLetters = ['c', 'd']
otherLetters.concat('e', letters) // ['c', 'd', 'e', 'a', 'b']
لتسلسل المصفوفتين ، قم بتمرير الأحرف كوسيطة إلى طريقة concat (). تصبح عناصر مصفوفة الحروف جزءًا من نتيجة التسلسل: ['c'، 'd'، 'e'، 'a'، 'b'].
لمنع تحلل المصفوفة إلى عناصر وجعل المصفوفة جزءًا من نتيجة الاتحاد كما هي ، يجب تعيين الخاصيةisConcatSpreadable على false:
const letters = ['a', 'b']
letters[Symbol.isConcatSpreadable] = false
const otherLetters = ['c', 'd']
otherLetters.concat('e', letters) // ['c', 'd', 'e', ['a', 'b']]
على عكس المصفوفة ، فإن طريقة concat () لا تفكك كائنات تشبه المصفوفة إلى عناصر. يمكن أيضًا تغيير هذا السلوك باستخدامisConcatSpreadable:
const letters = { 0: 'a', 1: 'b', length: 2 }
const otherLetters = ['c', 'd']
otherLetters.concat('e', letters)
// ['c', 'd', 'e', {0: 'a', 1: 'b', length: 2}]
letters[Symbol.isConcatSpreadable] = true
otherLetters.concat('e', letters) // ['c', 'd', 'e', 'a', 'b']
9.unscopables للوصول إلى الخصائص باستخدام
Symbol.unscopables هي خاصية محسوبة يتم استبعاد أسماء علمها من الكائن المضاف إلى بداية سلسلة النطاق باستخدام العبارة with. الخاصيةunscopables لها التنسيق التالي: {propertyName: <boolean_exclude_binding>}.
يعرّف ES6unscopables للمصفوفات فقط. يتم ذلك لإخفاء عمليات جديدة يمكنها استبدال المتغيرات التي تحمل الاسم نفسه في الكود القديم:
Array.prototype[Symbol.unscopables]
// { copyWithin: true, entries: true, fill: true,
// find: true, findIndex: true, keys: true }
let numbers = [1, 3, 5]
with (numbers) {
concat(7) // [1, 3, 5, 7]
entries // ReferenceError: entries is not defined
}
يمكننا الوصول إلى التابع concat () في with body ، لأن هذه الطريقة غير واردة في الخاصيةunscopables. تم تحديد طريقة إدخالات () في هذه الخاصية وتم تعيينها على "صواب" ، مما يجعلها غير متاحة في الداخل باستخدام.
تم تقديمunscopables فقط للتوافق مع الإصدارات السابقة مع التعليمات البرمجية القديمة باستخدام العبارة with (مهملة وغير مسموح بها في الوضع المتشدد).
أتمنى أن تجد شيئًا مثيرًا للاهتمام لنفسك. شكرآ لك على أهتمامك.