【もりけん塾】JavaScript課題4で学んだことまとめ

JavaScript

今日は、もりけん塾のJavaScript課題4 を終えて学んだことをアウトプットしていきます。

こんにちは。Webコーダーのはるです。

現在、所属している「もりけん塾」でハンズオン課題 に取り組み、レビューをいただいています。

今日は、課題4 についてアウトプットをしていきます。
(前回までのアウトプットは、こちらです。)

●課題4で学んだこと

・while文
・テンプレートリテラル
・AddEventListenerが不要な理由
・for of, forEachの書き方
・for, whileのパフォーマンス

【もりけん塾】JavaScript課題4で学んだこと まとめ

今回、取り組んだ課題はこちらです。


(もりけん先生のJavaScriptハンズオン課題より)

while文

今回の課題は、指定のオブジェクトリテラルを使用してDOMを作成するというものでした。

しかし、前回の課題3でオブジェクトリテラルを既に使用してしまっていたため、同じことしても勉強にならないよなぁ。。と思い、for文をWhile文で書き直してみることにしました。

最初に書いたコード

'use strict';

document.addEventListener('DOMContentLoaded', function() {
 const attributes = [
  {to: "bookmark.html", img: "1.png", alt:"画像1", text: "ブックマーク"},
  {to: "message.html", img: "2.png", alt:"画像2", text: "メッセージ"}
 ]

 const ul = document.getElementById('js-lists');
 const arrayLength = attributes.length;

 const fragment = document.createDocumentFragment();

 let i = 0;
 while (i < arrayLength) {
  const attribute = attributes[i];
  const li = document.createElement('li');
  const anchor = document.createElement('a');
  const img = document.createElement('img');

  anchor.textContent = attribute.text;
  anchor.href = '/' + attribute.to;
  img.src = attribute.img;
  img.alt = attribute.alt;

  li.appendChild(anchor).insertAdjacentElement('afterbegin',img);
  fragment.appendChild(li);

  i++;
 }

 ul.appendChild(fragment);
});

これでPRをしましたが、while文の書き方はOKだったようです。

/* for文(i < 10 の時) */

for (let i = 0, i < 10, i++) {
  実行する文
}


/* while文(i < 10 の時) */

let = 0;

while (i < 10) {
 実行する文
 i++;
}

ちなみに、while文は小さな配列の場合パフォーマンスがforに劣りますし(後述)、繰り返しの回数が決められていない時によく使われるものだと知りました。勉強のためにwhile文を使用しましたが、この課題ではwhile文は適していないと思います。

また、もりけん先生にレビューをしていただき、テンプレートリテラルの書き方・addEventListener(‘DOMContentLoaded‘)は不要であることを教えていただきました。次にまとめます。

テンプレートリテラル

最初に書いたコードです。

anchor.href = '/' + attribute.to;

テンプレートリテラルを使用すると、以下のように書くことができると教わりました。

anchor.href = `/${attribute.to}`;

MDNで学習しました。

  • テンプレートリテラルは、文字列を“( バッククォート)で囲むことで使用可能
  • 複数行の文字列を簡単に書ける
 console.log(`haru
log`);
//haru
//log
  • 関数や式の挿入が簡単にできる
 `${関数や式}`

 ${ }の前後にテキストがそのまま入れられるので、テキストと関数を一緒に使用しても読みやすいです。

addEventListenerは不要

今回のケースでは(課題3,4)、addEventLister(‘DOMContentLoaded‘)は不要とのレビューもいただきました。

DOM挿入先のHTMLに<ul>が既に存在しているからです。

/* HTML */

<body>
 <div>
  <ul id="js-lists"></ul> //既に存在
 </div>
 <script src="index.js"></script> //この後でscriptが呼び出される
</body>
元々私がaddEventLister(‘DOMContentLoaded‘)を使用した理由は、getElementByIdを使用した時に、対象のidがまだ読み込まれておらずエラーとなるのを避けるためでした。

今回のケースだと、index.jsが呼び出されて→getElementByIdが実行される時には既に<ul>が存在しているのです。

getElementByIdを使用する時には、何でもかんでもaddEventLister(‘DOMContentLoaded‘)をつければ良いわけではないと学びました。

for of, forEachの書き方

課題3で使用したfor文をWhile文で書き直したのですが、もりけん先生から他にもfor ofやforEachで書く方法もあるよと教わりました。(課題4のケースだと、forEachがよく使われるみたい)

調べて書いてみたいと思います。

もし、間違えていたらご指摘いただけると嬉しいです!→(お問い合わせ

for of

/* 構文 */
for ( 変数 of 配列 ) { //繰り返す処理 }

書き直したコード

const attributes = [
 { to: "bookmark.html", img: "1.png", alt: "画像1", text: "ブックマーク" },
 { to: "message.html", img: "2.png", alt: "画像2", text: "メッセージ" }
];

const ul = document.getElementById("js-lists");

const fragment = document.createDocumentFragment();

for (let attribute of attributes) {
 const li = document.createElement("li");
 const anchor = document.createElement("a");
 const img = document.createElement("img");

 anchor.textContent = attribute.text;
 anchor.href = `/${attribute.to}`;
 img.src = attribute.img;
 img.alt = attribute.alt;

 li.appendChild(anchor).insertAdjacentElement("afterbegin", img);
 fragment.appendChild(li);
}

ul.appendChild(fragment);

CodeSandbox…ちゃんと動作はしている。

//

調べてみると、for ofはイテレーターというイテラル・オブジェクトが存在する配列にのみ使えるのだそうです。

試しに今回の配列attributesをconsole.logで表示してみると、

__proto__というプロパティに、Symbol.iteratorが存在していました。

これが存在していることで値を順番に取り出すことができると知りました。

forEach

/* 構文 */

配列名.forEach( コールバック関数 ){
  //繰り返す処理
}

構文を調べて、コールバック関数?!となったので調べてみると、”他の関数に引数として渡される関数”と書かれたものが多かった。

コールバック関数の引数として設定した変数に、配列の値が一つずつ代入されていくらしい。

コールバック関数に関しては追々もっと深くやらないとダメだと思いました。理解が曖昧。

 

書き直したコード

const attributes = [
 { to: "bookmark.html", img: "1.png", alt: "画像1", text: "ブックマーク" },
 { to: "message.html", img: "2.png", alt: "画像2", text: "メッセージ" }
];

const ul = document.getElementById("js-lists");

const fragment = document.createDocumentFragment();

attributes.forEach((attribute) => {
 const li = document.createElement("li");
 const anchor = document.createElement("a");
 const img = document.createElement("img");

 anchor.textContent = attribute.text;
 anchor.href = `/${attribute.to}`;
 img.src = attribute.img;
 img.alt = attribute.alt;

 li.appendChild(anchor).insertAdjacentElement("afterbegin", img);
 fragment.appendChild(li);
});

ul.appendChild(fragment);

コールバック関数に引数attributeを設定して、そこに一つずつ配列の値が入るようにしました。

ただのfor文に比べると、iの記述とか、配列の数を取得しなくていいので、コードがすごくスッキリした!

CodeSandbox…一応動作しています。

for,whileそれぞれのパフォーマンス

最後に、課題を行う上でパフォーマンスについて調べたのでメモします。

参考記事を貼ります。

こちらのパフォーマンステストによると、

  • whileは、大規模な配列(要素数100万くらい)では最速
  • for…ofは、小さなデータセットでは最速
  • forEach,for of,forは、大きなデータセットではスケールダウンしてしまう
  • forEachの実行時間は、各反復の中で起こることに大きく影響される

と書かれていました。

while以外は、小さな配列だとそこまで大差はなく高速だということがわかりました。

//

学習に使用している本は、もりけん先生推奨の”JavaScript本格入門”です。

あとがき

今回お忙しい時間を割いてレビューをしてくださったもりけん先生、ありがとうございました!

getElementByIdを使用する時には、何でもかんでもaddEventLister(‘DOMContentLoaded‘)をつければ良いわけではないと学びました。

このとき、うわ・・・自分頭悪いと思いました(笑

日常生活でも、お仕事でコーディングしている時も、自分って考えが浅いなぁと思うことの連続です。

こういうところ矯正していきたい。人生の課題です。

・・・

ちなみに、私が書いたコード(レビュー後)はこちらから確認できます。

今日は以上です。

//

【もりけん塾で勉強しています】

もりけん先生(@terrace_tec)のHPはこちら