# JavaScript 再入門 #5 ###### tags: `JavaScript` - 参考: [【JS】初級者から中級者になるためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/) ## ループ文 ```javascript for (let i = 0; i < 10; i++) { console.log(i); } ``` ```javascript let i = 0; while (i < 10) { consle.log("hello"); i++; } ``` ## 演算子と優先順位 - **演算子** とは値(operand)をもとに処理(評価)を行い、結果を返す記号のこと - 各演算子では必ず評価後に **結果を返す** というのがポイントである - 演算子には優先順位があり、複数の演算子があると処理の順序が変わってくる - [演算子の優先順位 \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) ```javascript let a = 1 + 2; console.log(a); //-> 3 let b = 1 + 2 * 3; console.log(b); //-> 7 let c = (1 + 2) * 3; console.log(c); //-> 9 let d = 0; let e = d++; // 内部的には以下のような処理となる // let e; // d = (e = d) + 1; console.log(d, e); //-> 1, 0 let f = 0; let g = ++f; // 内部的には以下のような処理となる // let g; // f = g = f + 1; console.log(f, g); //-> 1, 1 ``` ## ループ文とブロックスコープ ```javascript // const は本来再代入できない // …はずだが以下の処理ではエラーは起きない for (let i = 0; i < 5; i++) { const j = i * 2; console.log(j); } //-> 0 //-> 2 //-> 4 //-> 6 //-> 8 ``` ```javascript // 今度は var を使ってみる for (let i = 0; i < 5; i++) { var j = i * 2; console.log(j); } //-> 8, 8, 8, 8, 8(Chrome の console 上では重ねて表示される) ``` - 以上から以下のことがわかる - `const` はループ処理のブロックスコープ内では **それぞれ別の定数** として出力されている - `var` はブロックスコープに縛られないため、同じ変数の最終的な評価値が 5 回表示されることになる ## 配列とループ文 ```javascript const arr = [1, 2, 3, 4, 5]; // for でループ for (let i = 0; i < arr.length; i++) { console.loh(arr[i]); } // while でループ let v, i = 0; while (v = arr[i++]) { console.log(v); } // ただし、while でループ処理を行うとき // 条件の評価中に falsy な値が出てくるとそこで処理が止まってしまうので注意 ``` ## `for...in` と列挙可能性 - `for...in` は列挙可能プロパティーに対して順不同で反復処理を実行するもの - プロトタイプチェーン内も列挙対象となる - ただし、`Symbol` で定義したプロパティーは列挙対象にはならない ```javascript! const s = Symbol(); const obj = { prop1: "value1", prop2: "value2", prop3: "value3", [s]: "value4" } Object.prototype.method = function(){} for (let key in obj) { console.loh(key, obj[key]); } //-> prop1 value1 //-> prop2 value2 //-> prop3 value3 ``` ## `for...in` と反復可能性 - `for...in` はイテレーターを持つオブジェクトに対して反復処理を実行するもの - **イテレーター** とは反復操作を行うときに用いるオブジェクトのこと - 例: `String`, `Array`, `Map`, `Set`, `arguments` ...など ```javascript! const arr = ["a", "b", "c"]; arr[4] = "e"; Object.prototype.method = function(){}; Object.defineProperty(arr, 0, { enumerable: false // ここで配列の列挙可能性を変更しても下記の出力には影響がない }); for (let v of arr) { console.log(v); } //-> a //-> b //-> c //-> undefined //-> e ``` ## `Map` と `Set` - ES 6 で導入されたもので、データを管理する構造のこと。**コレクション** とも呼ばれる - `Object` と `Map` の違い - `key` - `Object` は文字列のみ - `Map` は制約なし。プリミティブ値の数値や関数も取得可能 - `for...in` - `Object` に使える - `Map` には使えない - `for...of` - `Object` には使えない - `Map` に使える - `Array` と `Set` の違い - 重複値 - `Array` は内包可 - `Set` は内包不可 - `for...in` - `Array` に使える - `Set` には使えない - `for...of` - `Array` に使える - `Set` に使える ```javascript! const map = new Map(); const key1 = {}; const key2 = function() {}; let key3 = 0; map.set(key1, "value1"); console.log(map.get(key1)); //-> value1 console.log(map.get({})); //-> undefined map.set(key2, "value2"); console.log(map.get(key2)); //-> value2 map.set(key3, "value3"); console.log(map.get(key3)); //-> value3 console.log(map.get(0)); //-> value3 map.delete(key3); console.log(map.get(0)) //-> undefined for (const m of map) { console.log(m); //-> (2) [{}, "value1"] //-> (2) [f () {}, "value2"] } for (const [k. v] of map) { console.log(k, v); //-> {}, "value1" //-> f () {}, "value2" } const s = new Set(); s.add(key1); s.add(key1); // 同じものをもう一度格納してみる s.add(key2); s.add(key3); s.delet(key3); console.log(s.has(key3)); //-> false const arr = Array.from(s); console.log(arr); //-> [{}, f() {}] for (let k of s) { console.log(k); //-> {} //-> f () {} } ``` ## イテレーター - 反復操作を行うときに使うオブジェクトで、一定の記述ルールを持つ ```javascript! // イテレーターの構造 function genIterator() { // この層以下がイテレーター return { next: function() { // 必ず next メソッドが必要 return { // オブジェクトを返す done: true / false, // ループの継続可否 value: "値" // 返却する値 } } } } ``` ```javascript! function genIterator(max = 10) { let i = 0; return { next: function() { if (i >= max) { retrun { done: true } } else { return { done: false, value: i++ } } } } } const it = genIterator(10); let a = it.next(); while (!a.done) { console.log(a.value); a = it.next(); } //-> 0 //-> 1 //-> 2 //-> 3 //-> 4 //-> 5 //-> 6 //-> 7 //-> 8 //-> 9 const obj = { [Symbol.iterator]: genIterator.bind(null, 10); } for (const i of obj) { console.log(i); } //-> 0 //-> 1 //-> 2 //-> 3 //-> 4 //-> 5 //-> 6 //-> 7 //-> 8 //-> 9 ``` ## イテレーターを用いた反復可能オブジェクトの実装 ```javascript! const items = { prop1: "value1", prop2: "value2", prop3: "value3" } Object.prototype[Symbol.iterator] = function() { const keys = Object.keys(this); let i = 0, _this = this; return { next() { let key = kets[i++]; return { value: [key, _this[key]], done: i > keys.length } } } }; for (let [k, v] of items) { consle.log(k, v); } //-> prop1 value1 //-> prop2 value2 //-> prop3 value3 ``` ## ジェネレーター - イテレーターを生成するための特殊な関数のこと - イテレーターを素で書くよりも簡潔に記述できる ```javascript! // ジェネレーターの構造 function* gen() { if () { yield "値"; // done が false のとき } else { return "値"; // done が true のとき } } ``` ```javascript! function* genIterator(max = 10) { let i = 0; while (i < max) { yield i++; } return; } let a = it.next(); while (!a.done) { console.log(a.value); a = it.next(); } //-> 0 //-> 1 //-> 2 //-> 3 //-> 4 //-> 5 //-> 6 //-> 7 //-> 8 //-> 9 const obj = { [Symbol.iterator]: genIterator } for (const i of obj) { console.log(i); } //-> 0 //-> 1 //-> 2 //-> 3 //-> 4 //-> 5 //-> 6 //-> 7 //-> 8 //-> 9 ``` ## ジェネレーターを用いた反復可能オブジェクトの実装 ```javascript! const items = { prop1: "value1", prop2: "value2", prop3: "value3", }; Object.prototype[Symbol.iterator] = function*() { for (let key in this) { yield [key, this[key]]; } } for (let [k, v] of items) { consle.log(k, v); } //-> prop1 value1 //-> prop2 value2 //-> prop3 value3 ``` ## イテレーターとスプレッド演算子 - **スプレッド演算子** は反復可能もしくは列挙可能なオブジェクトを展開する演算子のこと - `{}` や `[]` の内部に記述して使用する - 関数の仮引数で使用される場合には **残余引数(レストパラメーター)** とも呼ばれ、実引数が渡されたとき配列に格納されて関数内で使用できるようになる - **スプレッド演算子の挙動はイテレーターの操作に従う** ```javascript! const arr1 = [1, 2, 3, 4, 5]; const arr2 = [...arr1]; arr2.push(6); console.log(arr2); //-> [1, 2, 3, 4, 5] console.log(arr2); //-> [1, 2, 3, 4, 5, 6] console.log(arr1 === arr2); //-> false function sum(...args) { let ret = 0; for (let v of args) { ret += v; } return ret; } const result = sum(1, 2, 3, 4); console.log(result); //-> 10 const obj = { prop1: "value1", prop2: "value2", prop3: "value3" } // このままだと配列への格納時に // object is not iterable (can not read property Symbol(Symbol.iterator)) // というエラーが起きるので以下の記述を追加してみる Object.prototype[Symbol.iterator] = function*() { for (let key in this) { yield [key, this[key]]; } } const arr3 = [...obj]; console.log(arr3); //-> [array(2), array(2), array(2)] // ["prop1", "value1"] // ["prop2", "value2"] // ["prop3", "value3"] const arr4 = {...obj}; console.log(arr4); //-> { // prop1: "value1", // prop2: "value2", // prop3: "value3" // } ```