for...of
في JavaScript
التعبير البرمجي for...of
يمر على الكائنات التي يمكن الدوران على عناصرها (iterable objects) بما في ذلك الكائن Array
و Map
و Set
و String
و TypedArray
و Arguments
، وتنفيذ تعابير برمجية معيّنة على قيمة كل خاصية من خاصيات تلك الكائنات.
البنية العامة
for (variable of iterable) {
statement
}
variable
ستُنسَد قيمة الخاصية مختلفة لهذا المتغير في كل دورة.
iterable
خاصيات الكائن القابلة للدوران (iterable properties).
الوصف
الفرق بين حلقتَي التكرار for...of
و for...in
التعبيران البرمجيان for...in
و for...of
يُستخدَمان للدوران على شيءٍ ما، لكن الاختلاف الرئيسي بينهما هو ما هي الأشياء التي تدور حلقة التكرار عليها.
فالتعبير for...in
يدور على الخاصيات الكائن القابلة للإحصاء (enumerable properties) بترتيب إدخالها الأصلي.
أما التعبير for...of
فسيدور على البيانات التي يُعرِّفها كائن iterable
على أنها قابلة للدوران.
هذا المثال يوضِّح الفرق بين استخدام حلقة for...of
و for...in
عند استخدامهما على كائن Array
:
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
iterable.foo = 'hello';
for (let i in iterable) {
console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
}
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // 0, 1, 2, "foo"
}
}
for (let i of iterable) {
console.log(i); // 3, 5, 7
}
سنُجزِّئ المثال السابق إلى أقسام ليسهل فهمها:
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
iterable.foo = 'hello';
سيرث كل كائن الخاصية objCustom
وكل كائن Array
سيرث الخاصية arrCustom
وذلك بسبب إضافة تلك الخاصيات إلى Object.prototype
و Array.prototype
؛ وبالتالي سيرثها الكائن iterable
.
for (let i in iterable) {
console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
}
حلقة التكرار السابقة ستعرض الخاصيات القابلة للإحصاء (enumerable properties) بترتيب إضافتها الأصلي، ولن تؤدي إلى إظهار عناصر المصفوفة 3
أو 5
أو 7
أو hello
لأنها لا تُعدّ خاصياتٍ قابلةً للإحصاء؛ لكنها ستعرض فهارس المصفوفة والخاصية arrCustom
و objCustom
، لمزيدٍ من المعلومات راجع صفحة for...in
.
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // 0, 1, 2, "foo"
}
}
حلقة التكرار السابقة تشبه أوّل حلقة تكرار، لكنها تستخدم الدالة hasOwnProperty()
للتأكد إذا كانت الخاصية تابعةً للكائن مباشرةً وليست خاصيةً موروثةً؛ لذا ستُعرَض الخاصيات 0
و 1
و 2
و foo
لأنها تابعة مباشرةً للكائن، ولن تُعرَض الخاصيات arrCustom
و objCustom
لأنها موروثة.
for (let i of iterable) {
console.log(i); // 3, 5, 7
}
حلقة التكرار السابقة تمر وتعرض القيم التي يُعرِّف كائن iterable
أنه يمكن المرور عليها، وهي عناصر المصفوفة 3
و 5
و 7
ولن تُعرَض خاصيات الكائن قط.
إنهاء التكرارات
يمكن إنهاء عملية الدوران في حلقات for...of
باستخدام التعابير البرمجية break
أو continue
أو throw
أو return
:
function* foo(){
yield 1;
yield 2;
yield 3;
};
for (let o of foo()) {
console.log(o);
break; // إغلاق التكرار
}
أمثلة
المرور على Array
let iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
يمكنك أيضًا استخدام const
بدلًا من let
، لكن حينئذٍ لن تتمكن من إعادة إسناد قيمة للمتغير داخل الكتلة:
let iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
// 10
// 20
// 30
المرور على String
let iterable = 'boo';
for (let value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
المرور على TypedArray
let iterable = new Uint8Array([0x00, 0xff]);
for (let value of iterable) {
console.log(value);
}
// 0
// 255
المرور على Map
let iterable = new Map([['a', 1], ['b', 2], ['c', 3]]);
for (let entry of iterable) {
console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]
for (let [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
المرور على Set
let iterable = new Set([1, 1, 2, 2, 3, 3]);
for (let value of iterable) {
console.log(value);
}
// 1
// 2
// 3
المرور على كائن Arguments
(function() {
for (let argument of arguments) {
console.log(argument);
}
})(1, 2, 3);
// 1
// 2
// 3
المرور على قائمة عقد في DOM
يمكن المرور على قائمة عقد في DOM مثل NodeList
، فالمثال الآتي يُضيف الصنف read
إلى الفقرات <p>
التي تكون أبناءً مباشرين للعنصر <article>
:
let articleParagraphs = document.querySelectorAll('article > p');
for (let paragraph of articleParagraphs) {
paragraph.classList.add('read');
}
المرور على المولِّدات
function* fibonacci() { // دالة مولِّدة
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (let n of fibonacci()) {
console.log(n);
// إيقاف السلسلة عند العدد 1000
if (n >= 1000) {
break;
}
}
المرور على الكائنات التي تُطبِّق بروتوكول iterable
يمكن أيضًا استخدام حلقة التكرار for...of
للمرور على الكائنات التي تُطبِّق بروتوكول iterable صراحةً:
var iterable = {
[Symbol.iterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return { value: this.i++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (var value of iterable) {
console.log(value);
}
// 0
// 1
// 2
دعم المتصفحات
الميزة | Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
الدعم الأساسي | 38 | 13 | غير مدعومة | 25 | 8 |
إغلاق التكرارات | 51 | 53 | غير مدعومة | مدعومة | مدعومة |
مصادر ومواصفات
- مسودة المعيار ECMAScript Latest Draft.
- معيار ECMAScript 2015 (6th Edition).