â€ģ゚マホ寞åŋœã¯ã—ãĻぞせん。

ã‚ĢテゴãƒĒãƒŧ: JavaScript

é…åˆ—ã§é‡č¤‡ã™ã‚‹é …į›Žã‚’取り除くやつ4į¨Žã€‚īŧˆé…åˆ—とかおれおれAdvent Calendar2018 – 18æ—Ĩį›Žīŧ‰

ã‚ĢテゴãƒĒãƒŧ: JavaScript

LINDORぎAdvent CalendarīŧˆæœŦį‰Šīŧ‰ãŽ18æ—Ĩį›Žã‚’開けたところ。
配列とかおれおれAdvent Calendar2018 – 18æ—Ĩį›Ž

ありそうでãĒã„ãƒĄã‚Ŋãƒƒãƒ‰ã€ãã‚ŒãŒé‡č¤‡æŽ’é™¤ã§ã™ã€‚

Underscore.jsやLodashãĢは uniq() ãĻぎがあãŖãĻ、こんãĒ感じ↓でäŊŋえぞす。

const arr0 = [11, 22, 11, 22, 33];
const arr1 = _.uniq(arr0);

JavaScriptãĢはありぞせん。

Set をäŊŋう

単į´”ãĒ値ãĒらこれが一į•Ēį°Ąå˜ã€‚

Set ã¯é‡č¤‡ã—ãĒい値をæ ŧį´ã§ãã‚‹ã‚Ēブジェクトです。 ... ã§åą•é–‹ã—ãĻ配列ã‚Ēブジェクトへ再変換しぞす。

const arr0 = [11, 22, 11, 22, 33];
const arr1 = [...new Set(arr0)];
console.log(arr1); // => [ 11, 22, 33 ]

includes() でįĸēčĒã™ã‚‹

これが一į•Ēį´ į›´ã€‚

includes() は配列が指厚ぎčĻį´ ã‚’åĢむかおうかčĒŋずるやつです。 é‡č¤‡ã—ãĻãĒかãŖたらčŋŊ加するだけ。

const arr0 = [11, 22, 11, 22, 33];

const arr1 = arr0.reduce((a, v) => {
  if (!a.includes(v)) {
    a.push(v);
  }
  return a;
}, []);

console.log(arr1); // => [ 11, 22, 33 ]

reduce() ãĢč‡ĒäŋĄãŒãĒければ forEach() でも。

const arr1 = [];
arr0.forEach((v) => {
  if (!arr1.includes(v)) {
    arr1.push(v);
  }
});

some() でやる

Set も includes() もã‚Ēブジェクトぎ場合は厌全ãĢ同じイãƒŗã‚šã‚ŋãƒŗ゚でãĒいと反åŋœã—ãĒいぎで、そぎ場合は some() がよろしいかと。

もãŖとč‡Ēį”ąåēĻがéĢ˜ã„。

const arr0 = [
  { id: '11', num: 1 },
  { id: '22', num: 2 },
  { id: '11', num: 3 },
  { id: '22', num: 4 },
  { id: '33', num: 5 },
];

const arr1 = arr0.reduce((a, v) => {
  if (!a.some((e) => e.id === v.id)) {
    a.push(v);
  }
  return a;
}, []);

console.log(arr1);
// [ { id: '11', num: 1 },
//   { id: '22', num: 2 },
//   { id: '33', num: 5 } ]

é‡č¤‡åˆ†ã¯å…ˆãĢå‡ēãĻくるやつがå„Ē先です。

Map をäŊŋう

これもã‚ĒブジェクトãĢ寞åŋœã€‚初čĻ‹ã§ã€ŒãŽã‚‡ãˆãƒŧ」ãĻãĒりそう。

const arr0 = [
  { id: '11', num: 1 },
  { id: '22', num: 2 },
  { id: '11', num: 3 },
  { id: '22', num: 4 },
  { id: '33', num: 5 },
];

const arr1 = [...new Map(arr0.map((v) => [v.id, v])).values()];

console.log(arr1);
// [ { id: '11', num: 3 },
//   { id: '22', num: 4 },
//   { id: '33', num: 5 } ]

äģ•įĩ„ãŋは Set と some() ぎįĩ„ãŋ合わせです、だいたい。

  1. map() でIDとã‚ĒブジェクトæœŦäŊ“ぎįĩ„ãŋ合わせへ変換
  2. įĩ„ãŋ合わせを元ãĢ Map ã‚ĒブジェクトをäŊœæˆã€‚ここでIDīŧˆ Map ぎキãƒŧīŧ‰ãŒé‡č¤‡ã™ã‚‹ã‚‚ぎは排除īŧ‰
  3. values() でã‚Ēブジェクトīŧˆ Map ぎ値īŧ‰ãŽãŋぎ反垊子を垗る
  4. ゚プãƒŦッド構文 ... をäŧ´ã†é…åˆ—初期化子 [] で配列ãĢ。厌成

é‡č¤‡åˆ†ã¯åžŒã‚ãĢあるやつがå„Ē先īŧˆä¸Šæ›¸ãīŧ‰ã§ã™ã€‚

おしぞい

ãĒã‚“ã‹č‰¯ã„ãŽã‚ã‚‹īŧŸ

å‚č€ƒ

だいたいぎįš°ã‚Ščŋ”しは配列ぎforEach()でいける。īŧˆé…åˆ—とかおれおれAdvent Calendar2018 – 17æ—Ĩį›Žīŧ‰

ã‚ĢテゴãƒĒãƒŧ: JavaScript

LINDORぎAdvent CalendarīŧˆæœŦį‰Šīŧ‰ãŽ16æ—Ĩį›Žã‚’開けたところ。
配列とかおれおれAdvent Calendar2018 – 16æ—Ĩį›Ž

įš°ã‚Ščŋ”しãĢもいろいろあるよ。

お垅たせīŧã€€įžäģŖįš„JavaScriptではä¸ģæĩãŽã€ã‚ˆã for をįŊŽãæ›ãˆäŊŋうやつです。

ぞず for ぎ䞋

const arr = [11, 22, 33];

for (let i = 0; i < arr.length; i++) {
  const value = arr[i];
  console.log(value);
}

forEach() ãĢした䞋

const arr = [11, 22, 33];

arr.forEach((value) => {
  console.log(value);
});

かんたãƒŧん。

į°Ąå˜ãĒぎで、あぞりčĒžã‚‹ã“とはありぞせん。

äģ•æ§˜

åŧ•æ•°ã¯äģ–ぎこれįŗģãŽé…åˆ—ãƒĄã‚Ŋッドと同じです。

forEach() はåŧ•æ•°ãĢ、é–ĸ数ã‚Ēãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’ã˛ã¨ã¤å—ã‘å–ã‚Šãžã™ã€‚ æˆģり値はありぞせん。 (undefined)

与えるé–ĸ数は、3つぎåŧ•æ•°ãŒä¸Žãˆã‚‰ã‚Œãžã™ã€‚

  • value â€Ļ 配列ぎčĻį´ 
  • index â€Ļ イãƒŗデック゚
  • array â€Ļ 操äŊœä¸­ãŽé…åˆ—æœŦäŊ“
const arr = [11, 22, 33];

arr.forEach((value, index, array) => {
  console.log(index, ':', value, ' <- ', array[index], array);
});

įŦŦ2åŧ•æ•°

原は forEach() そぎäģ–ãĢはãĢはįŦŦ2åŧ•æ•° thisArg がありぞす。

これはįŦŦ1åŧ•æ•°ãŽé–ĸæ•°åŽŸčĄŒæ™‚ãĢ this へ束į¸›ã•ã‚Œã‚‹ã‚ĒブジェクトãĒんだけお、 čŋ‘嚴は this が変わらãĒいã‚ĸロãƒŧé–ĸ数をį”¨ã„るぎがä¸ģæĩãĒぎで、 äŊŋう場éĸは少ãĒいかãĒと思いぞす。

const obj = {
  arr: [11, 22, 33],

  logAll: function () {
    this.arr.forEach(function (value) {
      this.output(value);
    }, this);
  },

  output: function (msg) {
    console.log('Message: ', msg);
  },
};

obj.logAll();

再刊į”¨

これもäģ–ãŽé…åˆ—ãƒĄã‚Ŋッドと同様ãĒんだけお、配列äģĨ外ぎã‚Ēブジェクトへ遊į”¨ã—ãĻも遊切ãĢ動äŊœã™ã‚‹ã‚ˆã†č¨­č¨ˆã•ã‚ŒãĻいぞす。

例

åŸēæœŦįš„ãĢ forEach() でできãĻ for でできãĒいことãŖãĻãĒいんじゃãĒいかãĒ。

晎通ãĢįš°ã‚Ščŋ”す

const arr = [11, 22, 33];
arr.forEach((value) => {
  console.log(value);
});

配列内ぎäģ–ぎčĻį´ ã‚’参į…§ã™ã‚‹

えãƒŧと䞋えば毎嚴ぎäēēåŖã¨ã‹ã€ãã†ã„ã†æ•°å€¤æƒ…å ąãŒä¸Žãˆã‚‰ã‚ŒãĻ、そぎåĸ—減をčĻ‹ãĻいきたい場合。

ぞずは for 文でやる䞋。

const arr = [100, 110, 115, 103, 110, 90];

for (let i = 1; i < arr.length; i++) {
  const item1 = arr[i - 1];
  const item2 = arr[i];
  const diff = item2 - item1;
  const sign = diff < 0 ? '' : '+';
  console.log(`${item1} -> ${item2} (${sign}${diff})`);
}

// 100 -> 110 (+10)
// 110 -> 115 (+5)
// 115 -> 103 (-12)
// 103 -> 110 (+7)
// 110 -> 90 (-20)

ぞず最初ãĢ i = 1 から始めるぎはできãĒいぎで、įš°ã‚Ščŋ”しぎ中でéŖ›ã°ã—ぞす。īŧˆslice() とかするとイãƒŗãƒ‡ãƒƒã‚¯ã‚šãŒãšã‚ŒãĄã‚ƒã†ãŽã§æŗ¨æ„ã€‚そãŖãĄãŽæ–šãŒč‰¯ã„ã‹ã‚‚ã ã‘ãŠã€‚īŧ‰

ぞた配列全äŊ“は与えるé–ĸ数ぎįŦŦ3åŧ•æ•°ãĢもらえるぎで、これを刊į”¨ã—ぞす。

const arr = [100, 110, 115, 103, 110, 90];

arr.forEach((item2, i, all) => {
  if (i < 1) { return; }
  const item1 = all[i - 1];
  const diff = item2 - item1;
  const sign = diff < 0 ? '' : '+';
  console.log(`${item1} -> ${item2} (${sign}${diff})`);
});

ここで all は外側ぎ arr と同じãĒぎで、そãŖãĄã§ã‚‚č‰¯ã„ã§ã™ã€‚

įĩæžœã‚’配列ãĢしたいãĒら reduce() も有į”¨ã€‚

同じå‡Ļį†ã‚’įš°ã‚Ščŋ”す

é…åˆ—č¨˜æŗ•ã‹ã‚‰į›´æŽĨãƒĄã‚Ŋãƒƒãƒ‰åŽŸčĄŒã§ãã‚‹ãŽã§ã€åŒŋ名é–ĸ数ぎåŗæ™‚åŽŸčĄŒãŋたいãĒノãƒĒで、åŒŋ名配列ぎåŗ時刊į”¨ãĻãĒ感じでもäŊŋえぞす。

[
  '.target1',
  '.target2',
  '.target3',
].forEach((selector) => {
  const el = document.querySelector(selector);
  el.classList.add('targeted');
});

同じå‡Ļį†ã‚’ぞとめるãŖãĻãĒらé–ĸ数化がæ­Ŗč§Ŗだと思うんだけお、手čģŊãĢ書きたいときとか。

ã‚ģミã‚ŗロãƒŗã‚’čĄŒæœĢãĢįŊŽã‹ãĒい゚ã‚ŋイãƒĢぎ場合はごæŗ¨æ„ãã ã•ã„ã€‚é…åˆ—č¨˜æŗ• [] ãŒå‰ãŽčĄŒã¨įš‹ãŒãŖãĻしぞうぎで。

äŧŧãŸč¨ˆįŽ—をする

į¸Ļæ¨Ēä¸Ąæ–šå‘ãŽč¨ˆįŽ—とか、プロパテã‚Ŗ名はį•°ãĒるがįŽ—å‡ēæ–šæŗ•ã¯åŒã˜ã€ãŋたいãĒå ´éĸで。

// 抜į˛‹

[
  ['clientWidth', 'left'],
  ['clientHeight', 'top'],
].forEach(([sizeName, axisName]) => {
  const pos = (elWrapper[sizeName] - elItem[sizeName]) / 2;
  elItem.style[axisName] = `${pos}px`;
});

į´”į˛‹ãĒ計įŽ—と副äŊœį”¨ã‚’分けたい場合は map() でも。

forEach() でできãĒいこと

と、äģ–とįĩ„ãŋ合わせãĻぎやり斚。

forEach() は「先頭からæœĢ尞ぞでįš°ã‚Ščŋ”す」もぎãĒぎで、それäģĨ外ぎパã‚ŋãƒŧãƒŗでįš°ã‚Ščŋ”したい場合はã‚ŗãƒŧドこねこねしãĻやるåŋ…čĻãŒã‚りぞす。

先頭äģĨ外から、æœĢå°žäģĨ外ぞでぎįš°ã‚Ščŋ”し

あんぞりやらãĒい気もするけお、途中からとか途中ぞでとかはできぞせん。

split() とįĩ„ãŋ合わせぞす。

const arr = ['A', 'B', 'C'];

// 先頭から1個ぞでをéŖ›ã°ã™
// => B, C
arr.slice(1).forEach((item, i) => {
  console.log(`${i}: ${item}`);
});

// æœĢ尞から1個ぞでをéŖ›ã°ã™
// => A, B
arr.slice(0, -1).forEach((item, i) => {
  console.log(`${i}: ${item}`);
});

index をčĻ‹ãĻã‚ŗãƒŧãƒĢバックé–ĸ数で return するぎでもã‚ĸãƒĒ。

éŖ›ã°ã—ãĻ回す

一つéŖ›ã°ã—とかはできぞせん。

できãĒいぎで、įš°ã‚Ščŋ”しぎã‚ŗãƒŧãƒĢバックé–ĸ数でéƒŊåēĻ return しãĻ、äŊ•ã‚‚しãĒいようãĢしぞす。

const arr = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'];

// 3つごと
// => A, D, G, J, ...
arr.forEach((c, i) => {
  if (i % 3 !== 0) { return; }
  console.log(`${i}: ${c}`);
});

あるいはäē‹å‰ãĢåŧžã„ãĻおく。こãŖãĄãŽæ–šãŒčĻ‹ãŸį›Žã¯ãã‚Œã„ãŖãŊい。

const arr = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'];

// 3つごと
// => A, D, G, J, ...
arr
  .filter((_, i) => i % 3 === 0)
  .forEach((c, i) => console.log(`${i}: ${c}`));

逆順ãĢįš°ã‚Ščŋ”し

意外とこれができぞせん。常ãĢ先頭からです。

配列を逆順ãĢする reverse() ã¨ã„ã†ãŽã‚‚ã‚ã‚‹ã‚“ã ã‘ãŠã€ã“ã„ã¤ã¯å¯žčąĄãŽé…åˆ—č‡ĒäŊ“を逆順ãĢするį ´åŖŠįš„ãĒ副äŊœį”¨ãŒã‚るぎで、刊į”¨å¯čƒŊãĒå ´éĸが限厚įš„です。

const arr = ['A', 'B', 'C'];

arr.reverse().forEach((item, i) => {
  console.log(`${i}: ${item}`);
});
// => C, B, A

// 元ぎ配列が変わãŖãĄã‚ƒã†īŧ
console.log(arr); // => [ 'C', 'B', 'A' ]

äē‹å‰ãĢ複čŖŊしãĻから reverse() すればいいんだけお。

[...arr].reverse()

うãƒŧん、 reduceRight() ãŒč‰¯ã„ã‹ãĒ。

const arr = ['A', 'B', 'C'];

arr.reduceRight((_, item, i) => {
  console.log(`${i}: ${item}`);
}, 0);

これはこれでįŦŦ2åŧ•æ•°ãĢäŊ•ã‹ä¸Žãˆã‚‹ãŽã¨ã€ã‚ŗãƒŧãƒĢバックé–ĸ数ぎįŦŦ1åŧ•æ•°ã‚‚ value でãĒいもぎが与えられるぎをåŋ˜ã‚ŒãĒいようãĢ気をäģ˜ã‘ãĒいといけãĒい。

回ãŖãĻくる値をį„ĄčĻ–するīŧŸ

const arr = ['A', 'B', 'C'];

arr.forEach((_, i) => {
  const item = arr[arr.length - 1 - i];
  console.log(`${i}: ${item}`);
});

sort() ã—ãĄã‚ƒã†ãŽãŒæ„å‘ŗが明įž­ã§ä¸€į•Ēč‰¯ã„ã‹ã‚‚ã—ã‚ŒãĒい。

配列äģĨ外ぎ forEach()

晎通ぎã‚Ēブジェクト

配列へ変換しãĻやりぞす。

プロパテã‚Ŗ名だけ垗る䞋。

const obj = {
  a: 11,
  b: 22,
  c: 33,
};

Object.keys(obj).forEach((key) => {
  console.log(key);
});
// => a, b, c

äģ–ãĢ値だけを垗る Object.values() ã¨ã€ä¸Ąæ–šã‚’åž—ã‚‹ Object.entries() がありぞす。

for-of でやãŖたやつら。

Map, Set

こいつらは forEach() を持ãŖãĻぞす。だいたい同じ動き。

Map はイãƒŗデック゚ぎäģŖわりãĢキãƒŧが垗られぞす。 ですよねãƒŧ。

順åēã¯ã¯ã‚­ãƒŧをčŋŊ加した順。

const map = new Map([
  ['foo', 11],
  ['bar', 22],
  ['boo', 33],
]);
map.forEach((value, key) => {
  console.log(key, value);
});

Set ぎ場合、イãƒŗデック゚やキãƒŧとãĒる部分ãĢも値が与えられぞす。 わお。

ã“ãĄã‚‰ã‚‚é †åēã¯čŋŊ加順です。

const set = new Set([11, 22, 33]);
set.forEach((value, v2) => {
  console.log(value, v2, value === v2); // 11, 11, true į­‰
});

DOMįŗģぎ配列éĸ¨ã‚Ēブジェクト

でもäŊŋえたりしぞす。

const els = document.querySelectorAll('.target');
console.log(els instanceof Array); // => false
els.forEach((el, index) => {
  console.log(index, el);
});

forEach() äģĨ外ぎ、䞋えば map() とかはãĒいです。

中čēĢは厌全ãĢ配列ぎそれと同じ。そこらčžēãŽã‚‚ã†ãĄã‚‡ã„čŠŗã—ã„čŠąã‚’åˆĨį¨ŋãĢį”¨æ„ã—ぞした。

jQuery

ãĢは each() というぎがあãŖたり。

const $els = $('.target');
$els.each((index, el) => {
  console.log(index, el);
});

åŧ•æ•°ãŽé †åēãŒé•ã†į‚šãĢæŗ¨æ„ã€‚ īŧˆäģŠæ™‚äŊŋうぎかはわからãĒいけお。īŧ‰

jQueryã‚Ēブジェクトは反垊可čƒŊãĒぎで、 [...$els].forEach(fn) もäŊŋえぞす。

for と速åēĻéĸぎ比čŧƒ

ぞずįĩčĢ–ですが、éĢ˜é€ŸåŒ–ぎためãĢ forEach() をéŋけãĻ for ã‚’æŽĄį”¨ã™ã‚‹åŋ…čĻã¯ã‚りぞせん。

可čĒ­æ€§ã‚„č¨˜čŋ°ãŽį”¨æ„ã•ã‹ã‚‰č€ƒãˆãĻも forEach() ãŽæ–šãŒč‰¯ã„ã‚ˆã†ãĢ思いぞす。īŧˆå€‹äēēぎ感æƒŗです。īŧ‰

é…ã„ã‹ã¨č¨€ã‚ã‚Œã‚Œã°ã€ã‚‚ãĄã‚ã‚“é…ã„ã‚“ã ã‘ãŠã€ãã“ãŽé…ã•ãŒå•éĄŒãĢãĒるįŠļæŗã¯æ™Žé€šã€æ—ĸãĢį ´įļģしãĻぞすから。それよりも前ãĢ気ãĢã™ã‚‹ãšãå€‹æ‰€ãŒã‚ã‚‹ã¯ãšã§ã™ã€‚č¨ˆįŽ—量īŧˆ O(n2) とかそういうやつīŧ‰ã‚’č€ƒãˆãĻã€ãƒ•ã‚Ąã‚¤ãƒĢã‚ĸクã‚ģ゚やらį”ģéĸ再描į”ģやらぎ遅いAPIãĢ気をäģ˜ã‘ãĻ。

2012嚴ぎ原験がありぞす。0.001ミãƒĒį§’äģĨä¸‹ãŽåˇŽã§ã™ã€‚0.001į§’じゃãĒいよ。

よãŖãŊおæĨĩぞãŖた場éĸではåˆĨだけお、ぞあ垎åĻ™ãĒ速åēĻ性čƒŊåˇŽã§ã¯ãĒく抟čƒŊåˇŽã§é¸ãŧうね。

あ、でも

äŊ•ã‹æŽĸすとかで最垌ぞでįš°ã‚Ščŋ”すåŋ…čĻãŒãĒい場合は forEach() じゃãĒくãĻ find() とか some() とか、そういう遊切ãĒもぎをäŊŋおう。意å‘ŗが明įž­ãĢãĒãŖãĻ可čĒ­æ€§ã‚‚向上するし。

おしぞい

įš°ã‚Ščŋ”ã—ã‚ˇãƒĒãƒŧã‚ēおしぞい。はãƒŧé•ˇã‹ãŖた。

forEach() はäžŋ刊で大変よろしい。

é–ĸé€Ŗ

å‚č€ƒ

非同期ãĢįš°ã‚Ščŋ”すãĒらfor-await-of構文がäŊŋえる、けおäŊŋわãĒã„æ–šãŒč‰¯ã„ã‹ã‚‚ã€‚īŧˆé…åˆ—とかおれおれAdvent Calendar2018 – 16æ—Ĩį›Žīŧ‰

ã‚ĢテゴãƒĒãƒŧ: JavaScript

LINDORぎAdvent CalendarīŧˆæœŦį‰Šīŧ‰ãŽ16æ—Ĩį›Žã‚’開けたところ。
配列とかおれおれAdvent Calendar2018 – 16æ—Ĩį›Ž

įš°ã‚Ščŋ”しãĢもいろいろあるよ。

for-of ぎäēœį¨Žã§ã€éžåŒæœŸãĢįš°ã‚Ščŋ”すやつãĢ寞åŋœã—ぞす。

const sleep = (ms) => new Promise((f) => setTimeout(f, ms));

async function* foo () {
  yield 11;
  await sleep(1000);
  yield 22;
  await sleep(1000);
  yield 33;
}

(async () => {
  for await (const value of foo()) {
    console.log(value);
  }
})();

// 11
// 22
// 33

äģ•æ§˜

for-in, for-of と同じįĢ ã§čĒŦ明されぞす。

åŸēæœŦįš„ãĒå‡Ļį†ã‚‚それらと一įˇ’。ãĒぎで過åŽģ記äē‹ãĢもį›Žã‚’通しãĻおいãĻ頂きたいです。ãĒãĢとぞ。

async 内でぎãŋ刊į”¨å¯čƒŊ

await ãĒぎで。

// OK
(async () => {
  for await (const v of obj) {
    // â€Ļ
  }
});

// SyntaxError: Unexpected reserved word
(() => {
  for await (const v of obj) {
    // â€Ļ
  }
});

非同期ãĢ反垊可čƒŊãĒã‚Ēブジェクト

for-of では「反垊可čƒŊ」ãĒã‚Ēブジェクトが刊į”¨å¯čƒŊで、それは遊切ãĒ [Symbol.iterator]() ãƒĄã‚Ŋãƒƒãƒ‰ãŒč¨­åŽšã•ã‚ŒãĻいるもぎ、でした。

for-await-of で刊į”¨å¯čƒŊãĒã‚Ēブジェクトは「非同期反垊可čƒŊ」ãĒもぎで、それは遊切ãĒ [Symbol.asyncIterator]() ãƒĄã‚Ŋãƒƒãƒ‰ãŒč¨­åŽšã•ã‚ŒãĻいるもぎ、です。

for-of ぎときと同じく、そういうã‚Ēブジェクトをč‡ĒäŊœã™ã‚‹ã“とができぞす。

const sleep = (ms) => new Promise((f) => setTimeout(f, ms));

const obj = {
  async* [Symbol.asyncIterator] () {
    yield 11;
    await sleep(1000);
    yield 22;
    await sleep(1000);
    yield 33;
  },
};

(async () => {
  for await (const value of obj) {
    console.log(value);
  }
})();

1000ミãƒĒį§’æ­ĸぞりãĒがらįš°ã‚Ščŋ”す様子。

非同期ぎおさらい

čģŊく await ãŽãŠčŠąã‚’ã—ãžã™ã€‚įŸĨãŖãĻるäēēはéŖ›ã°ã—ãĻæŦĄã¸ã€‚

async とは

é–ĸ数を Promise 化するやつです。

// Promiseį‰ˆ
const f = () => {
  const promise = new Promise((resolve, reject) => {
    resolve(123);
  });
  return promise;
};
// asyncį‰ˆ
const f = async () => 123;

const p = f();
console.log(p instanceof Promise); // => true
p.then((result) => {
  console.log(result); // => 123
});

return すると resolve() 、 throw は reject() です。

äģŠå›žãŽäž‹ã ã¨éžåŒæœŸé–ĸ数ぎ中でäŊ•ã‚‚しãĻãĒã„ã‘ãŠã€ã‚‚ãĄã‚ã‚“æ™Žé€šã¯ Promise ãĒりぞた await ãĒりで非同期ãĢå‡Ļį†ã‚’しぞす。

await とは

Promise ぎ then() ぎäģŖわりです。

// promise-thenį‰ˆ
p.then((result) => {
  console.log(result);
});
// async-awaitį‰ˆ
const result = await p;
console.log(result);

イãƒŗデãƒŗãƒˆãŒæˇąããĒらãĒいところがį´ æ•ĩ。

async ãĒé–ĸ数内でぎãŋ刊į”¨å¯čƒŊです。 Chrome DevToolsぎã‚ŗãƒŗã‚ŊãƒŧãƒĢだと await 動くけお、あれはį‰šåˆĨ。 外だとエナãƒŧãĢ。

SyntaxError: await is only valid in async function

catch() ぎäģŖわりは構文ぎ斚ぎ try-catch です。

fetch() ぎ䞋

䞋えば、指厚ぎパ゚ぎHTMLを取垗しč§Ŗ析、 <title> ãĢč¨­åŽšã•ã‚ŒãĻいる文字列を垗るやつ。

const fetchTitle = (path) => fetch(path)
  .then((res) => res.text()) // text()はPromiseをčŋ”す
  .then((html) => html.match(/<title>(.*)<\/title>/i)[1]);

これ↑を、こう↓書けぞす。

const fetchTitle = async (path) => {
  const res = await fetch(path);
  const html = await res.text();
  return html.match(/<title>(.*)<\/title>/i)[1];
};

でもãŖãĻ async がäģ˜ã„ãĻる fetchTitle() は Promise ã‚Ēブジェクトをčŋ”すぎで、こうäŊŋいぞす。īŧˆã‚‚ãĄã‚ã‚“ã“ã„ã¤ã‚‰ã‚‚ await ã§ã‚‚č‰¯ã„ã€‚īŧ‰

// įžåœ¨ãƒšãƒŧジぎã‚ŋイトãƒĢを取垗
fetchTitle(location.href)
  .then((title) => console.log('Title:', title));

// トップペãƒŧジぎã‚ŋイトãƒĢを取垗
fetchTitle('/')
  .then((title) => console.log('Title:', title));

äŊŋい斚

čŠąã‚’æˆģしãĻ for-await-of は、非同期反垊子 (AsyncIterator) がčŋ”すįĩæžœã‚’ await しãĒがら反垊しぞす。

こんãĒ非同期反垊子をčŋ”すé–ĸ数があãŖたとしぞす。

async function* foo () {
  yield 11;
  await sleep(1000);
  yield 22;
  await sleep(1000);
  yield 33;
}

įš°ã‚Ščŋ”さãĒい䞋

ぞずはここから。

// 晎通ぎ反垊子
const it = foo();
const result = it.next();
console.log(result);
// 非同期反垊子
const ait = foo();
const result = await ait.next();
console.log(result);

晎通ぎ for でįš°ã‚Ščŋ”す䞋

next() å‘ŧãŗå‡ēすところで await しãĻぞすね。 for 文で同じようãĢしãĻ、値を非同期ãĢåž—ãĒがら反垊することができぞす。

const ait = foo();
for (let cur = await ait.next(); !cur.done; cur = await ait.next()) {
  const { value } = cur;
  console.log('for', value);
}

for-await-of で書く䞋

é•ˇã„ for 文ãĢãĒãŖãĻしぞãŖたけれお、大丈å¤Ģ、僕らãĢは for-await-of 構文がありぞす。

for await (const value of foo()) {
  console.log(value);
}

はいできあがり。

ついでãĢ、晎通ぎ for 文で晎通ãĢ await する䞋

ここぞでやãŖãĻきた for-await-of ぎį‰šåž´ã¯ååžŠå­ãŒéžåŒæœŸã€ã¨ã„うところãĒぎで、äŊ•ã§ã‚‚ãĒいところで非同期ãĢやるãĒら晎通ãĢ await するだけです。

for (const url of urls) {
  const res = await fetch(url);
  console.log(res);
}

可čƒŊãĒら Promise.all() を

ã¨ã„ã†ã‚ã‘ã§č‰˛ã€…æ›¸ã„ãĻきたんだけお、非同期ãĢįš°ã‚Ščŋ”ã™ãŽã¯č‰¯ããĒいです。

だãŖãĻせãŖかく非同期ãĢå‡Ļį†ã§ãã‚‹ãŽã ã‹ã‚‰ã€é †į•ĒではãĒく一åēĻãĢぞとめãĻåŽŸčĄŒã—ãĻ、そぎ垌ãĢįĩæžœã‚’é †ãĢå‡Ļį†ã—ãĻいく斚がéĢ˜åŠšįŽ‡ã§ã™ã€‚

䞋えば非同期å‡Ļį†ãŒã˛ã¨ã¤500msかかるとしãĻ、3つ順ãĢã‚„ã‚Œã°åˆč¨ˆ1500msぎ垅抟時間がåŋ…čĻãĢãĒりぞす。これらを Promise.all() でä¸Ļ列ãĢåŽŸčĄŒã—ãĻã‚„ã‚Œã°ã€åž…ãĄæ™‚é–“ã¯įĩåą€500msぎぞぞです。

į›´åˆ—åŽŸčĄŒã¯åŗへ、ä¸Ļåˆ—åŽŸčĄŒã¯ä¸‹ã¸åģļãŗã‚‹ã€‚åžŒč€…ãŽæ–šãŒæ¨Ēčģ¸īŧˆæ™‚é–“īŧ‰ã¯įŸ­ã„。

įš°ã‚Ščŋ”し中ぎ await をįĻã˜ã‚‹ESLintãŽč¨­åŽš

ESLintãĢã‚‚ãã†ã„ã†č¨­åŽšãŒã‚ã‚Šãžã™ã€‚

Performing an operation on each element of an iterable is a common task. However, performing an await as part of each operation is an indication that the program is not taking full advantage of the parallelization benefits of async/await.

Usually, the code should be refactored to create all the promises at once, then get access to the results using Promise.all(). Otherwise, each successive operation will not start until the previous one has completed.

反垊ぎ各čĻį´ ãĢ寞しãĻ操äŊœã‚’čĄŒã†ã“ã¨ã¯ä¸€čˆŦįš„ãĒäŊœæĨ­ã§ã™ã€‚しかしãĒがら、各æŽĩ階ぎ操äŊœã§ await ã‚’åŽŸčĄŒã™ã‚‹ã¨ã€ããŽãƒ—ãƒ­ã‚°ãƒŠãƒ ãŒ async/await ãĢよるä¸Ļ列化ぎ恊æĩを十分ãĢäēĢ受できãĒいことãĢãĒりぞす。

一čˆŦãĢこぎようãĒã‚ŗãƒŧドは、一åēĻãĢ全ãĻぎプロミ゚をäŊœæˆã—そぎうえで Promise.all() をį”¨ã„ãĻįĩæžœã‚’垗るようãƒĒãƒ•ã‚Ąã‚¯ã‚ŋãƒŧされるずきです。そうでãĒいとé€Ŗįļšįš„ãĒå‡Ļį† (successive operation) が前ぎå‡Ļį†ãŒåŽŒäē†ã™ã‚‹ãžã§å§‹ãžã‚Šãžã›ã‚“。

for-await-of をįĻã˜ã‚‹ESLintãŽč¨­åŽš

ただし前項ぎもぎは晎通ぎ for 内で await 書いたときãĢåŧ•ãŖかけるį”¨ã§ã€ for-await-of はåŧ•ãŖかからãĒいです。

for-await-of をīŧˆ for-of ã¯č¨ąåŽšã—ã¤ã¤īŧ‰åŧžãč¨­åŽšã¯ã“ã‚“ãĒ感じ↓です。

module.exports = {
  "rules": {
    'no-restricted-syntax': [
      'error',
      'ForOfStatement[await=true]',
    ],
  },
};

for-await-of はエナãƒŧ、 for-of はOKãĢãĒる。

晎通ぎ for 文で晎通ãĢ await しãĒい䞋

上ぎ斚で書いたįš°ã‚Ščŋ”しぎ途中で await する斚ぎ䞋を、 Promise.all() をäŊŋãŖãĻä¸Ļ列ãĢåŽŸčĄŒã—ãĻ、そぎ全äŊ“ã‚’ await で垅つようãĢした䞋です。

const responses = await Promise.all(
  urls.map((url) => {
    console.log(url);
    return fetch(url);
  })
);

for (const res of responses) {
  console.log(res);
}

č¤‡æ•°ãŽ fetch() が同時ãĢčĩ°ã‚‹ãŽã§ã€ã“ãŖãĄãŽæ–šãŒé€Ÿã„ã§ã™ã€‚

例

えãƒŧと、非同期ãĢįš°ã‚Ščŋ”す䞋かあâ€Ļâ€Ļ。

同意をäŋƒã™ãƒĄãƒƒã‚ģãƒŧジ

ごめん思いつかãĒかãŖた。

// 抜į˛‹

async function* waitForAgreeing (interval = 2000) {
  let index = 0;
  while (true) {
    if (elAgree.checked) {
      return;
    }

    await sleep(interval);
    yield messages[index];
    index = (index + 1) % messages.length;
  }
}

const main = async () => {
  for await (const message of waitForAgreeing()) {
    elMessage.textContent = message;
  }
  elMessage.textContent = 'さんきゅãƒŧ';
};

2į§’ごとãĢ同意をäŋƒã™æ–‡č¨€ãŒčĄ¨į¤ēされįļšã‘る䞋。
åŒæ„ã™ã‚‹ãžã§ãƒĄãƒƒã‚ģãƒŧジが更新されįļšã‘る。

そぎäģ–

for-of č‡ĒäŊ“ãŒã‚ãžã‚Šč‰¯ããĒいīŧŸ

for-await-of をåŧžãč¨­åŽšã‚’上ぎ斚で書いたけお、そもそも for-of をåŧžããšãīŧŸ

AirbnbぎESLintãŽč¨­åŽšã‚’ã‚ˆãäŊŋãŖãĻるんだけお、そこでは for-of ぎ刊į”¨č‡ĒäŊ“がįĻæ­ĸされãĻいぞす。īŧˆåˆĨ途 for-in もįĻæ­ĸ。īŧ‰

į†į”ąã¯č¨­åŽšãĢ書いãĻある。

iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.

イテãƒŦãƒŧã‚ŋãƒŧ、ジェネãƒŦãƒŧã‚ŋãƒŧは再į”ŸæˆãƒŠãƒŗã‚ŋイム (regenerator-runtime) がåŋ…čĻã§ã€ãã‚Œã¯æœŦã‚Ŧã‚¤ãƒ‰ãŽč¨ąåŽšį¯„å›˛ã‹ã‚‰ã™ã‚‹ã¨ã‚ãžã‚ŠãĢ重すぎぞす。ぞたそれとはåˆĨãĢ、ãƒĢãƒŧプぎ刊į”¨ã¯å›žéŋし、配列ぎ反垊を刊į”¨ã—ãĻください。

arr.forEach() をäŊŋえãŖãĻさ。 Object.keys() とかもだめãĒぎかãĒ。おれくらい遅いんだろ。

ãĄãĒãŋãĢ for と比ずãĻ forEach() が遅すぎるãŖãĻことはãĒいです。

おぞけ: for-await-in

for await (const key in obj) {
  console.log(key);
}

SyntaxError: Unexpected token in

そんãĒもぎはãĒい。

おしぞい

あんぞりäŊŋいおころがãĒあ。 for でやれることだしせãŖかくだから for-of でもできるようãĢしãĻおこう、とかそういう感じãĒぎかãĒあ。

ああでも配列äģĨ外ぎジェネãƒŦãƒŧã‚ŋãƒŧč‡ĒäŊ“ãŽč‰¯ã„äŊŋい道がぞだわかãŖãĻãĒいから、そこらčžēわかãŖたら非同期ãĢįš°ã‚Ščŋ”したくãĒるかも。

é–ĸé€Ŗ

å‚č€ƒ

for-ofで配列も晎通ぎã‚Ēブジェクトも反垊しよう。īŧˆé…åˆ—とかおれおれAdvent Calendar2018 – 15æ—Ĩį›Žīŧ‰

ã‚ĢテゴãƒĒãƒŧ: JavaScript

LINDORぎAdvent CalendarīŧˆæœŦį‰Šīŧ‰ãŽ15æ—Ĩį›Žã‚’開けたところ。
配列とかおれおれAdvent Calendar2018 – 15æ—Ĩį›Ž

įš°ã‚Ščŋ”しãĢもいろいろあるよ。

for-in じゃãĒい斚です。 こãŖãĄäŊŋおう。

for-of 文

for-in と違ãŖãĻ値ぎ斚を持ãŖãĻきãĻくれぞす。

const arr = [11, 22, 33];

for (const value of arr) {
  console.log(value);
}

// 11
// 22
// 33

for ãĢįŊŽãæ›ãˆ

反垊可čƒŊãĒã‚Ēãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’å¯žčąĄã¨ã™ã‚‹ for-of を晎通ぎ for 文ãĢįŊŽãæ›ãˆã‚‹ã“ともできぞす。

const arr = [11, 22, 33];

const it = arr.values();
for (let cur = it.next(); !cur.done; cur = it.next()) {
  const { value } = cur;
  console.log('for', value);
}

äģ•æ§˜

for-in, for-of, for-await-of は全部ぞとめãĻ厚įžŠã•ã‚ŒãĻる。

ぎで、 for-in ぎ斚もãŋといãĻください。

for-in は列挙可čƒŊãĒプロパテã‚Ŗ名を垗ぞしたが、 for-of ぎ斚では反垊子 (iterator) をį”¨ã„たįš°ã‚Ščŋ”ã—ã‚’čĄŒã„ãžã™ã€‚

刊į”¨å¯čƒŊãĒã‚Ēブジェクトと反垊子 (iterator)

for-of は「反垊可čƒŊ」ãĒã‚Ēブジェクトでぎãŋ刊į”¨å¯čƒŊです。

こぎ「反垊可čƒŊ iterable」であるとは、ぞあæœŦį¨ŋは for-of がä¸ģįœŧですぎでざãŖくりį”ŗし上げると、 [Symbol.iterator] ãƒĄã‚Ŋッドがそぎ 反垊子 iterator ã‚Ēブジェクトをį”Ÿæˆã™ã‚‹ã‚ˆã†éŠåˆ‡ãĢį”¨æ„ã•ã‚ŒãĻいるįŠļæ…‹ã‚’č¨€ã„ãžã™ã€‚

č‡ĒäŊœã‚‚できぞす。

const obj = {
  * [Symbol.iterator] () {
    yield 11;
    yield 22;
    yield 33;
  },
};

čŠŗしくはåˆĨį¨ŋīŧˆäēˆåŽšīŧ‰ã‚’ごčĻ§ãã ã•ã„。

で、配列は反垊可čƒŊãĒã‚ĒブジェクトãĒぎでäŊŋえぞす。

console.log(Symbol.iterator in arr); // => true

äģ–ãĢ Set 、 Map それから String ã‚Ēブジェクトも反垊可čƒŊです。 īŧˆæ–‡å­—列 "" č‡ĒäŊ“はだめ。ã‚ĒブジェクトでãĒã„ãŽã§ãƒĄã‚ŊッドもãĒい。å‘ŧãŗå‡ēせるけお。īŧ‰

æœĒ寞åŋœãŽã‚Ēブジェクトをįš°ã‚Ščŋ”す

配列でäŊŋう分ãĢはį°Ąå˜ã ãŖたけお、晎通ぎã‚ĒブジェクトはそぎぞぞではäŊŋãˆãžã›ã‚“ã€‚ã˛ã¨æ‰‹é–“åŋ…čĻã€‚

const obj = {};
for (const value of obj) {
}
// TypeError: obj is not iterable

æŦĄãŽä¸‰é€šã‚ŠãŽãƒĄã‚Ŋッドで、äŊ•ãŽå¤‰å“˛ã‚‚ãĒいただぎã‚Ēブジェクトから配列をį”Ÿæˆã—ぞす。 前項ぎ通り、配列ãĒら反垊可čƒŊ。

  • Object.values()
  • Object.keys()
  • Object.entries()

反垊可čƒŊでãĒい通常ぎã‚Ēブジェクトはこれら通しãĻ反垊可čƒŊãĒã‚Ēブジェクトを垗、それを for-of へ与えることができる、というįŽ—æŽĩです。

äģĨ下、こういうã‚Ēブジェクトがある前提でã‚ŗãƒŧド䞋を提į¤ēしぞす。

const obj = {
  foo: 11,
  bar: 22,
  boo: 33,
};

プロパテã‚Ŗ値をįš°ã‚Ščŋ”す

for (const value of Object.values(obj)) {
  console.log(value);
}

// 11
// 22
// 33

プロパテã‚Ŗ名をįš°ã‚Ščŋ”す

for (const key of Object.keys(obj)) {
  console.log(key);
}

// foo
// bar
// boo

名前と値をįš°ã‚Ščŋ”す

ä¸Ąæ–šã‹īŧīŧŸã€€key と value ãŽä¸Ąæ–šãģしいぎかīŧŸã€€ä¸Ąæ–šâ€Ļâ€Ļイヤしんãŧめīŧīŧ

はい、そんãĒæŦ˛åŧĩりさんぎためぎ抟čƒŊã‚‚ã‚ã‚Šãžã™ã€‚ãĄã‚‡ãŖとわかりãĨらいんだけお、æˆģり値としãĻそぎ2つだけをæ ŧį´ã—た配列ぎ配列をčŋ”しぞす。

for (const [key, value] of Object.entries(obj)) {
  console.log(key, ':', value);
}

// foo : 11
// bar : 22
// boo : 33

垗られる順åē

3つとも内部å‡Ļį†ã¯åŒã˜ã“れ↓で、垗られる順åēã¯äģĨ下ぎ通り。

  1. 整数イãƒŗデック゚昇順īŧˆã‚’文字列ãĢしたもぎīŧ‰
  2. 文字列キãƒŧčŋŊ加順
const obj = {};

obj[2] = '#1';
obj.z = '#2';
obj[Symbol(1)] = '#3';
obj[10] = '#4';
obj.a = '#5';

console.log(obj);
// => { '2': '#1', '10': '#4', z: '#2', a: '#5', [Symbol(1)]: '#3' }

for (const [key, value] of Object.entries(obj)) {
  console.log(key, ':', value);
}

// 2 : #1
// 10 : #4
// z : #2
// a : #5

äģ•æ§˜ã¯ã“ãĄã‚‰ã€‚

ãĄãĒãŋãĢ OrdinaryOwnPropertyKeys() ではキãƒŧが Symbol ぎもぎもčŋŊ加順ãĢ取垗しãĻいるぎだけれお、そぎ垌 EnumerableOwnPropertyNames() ぎå‡Ļį†ã§æ–‡å­—列でãĒいもぎは捨ãĻられぞす。

先ぎ䞋↑で console.log() ぎ斚では Symbol がキãƒŧãĢãĒãŖãĻるもぎも取れãĻいるぎは、そぎå‡Ļį†ãŒ OrdinaryOwnPropertyKeys() をäŊŋãŖãĻるからãĒんでしょう。įŸĨらんけお、ã‚ŗãƒŗã‚ŊãƒŧãƒĢぎäģ•æ§˜ã¯ã€‚

äģģ意ぎ順åēãĢ

したい場合、åˆĨ途 sort() しãĻやりぞす。

文字列順とか localeCompare() がäžŋ刊ãŖãŊい。 īŧˆãĄãĒãŋãĢ local ではãĒく locale です。īŧ‰

// プロパテã‚Ŗ名昇順
const it = Object.entries(obj)
  .sort(([key1], [key2]) => key1.localeCompare(key2));
for (const [key, value] of it) {
  console.log(key, ':', value);
}

// bar : 22
// boo : 33
// foo : 11

sort() ãĢ与えた比čŧƒé–ĸ数は原はįœį•Ĩ可čƒŊãĒんですが、įŊ ãŒã‚ãŖたりするぎで、常ãĢįœį•ĨしãĒã„ãŽãŒč‰¯ã„ã‚„ã‚Šæ–šã‹ã¨æ€ãŖãĻおりぞす。

おしぞい

åŧˇåŠ›ã€‚

é–ĸé€Ŗ

å‚č€ƒ

for-inぎäģ•æ§˜ã‚‚čĻ‹ãĻãŋたよ。äŊŋう抟äŧšãĒさそうだけお。īŧˆé…åˆ—とかおれおれAdvent Calendar2018 – 14æ—Ĩį›Žīŧ‰

ã‚ĢテゴãƒĒãƒŧ: JavaScript

LINDORぎAdvent CalendarīŧˆæœŦį‰Šīŧ‰ãŽ14æ—Ĩį›Žã‚’開けたところ。
配列とかおれおれAdvent Calendar2018 – 14æ—Ĩį›Ž

įš°ã‚Ščŋ”しãĢもいろいろあるよ。

for-in はįžäģŖではたãļんもうäŊŋう抟äŧšãĒいんじゃãĒいかãĒと思う。

for-in 文

列挙可čƒŊ (enumerable) ãĒã‚Ēブジェクトプロパテã‚Ŗをįš°ã‚Ščŋ”しぞす。

const arr = [11, 22, 33];
for (const index in arr) {
  const value = arr[index];
  console.log(index, value);
}
// 0 11
// 1 22
// 2 33

配列じゃãĒくãĻもäŊŋえぞす、というか配列じゃãĒいぎでäŊŋう場éĸぎ斚が多いかãĒと思いぞす。

const obj = { a: 11, b: 22, c: 33 };
for (const prop in obj) {
  const value = obj[prop];
  console.log(prop, value);
}
// a 11
// b 22
// c 33

index は文字列

プロパテã‚Ŗ名が文字列ãĒぎはわかりやすいと思うんだけお、配列ぎ場合でも数値 0, 1, 2, … ではãĒく、文字列で '0', '1', '2', … が垗られぞす。

äģ•æ§˜įš„ãĢも、配列ぎ各čĻį´ ã¯æ–‡å­—列をキãƒŧãĢæ ŧį´ã•ã‚ŒãĻいるし、æˇģえ字ã‚ĸクã‚ģ゚は文字列へ変換しãĻã‹ã‚‰čĄŒã‚ã‚Œãžã™ã€‚

ãŖãĻã„ã†čą†įŸĨč­˜ã§ã™ã€‚

äģ•æ§˜

for-in, for-of, for-await-of は全部ぞとめãĻ厚įžŠã•ã‚ŒãĻる。

in åˇĻ側

for と同様、äģĨ下ぎ三į¨ŽéĄžã€‚

  • ただぎåŧīŧˆåŽŖ言ãĒしで i = 0 とかīŧ‰
  • var åŽŖ言
  • let, const åŽŖ言

いãŖたん for ぎ斚をčĻ‹ãĻください。

for-in ぎ場合は for と違ãŖãĻįš°ã‚Ščŋ”し全äŊ“ぎãƒŦã‚­ã‚ˇã‚ĢãƒĢį’°åĸƒã¯į”Ÿæˆã•ã‚ŒãĒいãŖãŊい。

įš°ã‚Ščŋ”し毎回ぎãƒŦã‚­ã‚ˇã‚ĢãƒĢį’°åĸƒã¯ for と同様、毎回åŧ•ãįļ™ãŽãĒがらį”¨æ„ã•ã‚Œã‚‹æ§˜å­ã€‚ 変数ぎ値を書き換えることãĒく毎回åŧ•ãįļ™ã„で新たãĢäŊœã‚Šį›´ã™ãŸã‚ã€ const がäŊŋえぞす。

ただよくわからãĒいところがあãŖãĻ。

įš°ã‚Ščŋ”しぎ前ãĢ一åēĻ in ぎåˇĻ側ぎ変数をį™ģéŒ˛ã™ã‚‹ãƒŦã‚­ã‚ˇã‚ĢãƒĢį’°åĸƒã‚’äŊœãŖãĻるんだけお、į™ģ録įĩ‚わãŖたら元ぎãƒŦã‚­ã‚ˇã‚ĢãƒĢį’°åĸƒã¸æˆģしãĻぞす。ãĒんだろこれ。äē‹å‰ãĢ検č¨ŧしãĻる感じīŧŸ

というか TDZ ãĻäŊ•ãŽé ­æ–‡å­—īŧŸã€€”Tsunami Disaster Zone”īŧŸã€€äģ•æ§˜æ›¸ä¸­ã“こãĢしかå‡ēãĻこãĒいんだけお。

これ↓īŧŸã€€īŧˆ var と違ãŖãĻåˇģき上げãĒいよãŖãĻčŠąã€‚īŧ‰ã€€ã‚„ãŖãąã‚Šã€ŒæœŦį•Ēはぞだだけおäē‹å‰ãĢãĄã‚‡ãŖとã‚ĸãƒŦしとこうかãĒãƒŧ」ãŋたいãĒ感じīŧŸ

in åŗ側

įš°ã‚Ščŋ”し前ãĢčŠ•äžĄã—ãĻ、 for-in ぎ場合、įš°ã‚Ščŋ”しį”¨ãĢプロパテã‚Ŗ名を列挙しぞす。

å¯žčąĄã‚Ēブジェクトからプロトã‚ŋイプチェイãƒŗを順々ãĢčžŋãŖãĻゆき、全ãĻぎプロパテã‚Ŗを列挙する。ただし、同じ名前ぎもぎは一åēĻしかå‘ŧばれãĒい。という感じ。

もしJSで原čŖ…するãĒらこういう↓å‡Ļį†ã‚‰ã—いよ。

function* EnumerateObjectProperties(obj) {
  const visited = new Set();
  for (const key of Reflect.ownKeys(obj)) {
    if (typeof key === "symbol") continue;
    const desc = Reflect.getOwnPropertyDescriptor(obj, key);
    if (desc) {
      visited.add(key);
      if (desc.enumerable) yield key;
    }
  }
  const proto = Reflect.getPrototypeOf(obj);
  if (proto === null) return;
  for (const protoKey of EnumerateObjectProperties(proto)) {
    if (!visited.has(protoKey)) yield protoKey;
  }
}

列挙可čƒŊ性

こぎ for-in でå‡ēãĻくるかおうかを「列挙可čƒŊ性 (enumerability)」とå‘ŧんでいるようです。

Object.defineProperty() でäŊœã‚‹ã¨ããĢ enumerable: false ãĢすると、プロパテã‚Ŗはあるけお列挙されãĒくãĒりぞす。

const obj = { a: 11, b: 22 };
Object.defineProperty(obj, 'c', {
  enumerable: false, // ←これこれ
  value: 33,
});

console.log(obj.c); // => 33

for (const prop in obj) {
  const value = obj[prop];
  console.log(prop, value);
}
// a 11
// b 22

Object.getOwnPropertyDescriptor() をį”¨ã„ã‚‹ã¨ã€ãŠã†ã„ã†č¨­åŽšãĢãĒãŖãĻいるぎかčĒŋずられぞす。

â€Ļ

const descriptorA = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptorA);
// { value: 11,
//   writable: true,
//   enumerable: true,
//   configurable: true }

const descriptorC = Object.getOwnPropertyDescriptor(obj, 'c');
console.log(descriptorC);
// { value: 33,
//   writable: false,
//   enumerable: false,
//   configurable: false }

ãĄãĒãŋãĢgetter/setterã‚‚ã“ã„ã¤ã‚‰ã§æ‰ąãˆãžã™ã€‚

Own propertyå•éĄŒ

クナ゚が導å…ĨされるäģĨ前、ES 5時äģŖぎJavaScriptではé–ĸ数をäŊŋãŖãĻã‚ŗãƒŗ゚トナクã‚ŋãƒŧをį”¨æ„ã—ãĻいぞした。そぎ場合 for-in ã§ãĄã‚‡ãŖと困ãŖたことがありぞす。

prototype ãĢč¨­åŽšã—ãŸã‚‚ãŽãžã§å‡ēãĻããĄã‚ƒã†ã‚“ã§ã™ã€‚

function MyClass (options) {
  this.name = options.name;
}
MyClass.prototype.sayHello = function () {
  console.log('Hi my name is ' + this.name + '!');
};

const obj = new MyClass({ name: 'Alice' });
obj.sayHello(); // Hi my name is Alice!

obj.boo = 33;
for (const prop in obj) {
  const value = obj[prop];
  console.log(prop, ':', value);
}
// name : Alice
// boo : 33
// sayHello : function () {
//   console.log('Hi my name is ' + this.name + '!');
// }

prototype へ指厚しãĻいる sayHello がå‡ēãĻぞすね。これはåŦ‰ã—くãĒい。

そぎイãƒŗã‚šã‚ŋãƒŗ゚がč‡Ē分で持ãŖãĻいるプロパテã‚ŖãĒぎか、それとも prototype īŧˆã‚„そぎäģ–īŧ‰ã‹ã‚‰æĨãĻいるぎかを判断するåŋ…čĻãŒã‚りぞす。

č§Ŗæąē

hasOwnProperty() をäŊŋいぞす。

これはã‚Ēブジェクトč‡ĒčēĢが指厚ぎプロパテã‚Ŗを持ãŖãĻいれば true をčŋ”すもぎです。 obj.hasOwnProperty('sayHello') は false ãĢãĒりぞす。

// ...

for (const prop in obj) {
  // prototypeãĢč¨­åŽšã•ã‚ŒãŸã‚‚ãŽį­‰ã¯į„ĄčĻ–
  if (!obj.hasOwnProperty(prop)) {
    continue;
  }

  const value = obj[prop];
  console.log(prop, ':', value);
}

äģŠãĒら class で大丈å¤Ģ

です。うん、こãŖãĄãĢしよ。

class MyClass {
  constructor (options) {
    this.name = options.name;
  }

  sayHello () {
    console.log(`Hi my name is ${this.name}!`);
  }
}

const obj = new MyClass({ name: 'Alice' });
obj.boo = 123;

for (const prop in obj) {
  const value = obj[prop];
  console.log(prop, ':', value);
}
// name : Alice
// boo : 33

const desc = Object.getOwnPropertyDescriptor(MyClass.prototype, 'sayHello');
console.log(desc.enumerable); // => false

įš°ã‚Ščŋ”し中ぎ操äŊœ

削除された場合

å‘ŧばれずį„ĄčĻ–されぞす。

A property that is deleted before it is processed by the iterator’s next method is ignored.

反垊子 (iterator) ぎ next ãƒĄã‚ŊッドãĢよりå‡Ļį†ã•ã‚Œã‚‹å‰ãĢ削除されたプロパテã‚Ŗはį„ĄčĻ–される。

čŋŊ加された場合

えãƒŧと「äŋč¨ŧされãĒい not guaranteed」ãĻぎは、æœĒ厚įžŠãŖãĻã“ã¨ã§č‰¯ã„ã‹ãĒ。

If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration.

新しいプロパテã‚Ŗが列挙 (enumeration) ãŽé€”ä¸­ã§å¯žčąĄã‚ĒブジェクトへčŋŊ加された場合、新しくčŋŊ加されたプロパテã‚ŖãŒåŽŸčĄŒä¸­ãŽ (active) 列挙内でå‡Ļį†ã•ã‚Œã‚‹ã“とはäŋč¨ŧされãĒい。

こぎã‚ŗãƒŧド↓でčŠĻしたところ、įš°ã‚Ščŋ”し中ãĢはå‘ŧばれぞせんでした。īŧˆæ‰‹å…ƒãŽChrome, Firefox, EdgeでįĸēčĒã€‚īŧ‰ã€€ã‚‚ãĄã‚ã‚“įš°ã‚Ščŋ”しぎ垌でįĸēčĒã—たらčŋŊ加されãĻいる。

そぎäģ–

in ぎåŗ側はゆるい

null とかでもエナãƒŧãĢãĒらãĒい。

for (const i in null) ;

for-of ではエナãƒŧです。

順不同

for-in で垗られるプロパテã‚Ŗ名ぎ順åēã¯æœĒ厚įžŠ (not specified) です。

The mechanics and order of enumerating the properties is not specified

おしぞい

å‚č€ƒ