Code Journey

30代未経験からプログラミング挑戦中(追うものは追われる者に勝る)

【初学者用まとめ(イベント処理編)】まず覚えるべきJavaScriptのコードの書き方(自習記録)

はじめに

前提

まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。

現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。

hirano-vm4.hatenablog.com

筆者は執筆時点で、RubyRuby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。

詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。

記事の本体は以下にになります。長くなってしまったので別記事として書いているものとなります。ご注意ください。

hirano-vm4.hatenablog.com

イベント処理

  • JavaScriptを使うとWebページ上でイベントが発生したときに実行する処理や関数を実行させることができる。イベントが発生した時に実行する処理や関数のことをイベントハンドラと呼ぶ

  • イベントは Web ページ上でマウスをクリックしたりキーボードのキーを押したときや、表示しようとしている Web ページの読み込みが完了した場合などに発生

HTML要素の属性としてイベントハンドラを登録

input 要素でマウスでクリックしたときに実行するイベントハンドラを登録する場合

<input type="button" value="button" onclick="イベントハンドラ">
  • onclick 属性に対する値としてイベントハンドラを記述(JavaScript のコードや関数を記述)
  • 複数の文を記述することもできる(最後の文以外には末尾にセミコロン(;)を記述)
  <body>
    <p>ボタンをクリック</p>

    <input type="button" value="button" onclick="alert('こんな感じ')" />
  </body>

イベントの種類の一覧

属性 イベントの種類
onclick マウスのボタンが要素の上でクリックされたとき
oncontextmenu マウスが右クリックされたとき
ondblclick マウスのボタンが要素の上でダブルクリックされたとき
onmousedown マウスが要素の上で押されているとき
onmouseup マウスのボタンが要素の上で離されたとき
onmousemove マウスが要素の上を移動しているとき
onmouseover マウスがリスナーが接続されている要素または子要素に移動したとき
onmouseout マウスがリスナーが接続されている要素または子要素から外れたとき
onselect テキストが選択されているとき
onwheel マウスホイールが回転しているとき
onauxclick マウスのメイン以外のボタン(中央のボタンなど)が要素の上でクリックされたとき
onkeydown いずれかのキーが押されたとき
onkeyup いずれかのキーが離されたとき
onkeypress いずれかのキーが押された状態にあるとき
onblur 要素がフォーカスを失ったとき(バブリングなし)
onfocus 要素がフォーカスを受け取ったとき(バブリングなし)
onchange 要素の値の変更が確定したとき(<input>, <select>, <textarea>)
oninput 要素の値が変更したとき(<input>, <select>, <textarea>)
onsubmit サブミットボタンが押されたとき
onreset リセットボタンが押されたとき
onload ページ全体がスタイルシートや画像などのすべて読み込まれたとき
onabort エラー以外の原因で読み込みが中止されたとき
onerror エラーが発生して読み込みが失敗したとき

バブリング・キャプチャリングとは?

ウェブページ上にユーザーが何かイベントをトリガーしたとき(例:ボタンをクリック・要素にマウスを載せるなど)、そのイベントはHTML要素のツリー構造をたどって(例:イベントが発生した要素の親要素である div 要素から、 body 要素、 html 要素、 Document オブジェクト、 Window オブジェクトまで DOM ツリーを上へ向かって順番に click イベントが発生)下から上へ伝播することを「バブリング」と呼ぶ。

  • イベントは通常、ユーザーの操作が行われた要素から親要素、さらに祖父要素へと伝播する
  • 親要素から子要素への逆伝播はキャプチャリングという
  • イベントは要素の境界を超え、親要素にも伝播する。親要素でイベントハンドラを設定されている場合、そのハンドラも実行される
  • バブリングはイベントハンドリングの一貫性を提供する。親要素で1つのイベントハンドラを設定することで、子要素すべてに対して同じ処理を行えて、コードの効率性と保守性に役立つ

DOMで取得した要素のプロパティにイベントハンドラを登録

要素オブジェクトのプロパティにイベントハンドラを登録

これまでの例ではHTMLの中でイベントハンドラを設定していたが、DOMを使用して任意の要素オブジェクトとして取得。その要素オブジェクトに対してイベントハンドラを設定できる。

  <body>
    <input type="button" value="button" id="test" />

    <script>
      const buttonClick = () => {
        alert("これを表示させる");
      };

      let button = document.getElementById("test");
      button.onclick = buttonClick;
    </script>
  </body>

  • HTML コードで記述された input タグには JavaScript のコードが含まれなくなり、イベントの処理は JavaScript のコードの中で完結する
イベントハンドラにコールバック関数を設定する
  • プロパティにイベントハンドラを設定する場合は、イベントが発生した時に呼び出されるコールバック関数を設定
  • click イベントに対してコールバック関数の buttonClick 関数を設定している
  • コールバック関数を設定するときには、関数名だけを記述する
    <script>
      const buttonClick = () => {
        let day = new Date();
        alert(day);
      };
      let button = document.getElementById("test");
      button.onclick = buttonClick;
    </script>
  </body>
イベントハンドラを無名関数やアロー関数を使って記述

イベントハンドラとして登録するコールバック関数は、無名関数を使って記述することもできる。

    <script>
      let button = document.getElementById("test");
      button.onclick = function () {
        let day = new Date();
        alert(day);
      };
    </script>
イベントハンドラを解除する
button.onclick = null;
    <script>
      let button = document.getElementById("test");
      button.onclick = function () {
        let day = new Date();
        alert(day);

        // 1度呼び出したら解除する
        this.onclick = null;
      };
    </script>

こうすると1度アラートが表示され、二回目以降は何も起こらなくなる。

※無名関数の場合、関数内で this を参照するとイベントが発生した要素が格納されている

コールバック関数が呼び出される時にイベントの情報を受け取る
  • イベントが発生してコールバック関数が呼ばれるときに発生したイベントに関する情報が格納された Event オブジェクトが引数として渡されてくる
  • イベントに関する情報を関数内で利用する場合は、コールバック関数で引数を記述する
<script>
    function butotnClick(event){
        // 何かしらの処理
    }

    let button = document.getElementById('test');
    button.onclick = butotnClick;
</script>

使用例

  <body>
    <p>ボタンをクリックしてください。</p>

    <input type="button" value="button" id="test" />

    <script>
      let button = document.getElementById("test");
      button.onclick = function (event) {
        console.log("eventのタイプ " + event.type);
        console.log("eventのターゲット " + event.target);
      };
    </script>
  </body>

// eventのタイプ click
// eventのターゲット [object HTMLInputElement]

addEventListenerメソッドを使ってイベントリスナーを登録

  • addEventListener メソッドを使用してイベントに対して登録した関数のことを特にイベントリスナーと呼ぶ
target.addEventListener(type, callback [, options])
  • 第一引数にはイベントの種類を表す文字列を指定(clickなどなど)
  • 第二引数にはイベントハンドラとしてコールバック関数を指定
  • 第さん引数にはオプション
<input type="button" value="button" id="test">

<script>
    function butotnClick(){
        console.log('お買い上げありがとうございます');
    }

    let button = document.getElementById('test');
    button.addEventListener('click', butotnClick);
</script>

idテストのボタンをユーザーがクリックしたあら、コールバック関数であるbutotnClickが呼び出されます。

  • 無名関数を使う場合は
<input type="button" value="button" id="test" />

<script>
  let button = document.getElementById("test");
  button.addEventListener("click", function () {
    console.log("テストします");
  });
</script>
  • アロー関数を使う場合
<input type="button" value="button" id="test" />

<script>
  let button = document.getElementById("test");
  button.addEventListener("click", () => {
    console.log("テストします");
  });
</script>
同じターゲットの同じイベントに対して複数のイベントリスナーを登録

HTML の属性値や DOM で取得した要素のプロパティに対してイベントハンドラを登録した場合、属性値やプロパティは一つの値しか保管できないため、同じイベントに対するイベントハンドラは一つしか登録することができない。

しかし、 addEventListener メソッドを使ってイベントリスナーを登録する場合には、同じターゲットの同じイベントに対して複数のイベントリスナーを登録することができる

ベントが発生すると登録されている順番に複数のイベントリスナーが呼び出される。

<input type="button" value="button" id="test" />

<script>
  function dispGreeting() {
    console.log("こんにちは");
  }

  function dispGoodNight() {
    console.log("こんばんは");
  }

  let button = document.getElementById("test");
  button.addEventListener("click", dispGreeting);
  button.addEventListener("click", dispGoodNight);
</script>

// こんにちは
// こんばんは
イベントリスナーを解除
  • removeEventListener メソッドは指定のイベントが発生したときに実行されるイベントリスナーを登録する
target.removeEventListener(type, callback [, options])
  • 削除するイベントリスナーを登録したときと同じ引数を指定する
  • 一致するイベントリスナーを削除
<input type="button" value="button" id="test" />

<script>
  function buttonClick() {
    console.log("こんにちは!これで最後です。");
    this.removeEventListener("click", buttonClick);
  }

  let button = document.getElementById("test");
  button.addEventListener("click", buttonClick);
</script>

一度だけイベントリスナーが呼びだされると、かんすないでイベントリスナーが解除される。

  • 無名関数を利用している場合第二引数に実行しちる関数を参照することができるarguments.calleeを指定する

発生したイベントの情報を取得する(Event)

イベントが発生して登録されたイベントハンドラやイベントリスナーが呼び出されると、一番目の引数に発生したイベントの情報が格納された Event オブジェクトが渡されてくる。

Eventオブジェクトの受け取り

ターゲットとなる要素のプロパティに対してイベントハンドラを登録する場合と、 addEventListener メソッドを使ってイベントリスナーを登録した場合、イベントが発生すると登録したコールバック関数が呼び出され、その時、第一引数に発生したイベントの情報が格納されたEventオブジェクトが渡される。

関数内でEventオブジェクトの情報を使いたい場合に活用できる。

<script>
    function butotnClick(event){
        console.log('こんにちは');
    }

    let button = document.getElementById('test');
    button.addEventListener('click', butotnClick);
</script>
Eventオブジェクトで取得できる情報

コールバック関数が呼び出されるときに引数として渡されてきた Event オブジェクトのプロパティを参照することで、発生したイベントに関する情報を取得できる。

プロパティ名 説明
Event.eventPhase イベントのフェーズ(キャプチャリングフェーズ、ターゲットフェース、バブリングフェーズ)
Event.type イベントの種類を表す文字列
Event.target イベントが発生した要素
Event.isTrusted イベントがユーザーの操作によって発生したかどうか
Event.bubbles イベントがバブリングするかどうか
Event.timeStamp 要素が作成されてからイベント発生までの経過時間(ミリ秒)
Event.cancelable イベントがキャンセル可能かどうか
Event.currentTarget イベントハンドラを登録した要素

以下のように使う

event.type

使用例

    <p>ボタンをクリックしてください。</p>

    <input type="button" value="button" id="xxx" />

    <script>
      let button = document.getElementById("xxx");

      button.addEventListener("click", function (event) {
        console.log("bubbles       :" + event.bubbles);
        console.log("cancelable    :" + event.cancelable);
        console.log("currentTarget :" + event.currentTarget);
        console.log("eventPhase    :" + event.eventPhase);
        console.log("target        :" + event.target);
        console.log("timeStamp     :" + event.timeStamp);
        console.log("type          :" + event.type);
        console.log("isTrusted     :" + event.isTrusted);
      });
    </script>


// bubbles       :true
// cancelable    :true
// currentTarget :[object HTMLInputElement]
// eventPhase    :2
// target        :[object HTMLInputElement]
// timeStamp     :1392.199999988079
// type          :click
// isTrusted     :true
Event.currentTargetプロパティとEvent.targetプロパティの違い
プロパティ名 説明
Event.currentTarget イベントハンドラを登録した要素
Event.target イベントが発生した要素

Event.curentTargetは以下のようなイベントハンドラを登録したターゲットの要素を指す。

let button = document.getElementById('xxx');
button.addEventListener('click', butotnClick);

Event.targetは実際にイベントが発生した要素を指します。

イベントに対するデフォルトの動作をキャンセルする(preventDefault)

リンクをクリックすれば、リンク先のページに移動したり、チェックボックスをクリックすればチェックが入るなどのデフォルトの動きが設定されているがpreventDefaultを使用するとキャンセルすることができる。

event.preventDefault()
  • すべてのイベントでキャンセルが可能ではない
  • Event.cancelableプロパティの値が true のイベントのみキャンセル可能
  <body>
    <div>
      <input type="checkbox" id="check1" />
      <label for="check1">はい</label>

      <input type="checkbox" id="check2" />
      <label for="check2">いいえ</label>
    </div>

    <script>
      let checkBox = document.getElementById("check2");

      checkBox.addEventListener("click", function (event) {
        alert("現在変更できません");
        event.preventDefault();
      });
    </script>
  </body>

「はい」にはチェックがいれられるが、「いいえ」をクリックするとアラートが出現してチェックが入らなくなる。

イベントをコードから発生させる(dispatchEvent)

イベントは HTTP ページをブラウザで見ているユーザーの操作によって発生するが、プログラムの中で新しいイベントを作成し指定した対象でイベントを発生させることがでできる。

  • 作成するには Event オブジェクトをのコンストラクタを使用
new Event(eventtype[, option])
  • 第一引数にイベントの種類を表す文字列を指定
  • カスタムイベントを作る場合は任意の名前
  • 第二引数には以下の3つの値に関する値をもったオブジェクトを指定する(省略可能)
    • bubbles(初期値は false): バブリングフェーズでイベントが伝搬されていくかどうか
    • cancelable(初期値は false): イベントがキャンセル可能かどうか
    • composed(初期値は false)
  • click イベントを新しく作成するには次のように記述
let e = Event('click');
イベントを発生させる
  • dispatchEvent メソッドは対象となるターゲットに引数に指定したイベントを発生させる
  • 引数には Event オブジェクトを指定
target.dispatchEvent(event)
  • click イベントを発生させるには次のように記述
let e = Event('click');

let target = document.getElementById('test');
target.dispatchEvent(e);
  • 発生したイベントがユーザーの操作によって発生したものか、それとも dispatchEvent メソッドによるコードから発生させられたものかは Event.isTrusted プロパティを参照することで判別できる
 <body>
    <div>
      <input type="button" id="btn1" value="button1" />
      <input type="button" id="btn2" value="button2" />
    </div>

    <script>
      let btn1 = document.getElementById("btn1");
      let btn2 = document.getElementById("btn2");

      btn1.addEventListener("click", function (event) {
        console.log("buttonA:" + event.isTrusted);

        let newEvent = new Event("click");
        btn2.dispatchEvent(newEvent);
      });

      btn2.addEventListener("click", function (event) {
        console.log("buttonB:" + event.isTrusted);
      });
    </script>
  </body>

// ボタン1を押した場合
  //buttonA:true
  //buttonB:false

// ボタン2を押した場合
  //buttonB:true
  • ボタン1が押された場合 まずユーザーのクリックにより表示されるのでbuttonA:trueが表示され、そのあとにクリックイベントを作成し、 dispatchEvent メソッドによってボタン2もユーザーの行動によらず押される。そのためボタンBのisTrustedではfalseとなっている。

【初学者用まとめ(DOM操作編)】まず覚えるべきJavaScriptのコードの書き方(自習記録)

はじめに

前提

まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。

現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。

hirano-vm4.hatenablog.com

筆者は執筆時点で、RubyRuby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。

詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。

この記事の本体は以下のなります。この記事はその記事の一部になりますので、そちらからご覧ください。

hirano-vm4.hatenablog.com

DOM操作

DOMとは

  • DOM(Document Object Model)は、文書(通常はHTMLまたはXML文書)の構造を表現し、その構造をプログラムから操作できる方法を提供するAPI
  • DOM は標準化団体である W3C で以前は標準化が行われていましたが、現在では WHATWG によって標準化が行われている
  • DOM は、 HTML や XML のドキュメントに含まれる要素や要素に含まれるテキストのデータをオブジェクトとして扱う(ドキュメントをオブジェクトが階層的に組み合わされたものとして識別)
  • 例えばボタンを設置して、それをユーザーがクリックしたらブラウザのリロードをせずに文字が出現するなどの操作が可能

DOMの階層構造

  • DOM ではドキュメントを構成するオブジェクトのことを「ノード」と呼ぶ
  • ノードは種類に応じて「要素ノード(htmlやbodyダグなど)」「テキストノード(そのまま文字列)」「属性ノード(idなど)」に分類される
  • 最上位のノードは Document ノード
  • 空白ノード:HTML ページの中で記述された空白やタブ、改行など空白文字は、 HTML ページがブラウザで表示される時にすべてがそのまま表示されるのではなく決められたルールに従って無視されて表示されるがDOM の中では要素と要素の間に空白やタブ、改行文字がある場合は空白ノードとして扱われるため注意が必要
  • Webページに記述した JavaScript から DOM を使用することで、 Web ページ上のテキストデータの読み込み、新しい要素の追加、 Web ページ上に表示されたボタンがクリックされた時にあらかじめ設定した処理を行うことができるようになる

ノード間の関係

  • ドキュメントに含まれる各ノードにはドキュメントツリーの位置に応じて親子関係や兄弟関係が存在する

id属性の値を指定して要素ノードを取得(getElementById)

Document オブジェクトのgetElementById メソッドは、要素の id 属性の値を指定して一致する Element オブジェクトを取得する

document.getElementById(id)

例:id属性の値として'button'が設定された要素ノードを取得する場合

let element = document.getEkementById('button');
  • HTMLにおいてidは重複して使えない(もし重複し場合は最初の要素ノードを取得)
  • これを変数に入れてその際に実行したい処理を記述していく

タグ名を指定して要素ノードを取得(getElementsByTagName)

Document オブジェクトの getElementsByTagName メソッドは、要素のタグ名を指定して一致する要素ノードをすべて取得する。

document.getElementsByTagName(tagname)
  • 引数にはタグ名を DOMString オブジェクトで指定(例: divp など)
  • * を指定した場合はすべての要素に一致
  • 戻り値は HTMLCollection オブジェクト(HTMLCollection オブジェクトは複数の要素ノードの集合)
    • HTMLCollection オブジェクトにはlength プロパティと item メソッド、namedItem メソッドが用意されているため取得した要素ノードの数を取得したり、指定した要素ノードを取したりできる
let elements = document.getElementsByTagName("div");

// 取得した要素の数を取得
let count = elements.length;

// インデックスを指定して要素を取得
let element = elements.item(0);

// インデックスを指定して要素を取得その2
let element = elements[0];

// ID属性またはname属性を指定して要素を取得
let element = elements.item("user");

練習してみた

HTML

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>getElementsByTagNameの練習!!</title>
  </head>
  <body>
    <h1>わいの好きなもの</h1>

    <div>
      <p>ラーメン</p>
      <p>寿司</p>
    </div>

    <div>
      <p>猫</p>
      <p>犬</p>
    </div>

    <button onClick="getElements();">要素をコンソールに表示させる</button>

    <script>
      function getElements() {
        let elements = document.getElementsByTagName("p");
        for (let i = 0; i < elements.length; i++) {
          console.log("Text: " + elements.item(i).textContent);
        }
      }
    </script>
  </body>
</html>

ボタンを押してみると実行できました😄

特定の要素の子孫要素に限定して要素ノードを取得
  • getElementsByTagName メソッドは Document オブジェクトの他に Element オブジェクトでも用意されている
  • getElementsByTagName メソッドを使用すると、その要素ノードの子や孫の要素ノードに限定してタグ名(要素名)を指定して要素ノードを取得することができる

idの属性の値がlistの要素ノードの子孫の中からdivタグの要素ノードを取得する場合は通常以下のように2行にわたって記述が必要

let element = document.getElementById('list');
let elements = element.getElementsByTagName('div');

しかしまとめて記述することもできる。

let elements = document.getElementById('list').getElementsByTagName('div');
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>getElementsByTagNameの練習!!</title>
  </head>
  <body>
    <h1>わいの好きなもの</h1>

    <div id="food">
      <p>ラーメン</p>
      <p>寿司</p>
    </div>

    <div id="other">
      <p>猫</p>
      <p>犬</p>
    </div>

    <button onClick="getElements();">犬・猫だけを表示</button>

    <script>
      function getElements() {
        let elements = document
          .getElementById("other")
          .getElementsByTagName("p");
        for (let i = 0; i < elements.length; i++) {
          console.log("Text: " + elements.item(i).textContent);
        }
      }
    </script>
  </body>
</html>

犬・猫だけ表示ができました〜

class属性の値を指定して要素ノードを取得(getElementsByClassName)

getElementsByClassName メソッドは、要素の class 属性の値を指定して一致する要素ノードをすべて取得する。

document.getElementsByClassName(classnames)
  • 引数には class 属性の値を DOMString オブジェクトで指定
  • 複数の class 属性の値を指定することができ、指定した値のいずれかに一致する要素ノードをすべて取得
  • 複数しているする場合は空白文字で区切る('classname1, classname2')
  • 戻り値は HTMLCollection オブジェクト(lengthメソッドやインデックスを指定しtえ要素を取得することができる)
  • 同じ class 属性の値をを持つ要素ノードは、 HTML ページの中で記述された順番にインデックスが割り当てられる
  • 指定したclass属性の要素ノードがない場合はnullではなくlengthが0のHTMLCollection オブジェクトが返ってくる
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>getElementsByClassNameの練習</title>
</style>
</head>
<body>

<p>[適当な日記]</p>

<div class="content">
  <p>今日はこんな場所に行きました〜</p>
  <p class="content">行き先1:公園</p>
  <p class="content">行き先2:スーパー</p>
</div>

<button onClick="getElements();">要素を取得</button>

<script>
function getElements(){
    let elements = document.getElementsByClassName('content');
    // 取得した値を使ってやりたい処理を書く
</script>
</body>
</html>

ボタンを押すと以下の要素が取得できる

<div class="content">
  <p>今日はこんな場所に行きました〜</p>
  <p class="content">行き先1:公園</p>
  <p class="content">行き先2:スーパー</p>
</div>

<p class=<200b>"content"><200b>行き先1:公園<200b></p><200b>

<p class=<200b>"content"><200b>行き先2:スーパー<200b></p><200b>

name属性の値を指定して要素ノードを取得(getElementsByName)

getElementsByName メソッドは、要素の name 属性の値を指定して一致する要素ノードをすべて取得する。

document.getElementsByName(name)
  • 引数には name 属性の値を DOMString オブジェクトで指定
  • 戻り値は NodeList オブジェクト NodeList オブジェクトは複数の要素ノードの集合
  • NodeList オブジェクトでは length プロパティと item メソッドが使える
  • 同じ name 属性の値を持つ要素ノードは、 HTML ページの中で記述された順番にインデックスが割り当てられていく
  • 要素が見つからない場合、 length が 0 の NodeList オブジェクトを返す
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>getElementsByNameの練習</title>
  </head>
  <body>
    <p>メニュー</p>

    <label><input type="radio" name="food" value="Coffee" />coffee</label>
    <label><input type="radio" name="food" value="Cake" checked />Cake</label>
    <label><input type="radio" name="food" value="Tea" />Tea</label>

    <button onClick="getElements();">要素を取得</button>

    <script>
      function getElements() {
        let elements = document.getElementsByName("food");
        console.log(elements);
        for (let i = 0; i < elements.length; i++) {
          if (elements.item(i).checked) {
            console.log(elements.item(i).value + " is checked");
          } else {
            console.log(elements.item(i).value + " is not checked");
          }
        }
      }
    </script>
  </body>
</html>

このようにnameを指定して対象の要素ノードを操作できる。

CSSセレクタ形式の条件に一致する要素ノードを取得(querySelector, querySelectorAll)

querySelectorメソッド・querySelectorAllメソッドはCSSセレクタ形式で条件を指定して一致する要素を取得できる。

document.querySelector(selectors)
  • 引数には CSS セレクタ形式の値を DOMString オブジェクトで指定
  • 戻り値は Element オブジェクト
  • querySelector メソッドは単一の要素ノードを返す(複数の要素が条件に一致した場合は最初の要素ノードが返される)
  • 見つからなかった場合は null
document.querySelectorAll(selectors)
  • 戻り値は Element が 1 つまたは複数含まれる NodeList オブジェクト
  • 条件に一致したすべてのノードを返す
  • 見つからなかった場合は length が 0 の NodeList オブジェクトを返す( length プロパティと item メソッドが使える)

CSSセレクタの種類と指定方法

CSS(Cascading Style Sheet) セレクタは、 HTMLページに対してスタイルをどの要素に設定するのかを指定する時に使用する。

全要素を対象とする(*) - すべての要素ノードを対象とする場合は * と記述 - HTML全体を指定する

let elements = document.querySelectorAll('*');

タグ名を指定する div p hタグなどを指定する場合は、タグ名をそのまま記述。

let elements = document.querySelectorAll('p');

pタグを全て取得する

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>getElementsByNameの練習</title>
  </head>
  <body>
    <h2>メニュー</h2>
    <p>要素1</p>
    <p>要素2</p>
    <p>要素3</p>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll("p");
        console.log(result); // NodeList(3) [p, p, p]
        console.log(result[0]); //  <p>要素1</p>
        console.log(result[1]); //  <p>要素2</p>
        console.log(result[2]); // <p>要素3</p>
      }
    </script>
  </body>
</html>

id属性の値を指定(#id) 要素に設定された id 属性の値を指定する場合は、#値 と記述する

  <body>
    <h2>メニュー</h2>
    <div id="content">
      <p>ここを取得</p>
    </div>

    <div>
      <p>ここは取得しない</p>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll("#content");
        console.log(result); // NodeList [div#content]  <p>ここを取得</p>の要素が取得できる
      }
    </script>
  </body>

class属性の値を指定(.class) 要素に設定された class 属性の値を指定する場合

  <body>
    <h2>メニュー</h2>
    <div class="my_class">
      <p>ここを取得</p>
    </div>

    <div>
      <p>ここは取得しない</p>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll(".my_class");
        console.log(result); // NodeList [div.my_class]
      }
    </script>
  </body>

複数のセレクタのいずれかに一致する(A B C) セレクタをカンマ(,)で区切って記述することで、複数のセレクタの少なくともいずれか一つに一致する要素ノードを取得できる。

以下の例では、class属性がmy_class2・id属性がmy_idいずれかに該当する要素を取得しています。

  <body>
    <h2>メニュー</h2>
    <div id="my_id">
      <p>ここを取得</p>
    </div>

    <div>
      <p class="my_class">ここは取得しない</p>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll("#my_id, .my_class");
        console.log(result); // NodeList(2) [div#my_id, p.my_class]
      }
    </script>
  </body>

Aのセレクタに一致する要素の子孫要素の中でBのセレクタに一致する(A B) セレクタを空白で区切ることで、最初に一致した要素の子孫要素の中で、さらに要素を指定して選択することができる。

  <body>
    <div id="main">
      <p class="greeting">こんにちは</p>
      <div>
        <p class="other">元気です</p>
      </div>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll("#main .greeting");
        console.log(result); // NodeList [p.greeting]
      }
    </script>
  </body>

このようにid属性のmainの中のclass属性greetingだけを取得できている。

Aのセレクタに一致する要素の子要素の中でBのセレクタに一致する(A > B) セレクタを > で区切って記述することで、最初のセレクタに一致する要素の子要素の中で、次に記述したセレクタに一致する要素ノードを取得する。

  • 空白で区切った場合は子および孫要素が対象となったが > で区切った場合は子要素だけが対象
  </head>
  <body>
    <div id="main">
      <p class="greeting">こんにちは</p>
      <div>
        <p class="greeting">こんばんは</p>
      </div>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll("#main > p");
        console.log(result); // NodeList [p.greeting] こんにちはだけ取得できた
      }
    </script>
  </body>

Aのセレクタに一致する要素の中で属性値Bを持つ要素に一致する(A[B])

  <body>
    <div>
      <p style="font-size: 10px">こんにちは</p>
    </div>

    <div>
      <p class="other">その他</p>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.querySelectorAll("p[style]");
        console.log(result); // NodeList [p] こんにちはだけ取得できた
      }
    </script>
  </body>

ノードの子・親・兄弟ノードを取得

  • HTML ページに含まれるノードは階層構造になっていてそれその親・兄弟・子ノードを取得したい時がある
  • これらのノードは Node オブジェクトで用意されている各種プロパティを参照することで取得できる
  • このプロパティは参照専用
  • 戻り値はNodeList オブジェクト(NodeList オブジェクトは複数のノードの集合。 length プロパティ・item メソッドが使える)
すべての子ノードを取得

getElementById メソッドや querySelector メソッドなどを使って要素ノードを取得したあと、 Node オブジェクトの childNodes プロパティを参照することで、このノードの 1 つ下の階層にある子ノードをすべて取得することができる

  • HTML 文の記述の仕方によっては空白ノードが含まれる

使い方

node.childNodes
  <body>
    <div id="element">
      <p>要素1</p>
      <p>要素2</p>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.getElementById("element");
        console.log(result.childNodes); // NodeList(5) [text, p, text, p, text]が取得できる
        console.log(result.childNodes[1]); // <p>要素1</p>が取得できる
        console.log(result.childNodes[3]); // <p>要素2</p>が取得できる
      }
    </script>
  </body>

最初の子ノードと最後の子ノードを取得

node.firstChild
  • firstChildプロパティを参照することで、このノードの 1 つ下の階層にある子ノードの中の最初のノードを取得することができる(参照専用)
  • 戻り値はNode オブジェクト
node.lastChild
  • lastChild プロパティを参照することで、このノードの 1 つ下の階層にある子ノードの中の最後のノードを取得できる

同じ階層の次のノードと前のノードを取得

node.nextSibling
  • nextSibling プロパティを参照することで、このノードと同じ階層にある次のノードを取得できる
  • 戻り値はNode オブジェクトでない場合はnull返る
node.previousSibling
  • previousSibling プロパティを参照することで、このノードと同じ階層にある一つ前のノードを取得できる(参照専用)
  • 戻り値はNode オブジェクト

親ノードを取得する

node.parentNode

parentNode プロパティを参照することで、このノードの親ノードを取得できる。

  <body>
    <div>
      <p>要素1</p>
      <p id="test">要素2</p>
    </div>

    <button onClick="getElements();">要素を取得</button>
    <script>
      function getElements() {
        let result = document.getElementById("test");
        let parent = result.parentNode;

        while (parent) {
          console.log(parent.nodeName); // NodeList(5) [text, p, text, p, text]が取得できる
          parent = parent.parentNode;
        }
      }
    </script>
  </body>

id属性がtestの要素を取得して変数に格納、その変数に対してparentNodeで親ノードを取得。 それを繰り返し分の中で再度変数に入れ直すことで親の親の親と取得でき最終的に#documentまで取得できた。

実行結果

DIV
BODY
HTML
#document

ノードの種類を確認する

nodeType プロパティを参照するとノードの種類を確認することができる。

node.nodeType

戻り値はノードの種類を表す unsigned short 型の値(数字)が返ってくる。

 1 ELEMENT_NODE                 要素ノード
 2 ATTRIBUTE_NODE               属性ノード
 3 TEXT_NODE                    テキストノード
 4 CDATA_SECTION_NODE           CDATAセクション
 5 ENTITY_REFERENCE_NODE        実体参照
 6 ENTITY_NODE                  実体宣言
 7 PROCESSING_INSTRUCTION_NODE  処理命令
 8 COMMENT_NODE                 コメントノード
 9 DOCUMENT_NODE                ドキュメント
10 DOCUMENT_TYPE_NODE           ドキュメントタイプ
11 DOCUMENT_FRAGMENT_NODE       ドキュメントの断片
12 NOTATION_NODE                記法
    <p>今日は外でランチを食べました</p>

    <div id="blog">
      <p>朝から外出していたのでランチは外で頂きました。</p>
      <p>場所:港区南青山</p>
      <p>店名:リストランテ南青山</p>
    </div>

    <button onClick="getElements();">要素を取得</button>

    <script>
      function getElements() {
        let elements = document.getElementById("blog");
        let children = elements.childNodes;

        let len = children.length;
        for (let i = 0; i < len; i++) {
          console.log(children.item(i).nodeType);
        }
      }
    </script>
  </body>

// 3
// 1
// 3
// 1
// 3   このように取得できる。必要に応じてこれを活かして文字列に変換する

ノードの名前を取得

odeName プロパティを参照するとノードの名前を取得することができる。

node.nodeName
<!DOCTYPE html>
<html>
  <body>
    <p>This is a paragraph.</p>
    <h1>This is a heading.</h1>
  </body>
</html>
// ページ内の要素を取得
let paragraphElement = document.querySelector("p");
let headingElement = document.querySelector("h1");

// nodeNameプロパティを使用して要素の名前を取得
let paragraphName = paragraphElement.nodeName;
let headingName = headingElement.nodeName;

// 結果をコンソールに表示
console.log("段落要素の名前: " + paragraphName);
console.log("見出し要素の名前: " + headingName);

// 段落要素の名前: P
// 見出し要素の名前: H1

ノードの値を参照

Node オブジェクトの nodeValue プロパティを参照するとノードの値を取得することができる。

node.nodeValue
let paragraph = document.querySelector("p");
let textInsideParagraph = paragraph.firstChild.nodeValue;

console.log(textInsideParagraph);

// This is a paragraph.

let new = paragraph.firstChild.nodeValue = "新しいテキスト";
console.log(new);

// 新しいテキスト

ノードの値を設定

nodeValueプロパティは参照するだけではなく新しい値を設定することもできる。

node.nodeValue = 'value'
let paragraph = document.querySelector("p");
let textInsideParagraph = paragraph.firstChild.nodeValue;

console.log(textInsideParagraph);

// This is a paragraph.

let textInsideParagraph

ノード内のテキストを取得・設定(textContent)

textContentプロパティを参照すると、対象のノードに含まれるテキストを取得する。

node.textContent
  • 戻り値はテキストが連結された DOMString オブジェクト
<div>
  <p>要素1</p>
  <p>要素2</p>
</div>

これらをtextContentで取得すると要素1要素2が取得できる。

新しいテキストを設定する また参照するだけでなく新しい値を設定できる。

node.textContent = 'value'
  • textContent プロパティに新しい文字列を設定するときに HTML で特別な意味を持つ文字はエスケープ処理されてから設定される
変換前: <p>サンプル</p>
変換後: &lt;p&gt;サンプル&lt;/p&gt;

要素内のレンダリングされたテキストを取得・設定(innerText)

  • innerText プロパティを参照すると、対象の要素のテキストを取得する 取得するテキストは対象の要素をブラウザで表示した時に表示されるテキストだけ
  • 要素に子や孫の要素が含まれる場合は、それらの要素のテキストも含めて取得
テキストを参照する

使い方

htmlElement.innerText
  • 戻り値はすべてのテキストが連結された DOMString オブジェクト
  • 空白ノードなどのようにブラウザでは表示されないものは取得しない

  • タグで改行が行わている場合は改行された状態でテキストを取得
<div>
  <p>サンプル1<br>サンプル2</p>
  <p>サンプル3</p>
</div>

これをinnerTextで取得すると以下のようになる。

サンプル1
サンプル2

サンプル3
新しいテキストを設定する

nnerText プロパティは参照するだけではなく新しい値を設定することができる。

htmlElement.innerText = 'value'

表示されているpタグの文字列をボタンを押して、新しい文字を設定して変化させる。

  <body>
    <div id="test">ここが変化するよ〜</div>
    <button onClick="textchage();">テキストが変わる魔法ボタン</button>

    <script>
      function textchage() {
        let element = document.getElementById("test");
        element.innerText = "ほら、変わった!";
      }
    </script>
  </body>

こんな感じで変化する

HTML文を参照する(innerHTML)

innerHTML プロパティを参照すると要素に含まれる HTML 文を取得することができる。

element.innerHTML
  • 戻り値は要素に含まれるHTMLが文字列で返ってくる
<div id="test">
  <p>サンプル1<br>サンプル2</p>
  <p>サンプル3</p>
</div>
let elements  = document.getElementById("test")
console.log("element.innerHTML")

上記のHTMLは以下のように取得できる

  <p>サンプル1<br>サンプル2</p>
  <p>サンプル3</p>

<div id="test">の要素の中身を取得するので、外側のdivタグは含まれない。取得した要素の内側にdivタグがあればもちろん含まれる。

新しいHTML文を設定する

innerHTML プロパティは参照するだけではなく新しい値を設定することもできる。

element.innerHTML = 'value'
  • 要素の innerHTML プロパティの値に新しい文字列を設定すると、対象の要素の子孫要素のノードがすべて削除されたあとに設定した HTML 文の値が追加される

実演

  <body>
    <div id="test">ここをh1タグの見出しに変えます</div>

    <button onClick="setTitle();">ここを押したら変わるよ</button>

    <script>
      function setTitle() {
        let element = document.getElementById("test");
        element.innerHTML = "<h1>でかい見出しになった!!</h1>";
      }
    </script>
  </body>

実行したらこんな感じ。文字だけでなくタグも変わっている。

要素自身も含めたHTML文を参照する(outerHTML)
  • outerHTML プロパティを参照すると要素と要素に含まれる HTML 文を取得できる
  • HTML 文は HTML ファイルに記述されている内容をそのまま取得
<div>
  <p>サンプル1<br>サンプル2</p>
  <p>サンプル3</p>
</div>

これをouterHTMLで取得すると以下のようになる。そのままになる。

<div>
  <p>サンプル1<br>サンプル2</p>
  <p>サンプル3</p>
</div>

要素の属性値を取得・設定(getAttribute,setAttribute)

属性値を取得

要素に設定された属性値は以下のようにプロパティ名として属性名を指定して参照することができる。

element.属性名

   let element = document.getElementById(test');
    console.log(element.children[0].href);
    console.log(element.children[1].className);
    console.log(element.children[1].title);
    console.log(element.children[1].href);
  • 属性名があるが値が設定されていない場合は空文字
  • 存在しない属性名を参照するとundefined
属性値を設定

属性名を表すプロパティに対して値を設定することで、属性値に新しい値を設定することができる。

element.属性名 = 'value'
  • 属性名に対して値が設定されていた場合は、新しく値が設定される
  • ない場合は新しく属性名に対して値が設定される
let element = document.getElementById('test');
element.class = 'test';
  • 引数には取得したい属性名を DOMString オブジェクトで指定
  • 戻り値は引数に指定した属性名の属性値を DOMString オブジェクトで返す
  • 定した属性が見つからなかった場合は空文字か null
  • getAttribute メソッドを使う時は引数に 'class' のように属性名をそのまま指定する
    let element = document.getElementById('shopinfo');
    console.log(element.children[0].getAttribute('href'));
    console.log(element.children[1].getAttribute('class'));
属性値を取得(getAttributeメソッド)

要素の属性名を取得するもう一つの方法としてgetAttributeメソッドがある。指定した属性名の属性値を取得する。

element.getAttribute(qualifiedName)
属性値を設定(setAttributeメソッド)

setAttribute メソッドは指定した属性名の属性値に新しい値を設定する。 - 第一引数に属性名 - 第二引数に属性値を指定 - 属性名に対して値が設定されていた場合は新しい属性値が設定される - 属性名に値が設定されていなかった場合は、新しく属性名に対して属性値が設定される 書き方

element.set Attribute(qualifiedName, value)
let element = document.getElementById(' test');
element.setAttribute('class', 'test'); // 新たにclass属性をtestとして設定

要素にせってされている属性を削除(removeAttribute)

emoveAttribute メソッドを使用することで要素に設定されている属性を削除することができる

書き方

element.removeAttribute(qualifiedName)

ノードを子ノードの中の先頭または最後に追加(prepend,append,appendChild)

ノードを子ノードの先頭に追加する(prepend)
parentnode.prepend(node[,node,...])

prepend メソッドはノードを子ノードの先頭に追加する。

  • 引数には追加する Node オブジェクトまたは DOMString オブジェクトを指定

    let child = document.createElement('p');
    child.prepend('テスト');

    let parentnode = document.getElementById('test');
    parentnode.prepend(child);

上記の例は新しくpタグを作成して先頭にテストという文字列を設定して変数に入れる。次にidのtestを取得し、そこの先頭に先ほど作った文字列を入れている。

ノードを子ノードの最後に追加する(append)

同じように最後にも追加ができる。

parentnode.append(node[,node,...])
ノードを子ノードの最後に追加する(appendChild)

appendChild メソッドはノードを子ノードの最後に追加する。

parentnode.appendChild(node)

ノードを子ノードの中の指定ノードの前または後ろに追加(before,insertBefore,after)

ノードを子ノードの中の指定ノードの前に追加する(before)

before メソッドはノードを子ノードの中の指定ノードの前に追加する。

childnode.before(node[,node,...])
  • 引数には追加する Node オブジェクトまたは DOMString オブジェクトを指定
  • 親ノードではなくノードを追加する基準となる指定ノードに対して実行する
let childnode = document.getElementById('test');
let child = document.createElement('li');
child.append('要素を前に追加');

childnode.before(child);
ノードを子ノードの中の指定ノードの前に追加する(insertBefore)

insertBefore メソッドはノードを子ノードの中の指定ノードの前に追加する。

parentnode.insertBefore(node, childnode)
  • 第一引数には追加するNodeオブジェクトを指定
  • 第二引数に指定したNodeオブジェクトの前に追加する
  <body>
    <ul id="list">
      <li id="test1">要素1</li>
      <li id="test2">要素2</li>
    </ul>
    <button onClick="getElement();">ノードを追加</button>

    <script>
      function getElement() {
        let child = document.createElement("li");
        child.append("追加する要素");

        let parentnode = document.getElementById("list");
        let childnode = document.getElementById("test2");
        parentnode.insertBefore(child, childnode);
      }
    </script>
  </body>

ノードを子ノードの中の指定ノードの後に追加する(after)
childnode.after(node[,node,...])
  • after メソッドはノードを子ノードの中の指定ノードの後に追加する
  • 複数のノードを指定した場合は、まとめて追加される
  <body>
    <ul id="list">
      <li id="test1">要素1</li>
      <li id="test2">要素2</li>
    </ul>
    <button onClick="getElement();">ノードを追加</button>

    <script>
      function getElement() {
        let child = document.createElement("li");
        child.append("追加する要素");

        let childnode = document.getElementById("test2");
        childnode.after(child);
      }
    </script>
  </body>

既存のノードを追加する
  • ノードの追加は新しく作成するだけでなく同じドキュメントに存在する既存のノードを新しく場所を指定して追加することができる
  • その場合、現在の親ノードから対象のノードは削除されて新しく指定した親ノードの子ノードに追加される
  <body>
    <ul id="list">
      <li id="test1">要素1</li>
      <li id="test2">要素2(これを要素4の後ろに追加する)</li>
    </ul>

    <ul id="list2">
      <li id="test3">要素3</li>
      <li id="test4">要素4</li>
    </ul>

    <button onClick="getElement();">ノードを追加</button>

    <script>
      function getElement() {
        let child = document.getElementById("test2");

        let childnode = document.getElementById("test4");
        childnode.after(child);
      }
    </script>
  </body>

ノードを別のノードに置換(replaceWith)

ノードを別のノードに置き換える(replaceWith)

childnode.replaceWith(node[,node,...])
  • replaceWith メソッドはノードを別のノードに置き換える
  • 複数のノードを指定した場合は、対象のノードを複数のノードと置き換える
  • 親ノードではなく置き換えるノードに対して実行する
  <body>
    <ul id="list">
      <li id="test1">要素1</li>
      <li id="test2">要素2(これを置き換える)</li>
    </ul>

    <button onClick="getElement();">ノードを追加</button>

    <script>
      function getElement() {
        let child = document.createElement("li");
        child.append("置き換わった!");

        let childnode = document.getElementById("test2");
        childnode.replaceWith(child);
      }
    </script>
  </body>

ノードを削除(remove, removeChild)

ノードを削除する(remove)
childnode.remove()
  • 対象のノードを削除する
let childnode = document.getElementById('removeId');

childnode.remove();
ノードを削除する(removeChild)
  • removeChild メソッドはノードを削除する
  • 第一引数に指定してNodeオブジェクトを削除する
    <script>
      function getElement() {
        let parentNode = document.getElementById("list");

        parentNode.removeChild(parentNode.lastElementChild);
      }
    </script>
  • 指定したHTML要素自身を削除するときはremoveメソッドを使う
  • 指定したHTML要素の子要素を削除するときはremoveChildメソッドを使う

HTMLを表す文字列からノードを作成し指定の位置に追加(insertAdjacentHTML)

insertAdjacentHTML メソッドを使用すると、引数に指定した文字列を HTML 文として解析し、 HTML 文から作成したノードを指定した位置に追加することができる。

element.insertAdjacentHTML(position, text)
  • 第二引数に指定した文字列をHTML文として、HTML文からノードを作成
  • 作成したノードを第一引数で指定した位置に追加する
第一引数にしていできる値
'beforebegin' element の前に追加
'afterbegin' element の子要素の先頭に追加
'beforeend' element の子要素の最後に追加
'afterend' element の後に追加
実例
  <body>
    <ul id="list">
      <li id="test1">要素1</li>
      <li id="test2">要素2</li>
    </ul>

    <button onClick="getElement();">ノードを追加</button>

    <script>
      function getElement() {
        let element = document.getElementById("list");

        element.insertAdjacentHTML("beforeend", "<li>追加されました!</li>");
      }
    </script>
  </body>

現在処理を実行しているscript要素を取得(currentScript)

currentScriptプロパティを参照すると現在実行している <script> 要素を取得する

  <body>
    <script>
      let element = document.currentScript;
      console.log(element);
    </script>
  </body>

これをブラウザで実行するscriptタグがコンソールに表示される。

    <script>
      let element = document.currentScript;
      console.log(element);
    </script>

以下のように取得したScriptタグに要素を追加するなどの操作が行える。

  <body>
    <script>
      let element = document.currentScript;
      element.insertAdjacentHTML("beforebegin", "<p>取得したScriptタグに要素を追加</p>");
    </script>
  </body>

ドキュメントにHTMLを表す文字列を書き込む(write)

document.write(string)
  • write メソッドは、ドキュメントに対して文字列を書き込む
  <body>
    <script>
      document.write("<p>書き込みました</p>");
    </script>
  </body>

実行するとブラウザに書き込みましたと表示される。

文字列の形式で HTML コードを書き込むことができるが、現在 HTML に関する仕様を定めている HTML Living standard ではこのメソッドの使用について推奨はしていない。

Document.currentScript プロパティと insertAdjacentHTML メソッドを組み合わせて代替するなどの方法がよさそう。

【初学者用まとめ(基礎編)】まず覚えるべきJavaScriptのコードの書き方(自習記録)

はじめに

前提

まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。

現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。

hirano-vm4.hatenablog.com

筆者は執筆時点で、RubyRuby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。

詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。

コンソールに表示する(console.log())

  • 括弧 () 内に入力された文字をコンソールに出力
  • シングルクォーテーション( ' )かダブルクォーテーション( " )で囲む
  • 文の最後はセミコロン(;)
  • 演算子も使える(クォーテーションがつくと文字列になるので注意)
// 8となる
console.log(5 + 3);

// 12となる
console.log(20 - 8);

// 文字列とみなされ、そのまま表示される
console.log("4 + 5");

// 36
console.log(8 * 4);

// 6
console.log(24 / 4);

// 「A」と「B」を連結してコンソールに出力
console.log("A" + "B");

// 文字列の「20」と「15」を連結してコンソールに出力
console.log("20" + "15");

// 1となり、このあたりはRubyと同じ
console.log(7 % 2);

コメントアウト

1行のコメントアウト

  • 文頭に「//」を書くと、その行はコメントとみなされる
const price = prompt("価格を入力せよ"); // 以降はコメントとして使える

複数行のコメント

/*から*/の間は改行があってもコメントアウトになる

/*
aaa
bbb
ccc
*/

変数

変数は数値や文字列などの値を変数(名前が書かれた箱のようなイメージ)に入れて保持できる。

何度も使ったりする場合はその都度コードを書いていたら冗長になってしまうため、変数に格納して使いまわす。

  • 書き方

let 変数名 = 変数に入れる値

※letだけでなくconstやvarもある。再代入できるかどうかなどの細かい違いがあるので「変数と定数の違い」の表を見てください

  • JavaScriptでは2単語以上ではキャメルケース ⚪︎createName ※スネークケースは× create_name
// 変数nameを定義し、「けだま」を代入
let name = "けだま";

// 変数nameの値を出力してください
console.log(name); //=> けだま
  • 上から順に実行される。変数は上書きできる
let name = "しらたま";
console.log(name);

// 変数nameの値を更新
name = "きなこ";

// 変数nameの値を出力すると「きなこ」になる
console.log(name);
  • 変数自体を更新する
let number = 7;

// 変数numberの値に3を加えると10になる
number += 3 ;


// 変数numberの値を2で割ると5が出力される
number /= 2;

変数と定数の違い

上記のようにletを使うと値を再代入できてしまうがconstであれば以下のようにスコープが狭い。

  • 定数を使うメリット 定数のメリットは、「後から値を更新できない」ところにある。また予期せぬ更新を防ぐ。

基本的にはconstを使う!!

// 定数languageを定義
const language = "フランス語";

// 定数languageの値を出力
console.log(language); // => フランス語

console.log(language + "を話せます");  // => フランス語を話せます

テンプレートリテラル

  • 文字列の中で${定数または変数}とすることで、文字列の中に定数や変数を含めることができ
  • 文字列全体をバッククォーテーション()`で囲む

この辺Rubyと間違えそう笑 (Ruby"#{}")

const name = "きなこ";
const age = 4;

// ぼくの名前はきなこです
console.log(`ぼくの名前は${name}です`)
;
// 今は4歳です
console.log(`今は${age}歳です`);

データの入力を受け取る

promptメソッドはユーザーに対して入力を求め、入力された値を戻り値とするメソッド。受け取った値を変数に代入する。そしてコンソールに出力すると受け取れていることが確認できる。

変数 = input('メッセージ文字列')

const text = prompt("入力せよ");

console.log(text);

Chromeデベロッパーツールで実行してみると「入力ダイアログボックス」が表示される。

promptメソッドの戻り値は数字であっても文字列として保持される

const price = prompt("価格を入力せよ"); // 100を入力

console.log(price + 10);
// =>10010
  • parseIntで整数化させると数値として扱われる
const price = prompt("価格を入力せよ"); // 100を入力

console.log(parseInt(price) + 10);
//=> 110

そもそもconsoleとは?

  • console.logの中でメソッドはlogを指す
  • consoleはオブジェクトが入った変数

    オブジェクトというのはJavaScriptで操作できる「何か」を表すもの

  • 他にもWebブラウザの表すWindowオブジェクトやWebページの内容を表すDocumentオブジェクトなどがある
  • オブジェクトとは複数の「機能(メソッド)」と「変数(プロパティ)」の集合体だといえる

条件分岐

  • 特定の条件を満たす場合のみ実行する
  • 「true(真)」と「false(偽)」で判定する(合わせて真偽値または論理値)
  • JavaScriptにはtrueかfalseを返す関数やメソッドや演算子があり、これを組み合わせて条件分岐を作っていく

(例) isNaN('文字列'):trueかfalseを返すメソッド age < 30::true かfalseを返す演算子 if(式や関数など):trueかfalseかで分岐する文

入力されたものが数値かどうかを確認する

isNaN関数を浸かって実践。promptメソッドを浸かって入力されたものが数値になるかを判断してみます。

  • isNaN関数書き方:isNaN( 値 ) 値は数値に変換は不可?
    • NaNは「Not a Number」の略
    • 渡された値が数値に変換可能ならfalse
    • 変換不可能ならtrueを返す
isNaN('1234') // => false

isNaN('-10') // => false

isNaN('3.14') // => false

isNaN('555') // =>true 全角はNG

isNaN('abc123') // => true

if文で数値でない場合に処理を実行する

  • 書き方
if (式や関数など) {
  実行する処理
}

(式や関数など)がtrueの時にブロック内の処理を実行する

const text = prompt( "入力せよ" );
if ( isNaN( text ) ) {
    console.log( "数字ではありません" );
}
// => 数字ではありません

数値のときに計算する場合

isNaNメソッドのまま逆の判定をしたい時は ! isNaNとすることでtrueをfalse逆にすることが出きる。Chromeデベロッパーツールで100を入力してみると計算できていることがわかる。

const text = prompt( "入力せよ" );
if ( ! isNaN( text ) ) {
    console.log( parseInt( text )  + 80 );
}

// => 180

条件に合致しないときの処理も記述する

const text = prompt( "入力せよ" );
if ( ! isNaN( text ) ) {
    console.log( parseInt( text )  + 80 );
} else {
    console.log( "数字ではない" );
}

// => 数字ではない  ※文字列を入力

比較演算子を使ってif文をつくる

JavaScriptの比較演算子は以下の一覧のようになっている。

比較演算子 説明
== 等しいかどうかを比較(型の変換あり)
!= 等しくないかどうかを比較(型の変換あり)
=== 厳密な等価性を比較(型の変換なし)
!== 厳密な不等価性を比較(型の変換なし)
< より小さいかどうかを比較
> より大きいかどうかを比較
<= 以下かどうかを比較
>= 以上かどうかを比較

Rubyの比較演算子と比較して特に違いを感じたのは「==」「===」と「!=」「!==」の違いです。

123 == '123' // => true  型変換されるので等しいという判定になる

123 === '123' // => false 型変換されないので等しくないとなる

これは「!==」も同様です。「==」「!=」はこのルーズさがあるので問題を引き起こす可能性があり「!==」「===」が推奨と学習テキストには記述がありました。

この比較演算子とif文を使って条件分岐を作ってみまます。

const text = prompt("年齢は?");
const age = parseInt(text);
if (age >= 20) {
  console.log("成人");
}

// => 成人

3つ以上の条件分岐

これはRubyとほぼ同じ。しかしRubyだとelsifだがJavaScriptではelse if(半角スペースあり)となるあとはif else if elseそれぞれで{}を用意する必要がある。

if (式や関数) {
  // 実行する処理
} else if (式や関数) {
  // 実行する処理
} else (式や関数) {
  // 実行する処理
}

上から実行され該当するしたところで処理は終了。

使用例

const text = prompt("年齢を入力してください");
const age = parseInt(text);

if (age < 20) {
  console.log("未成年");
} else if (age < 65) {
  console.log("成人");
} else {
  console.log("高齢者");
}

条件分岐の中に条件分岐を書く

const text = prompt("年齢を入力してください");

if (!isNaN(text)) {
  const age = parseInt(text);
  if (age < 20) {
    console.log("未成年です");
  } else {
    console.log("成人です");
  }
}

switch文

switch 文は対象となる値がいずれかの値と一致するかどうかを調べて処理を分けることができる。

function evaluateNumber(number) {
  switch (true) {
    case typeof number !== 'number':
      console.log('無効な数値です');
      break;
    case number > 0:
      console.log('入力した数値は正の数です');
      break;
    case number < 0:
      console.log('入力した数値は負の数です');
      break;
    case number === 0:
      console.log('入力した数値はゼロです');
      break;
    default:
      console.log('該当する条件がありません');
  }
}

// 異なる数値を試す
evaluateNumber(10); // '入力した数値は正の数です'
evaluateNumber(-5); // '入力した数値は負の数です'
evaluateNumber(0);  // '入力した数値はゼロです'
evaluateNumber('abc'); // '無効な数値です'
evaluateNumber(true);  // '該当する条件がありません'

Switchのあとの()内に記述した式を評価して、その値がcaseの後に書かれたいずれかの値と一致するかどうか調べて一致した処理を行う。

breakを記述すると一致した場合処理を終える。breakがないと一致した式以降の処理も全て実行されてしまうため注意。

また、いずれの値とも一致しなかった場合は default 句の後に記述されている処理を実行する。 default 句は不要であれば省略も可能。

なお、条件の比較は== 演算子ではなく === 演算子を使って比較されることに留意する。

使いどころとしては「if 文を使って数多くの値と === 演算子を使って一致するものがないかどうか調べたい場合」にはswitch 文を使うとすっきりする。

論理演算子

論理演算子
論理積 && かつ(左右の値がtureのときだけtureを返す)
論理和 || または(左右一歩位がtrueのときにtrueを返す)
論理否定 ! trueとfalseを逆転する
$$
const text = prompt("年齢を入力してください");
const age = parseInt(text);

if (age >= 6 && age <= 15) {
  console.log("義務教育期間中");
}
||
const text = prompt("年齢を入力してください");
const age = parseInt(text);

if (age < 20 || age >= 65) {
  console.log("未成年または高齢者");
}

if文には真偽値を返す式や関数以外を渡すこともできる

falseとみなされる値
false
0
-0
0n
"" (空文字)
null
undefined
NaN

数値の 0 ・文字列の空文字、・null ・undefined などは falseとなり、それ以外がtrueとなる!

たとえばこんなif分はtrueとなりブロックの中のコードが実行される。

if (0) {
  // 実行したい処理
}

if文を1行で書く

以下のような記述を覚えておくとコードがスッキリする

処理が1行しかない場合の省略形

if (num > 80) console.log("numは80より大きいです。"); このように記述が可能

三項演算子で記述

条件式 ? trueの処理 : falseの処理

配列

配列は中に複数の値を入れられる「型」。要素の値は文字列、数値なんでもOK!

配列の書き方

変数 = [値0, 値1, 値2, …]

配列の要素の取り出し方

配列内の要素を利用する時は、変数名のあとに各カッコで囲んで数値を記述する。この数値を「インデックス(添え字)」という。

なお、このインデックスは最初の値は「0」から始まるので注意(1ではない)。

先頭を取得

変数[0]

const member = ["a", "b", "c", "d", "e"];

console.log(member[0]); //=> "a"

末尾を取得

Rubyのように変数[-1]では取得できない

const member = ["a", "b", "c", "d", "e"];

// 方法1: 配列のlengthメソッドを使用
const lastElement = member[member.length - 1];
console.log(lastElement); // "e"


// 方法2: pop() メソッドを使用して最後の要素を取り出す
const lastElement2 = member.pop();
console.log(lastElement2); // "e"

配列の要素を書き換える

以下のようにすると配列の要素を書き換えることができる。

const member = ["a", "b", "c", "d", "e"];

member[0] = "x";

console.log(member[0]);

//  "x"

配列の要素が空のパターン

以下のように要素が空の場合、その要素を取り出すとundefinedになる。

let arr = [1, , 3];

console.log(arr);
>> [1, empty, 3]

console.log(arr[1]);
>> undefined

要素の値として式を指定

配列を作成する時に要素の値として数値や文字列などのリテラルだけではなく式を指定することができる。

let tax = 1.1;
let price = [100 * tax, 200 * tax];

console.log(price);
>> [110, 220]

多次元配列

配列の中の要素に配列が入っている場合、多次元配列と呼ぶ

let familyData = [
  ["Shiratama", 5],
  ["Kinako", 4],
];

多次元配列の値を取り出し方

以下のように「配列の要素⚪︎個の配列の⚪︎個目の要素」として取得する(配列[0][0]など)

let familyData = [
  ["Shiratama", 5],
  ["Kinako", 4],
];

console.log(familyData[0]);
// ['Shiratama', 5]

console.log(familyData[0][0]);

// Shiratama

配列を操作する際に使える便利なメソッド

配列はArray型のオブジェクト。

コード 説明
array.length 配列の要素数を返す。
array.push(element) 配列の末尾に要素を追加する。
array.pop() 配列の最後の要素を削除して返す。
array.remove(value) 配列から値と一致する要素を取り除く。
array.sort() 配列の要素を並べ替える。
array.shift() 配列の先頭要素を削除して返す。

参照:Array - JavaScript | MDN

様々なオブジェクトとそのメソッドがあるためリファレンスを確認する!!

JavaScript | MDN

ブラウザのコンソールで配列の中身を表示する

以下を実行して、コンソールを確認。「▶️」を押すと、中身が確認できる。

JavaScriptでは文字列も連続のデータ

文字列ABCDE"であってもfor文で配列のように文字列[要素数]で値を取得できる。

const teams = "ABCDE";

console.log(teams[0]);

// A

for (let team of teams) {
  console.log(team);
}

// A
// B
// C
// D
// E

変数に値を加える際に省略して記述する方法(代入演算子

代入演算子 説明
+= a += 10 加算代入 (a = a + 10)
-= a -= 5 減算代入 (a = a - 5)
*= a *= 2 乗算代入 (a = a * 2)
/= a /= 3 除算代入 (a = a / 3)
%= a %= 4 剰余代入 (a = a % 4)
**= a **= 2 指数代入 (a = a ** 2)

インクリメント演算子

JavaScriptには、インクリメント演算子として ++ が用意されていて、変数の値を1つ増加させるための演算子。 インクリメント演算子は、ループ処理やカウンターの更新など、変数の値を増加させる際によく使われる。

  • 前置インクリメント (++a): 値を増加させた後の値を返す
let a = 5;
let b = ++a;
console.log(a); // 結果は 6
console.log(b); // 結果は 6

++a が前置インクリメントで、a の値が増加した後に b に代入されている。そのため、a と b はともに 6 の値を持っていいる。

  • 後置インクリメント (a++): 値を返した後に値を増加させる
let x = 5;
let y = x++;
console.log(x); // 結果は 6
console.log(y); // 結果は 5

後置インクリメントの場合、まず x の値が y に代入され、その後に x の値が増加する。そのため、x は 6 に増加し、y は元の値の 5 を持っている。

デクリメント演算子

インクリメント演算子の逆。インクリメントが変数の値を1つ増加させる演算子であるのに対し、デクリメントは変数の値を1つ減少させる演算子。主に数値の変数やカウンターの値を減らす際に使用する。

  • 前置デクリメント (--a): 値を減少させた後の値を返す
let x = 10;
let y = --x;
console.log(x); // 結果は 9
console.log(y); // 結果は 9

--x が前置デクリメントで、x の値が減少した後に y に代入されている。そのため、x と y はともに 9 の値を持っている。

  • 後置デクリメント (a--): 値を返した後に値を減少させる 後置デクリメントを使った場合、値を返す順序が異なる
let p = 10;
let q = p--;
console.log(p); // 結果は 9
console.log(q); // 結果は 10

後置デクリメントの場合、まず p の値が q に代入され、その後に p の値が減少する。そのため、p は 9 に減少し、q は元の値の 10 を持っている。

繰り返し

名の通り同じ処理を繰り返す文。指定した条件がtrueの間処理が繰り返され、指定した条件から外れると処理を終了させる。ループとも呼ばれる。

while文

while文は、条件を満たす間繰り返しをする書き方になります。(while 〜する限り)

while (継続条件) {
  繰り返したい処理;
}
let count = 0;
while (count < 10) {
  console.log(`${count + 1}回目の表示です`); // 0から始まるの表示が1から始まるよう+1している
  count++; // 変数countに1を毎回足す
}
// 1回目の表示です
// 2回目の表示です
// 3回目の表示です
// 4回目の表示です
// 5回目の表示です
// 6回目の表示です
// 7回目の表示です
// 8回目の表示です
// 9回目の表示です
// 10回目の表示です

毎回変数countに数字が1ずつ追加されていってcount < 10が満たす間は繰り返される。

for文

for文は回数が決まった繰り返しに向いている。

  • 書き方
for (初期化; 継続条件; 最終式) {
  繰り返しする処理;
}
  • 動き方

    • 処理を始める前に初期化が実行される
    • 継続条件がtrue(真)の間、繰り返される
    • 最終式はブロック処理が終わるごとに実行される
  • 使用例

for (let count = 0; count < 10; count++) {
  console.log(`${count + 1}回目の表示です`);
}

// 1回目の表示です
// 2回目の表示です
// 3回目の表示です
// 4回目の表示です
// 5回目の表示です
// 6回目の表示です
// 7回目の表示です
// 8回目の表示です
// 9回目の表示です
// 10回目の表示です

逆順で繰り返す

1ずつ増えていくではなく、1ずつ減らしていくパターンで繰り返したいときにはデクリメント演算子を使用する。

for (let count = 10; count >= 0; count--) {
  console.log(`残り${count}`);
}

残り10
// 残り9
// 残り8
// 残り7
// 残り6
// 残り5
// 残り4
// 残り3
// 残り2
// 残り1
// 残り0

繰り返しからの脱出(break)&スキップ(continue)

  • break

中にif文を使って条件を指定して、その条件を満たす場合に繰り返しを中断する

for (let count = 0; count < 10; count++) {
  if (count == 5) {
    break;
  }
  console.log(`${count + 1}回目の表示です`);
}

// 1回目の表示です
// 2回目の表示です
// 3回目の表示です
// 4回目の表示です
// 5回目の表示です
  • continueを使ってスキップする

continueの場合は条件に合致したときはスキップし、繰り返しは継続する。

for (let count = 0; count < 10; count++) {
  if (count == 5) {
    continue;
  }
  console.log(`${count + 1}回目の表示です`);
}

// 1回目の表示です
// 2回目の表示です
// 3回目の表示です
// 4回目の表示です
// 5回目の表示です  6回目の表示がなくなっている!
// 7回目の表示です
// 8回目の表示です
// 9回目の表示です
// 10回目の表示です

繰り返しの中で繰り返しを使う

繰り返し分の中で、違う繰り返し文を使うことももちろん可能。

  • 掛け算1〜9の位を計算
for (let a = 1; a < 10; a++) {
  for (let b = 1; b < 10; b++) {
    console.log(`${a} × ${b} = ${a * b}`);
  }
}

繰り返しの中の繰り返しなので変数aもbも使える。

配列を使って繰り返しする

for~of文を使う(ES2015から利用可)

for~of文は配列から要素を1つずつ順番に取り出して繰り返しできる(ES2015から利用可)。

const members = ["a", "b", "c", "d", "e"];

for (let member of members) {
  console.log(`${member}さん`);
}

// aさん
// bさん
// cさん
// dさん
// eさん

for文を使って配列のインデックスを指定する

for~of文が使えない場合での以下のように配列のインデックスを指定できる。for文で定義した変数を活用する。

const members = ["a", "b", "c", "d", "e"];

for (let count = 0; count < 5; count++) {
  console.log(`${members[count]}さん`);
}

// aさん
// bさん
// cさん
// dさん
// eさん

(練習)5チームのリーグ戦の対戦表をつくる

  • A~Eまでの5チーム
  • A 対 Aなどのあり得ないパターンを排除したい

チーム名を配列にセットし、for〜of文をネストさせ、if文で同じ値だった場合はスキップさせる。しかしこれだとA 対 Aと同チームは排除できるが、A 対 BとB 対 Aのように同じ組み合わせは残ってしまう。

const teams = ["A", "B", "C", "D", "E"];

for (let team1 of teams) {
  for (let team2 of teams) {
    if (team1 == team2) {
      continue;
    } else {
      console.log(team1 + " 対 " + team2);
    }
  }
}

//  A 対 B
//  A 対 C
//  A 対 D
//  A 対 E
//  B 対 A
//  B 対 C
//  B 対 D
//  B 対 E
//  C 対 A
//  C 対 B
//  C 対 D
//  C 対 E
//  D 対 A
//  D 対 B
//  D 対 C
//  D 対 E
//  E 対 A
//  E 対 B
//  E 対 C
//  E 対 D

以下のでも同様

const teams = ["A", "B", "C", "D", "E"];

for (let team1 of teams) {
  for (let team2 of teams) {
    if (team1 != team2) {
      console.log(team1 + " 対 " + team2);
    }
  }
}

for文を使って以下のようにやれば実現できそう

const teams = ["A", "B", "C", "D", "E"];

for (let i = 0; i < teams.length; i++) {
  for (let j = i + 1; j < teams.length; j++) {
    console.log(`${teams[i]}${teams[j]}`);
  }
}


// A 対 B
// A 対 C
// A 対 D
// A 対 E
// B 対 C
// B 対 D
// B 対 E
// C 対 D
// C 対 E
// D 対 E

関数

  • 関数を作ることを「関数を定義する」という
  • プログラムの一部を繰り出して名前をつけるイメージ
function 関数名(引数1, 引数2, ...){
  実行される処理1;
  実行される処理2;
  ...

  return 戻り値;
}

関数名(引数1, 引数2, ...); // 呼び出し方

関数を自分で作るメリット

  1. プログラムの構造がわかりやすくなる プログラムを一括りにして名前をつけることでもあるため、そのプログラムが何をしているのかを理解しやすくなる。故に、命名が重要になる!

  2. 関数は何度も呼び出せる 関数は使い回すことができる。そのため重複したコードを書く必要がなくなり管理もしやすく、コードの記述量も減る。

関数の書き方

  • アロー関数式(ES2015からできるようになった記述)
    • =>を「アロー」と呼び()=>{}と組み合わせて関数を作る
const 変数名 = () => {
  関数内で実行する文
}
const test = () => {
  console.log("テスト");
};

アロー関数式の省略形

関数ブロック内で実行する処理が return 文だけだった場合、ブロックをあらわす { と } 、そして return も省略して次のように記述することができる

let 変数名 = (引数1, 引数2, ...) => 戻り値;

引数が一つで関数ブロック内で実行する処理が return 文だけだった場合

let 変数名 = 引数 => 戻り値;

使用例

let calcScore = (x, y) => x + y;

let result = calcScore(10, 20);
console.log(result);
// 30

関数の呼び出し

関数名();で呼び出す。引数を受ける場合は()内に値を入力する。

const test = () => {
  console.log("テスト");
};

test();

// テスト

引数を使う関数

()内に引数名を設定して引数を渡して関数を呼び出す。

const createLetter = (name) => {
  console.log(`${name}さん、こんにちは`);
};

createLetter("しらたま");
createLetter("きなこ");

// しらたまさん、こんにちは
// きなこさん、こんにちは

何度も同じ処理をする場合、引数を設定することで名前が変わっても同様の処理を行うことができる。

複数の引数を渡す

const createLetter = (name, age) => {
  console.log(`${name}さん、こんにちは`);
  console.log(`${age}歳の誕生日おめでとう`);
};

createLetter("しらたま", 6);
createLetter("きなこ", 4);

// しらたまさん、こんにちは
// 6歳の誕生日おめでとう
// きなこさん、こんにちは
// 4歳の誕生日おめでとう

functionで関数を作る(ES2015以前の場合)

function createLetter(name) {
  console.log(`${name}さん、こんにちは`);
}

createLetter("しらたま");
createLetter("きなこ");

// しらたまさん、こんにちは
// きなこさん、こんにちは

関数の中で関数を使う(テンプレート文字列 ※ES2015〜)

「テンプレート文字列」を使うと何度もconsole.log()を書く必要がなくなる - テンプレート文字列は``(バッククォーテーション)で囲んだ範囲に書いた文字列 - この範囲での改行やスペースはプログラムの結果に反映される - 長い文章を書く場合に有効な記述

`文章1
文章2`
  • 使用例
const createInvoice = (name, price) => {
  const msg = `${name}
お買い上げありがとうございます。
${price}円をお支払いください。`;
  console.log(msg);
};

createInvoice("しらたま", 1000);
createInvoice("きなこ", 800);

// しらたま様
// お買い上げありがとうございます。
// 1000円をお支払いください。

// きなこ様
// お買い上げありがとうございます。
// 800円をお支払いください。

関数リテラル

関数リテラルという方法で関数を定義することもできる。 以下のように書く。

let 変数名 = function(引数1, 引数2, ...){
  実行される処理1;
  実行される処理2;
  ...

  return 戻り値;
};

変数名(引数1, 引数2, ...); // 呼び出しは同じ

このように変数の中にfunctionを定義する。変数名は変数名となる。引数がない場合でもfunctionの後ろには必ず()の記述が必要な点に注意。

関数リテラルの使い所 関数リテラルでは関数に名前をつける必要がない。そのため1度しか使われない関数の場合、各々を1つずつ関数宣言として定義していく場合、関数名が他の関数や変数名と被ってしないかを気にしないといけなくなる。

しかし、関数リテラルを使えば関数名を指定せずに関数を定義できるためそのような心配がいらなくなる

一度しか利用されない関数は、例としてイベント処理やコールバック関数などで使用される。

ローカル変数

先ほど使った関数内のmsg変数はこのメソッド内でのみ呼び出せる。これをローカル変数という。

const createInvoice = (name, price) => {
  const msg = `${name}
お買い上げありがとうございます。
${price}円をお支払いください。`;
  console.log(msg);
};

console.log(msg);

// 呼び出そうとしてもReferenceError: msg is not defined(定義されていないため参照できない)となる

戻り値を返す関数

自作の関数で戻り値を返したい場合はreturn文を書く

  • 書き方
const 変数 = () => {
  関数で実行する処理;
  return;
};
function add(a, b) {
  return a + b;
}

console.log(add(1, 4));

// 5

returnがなくなると期待した値が出力されずundefinedとなる

function add(a, b) {
  a + b;
}

console.log(add(1, 4));

undefined

関数から複数の値を返す

returnを使うと1つの値しか戻せない。2つ以上の値を戻り値したい場合は、配列などで返すことができる。

function test(num1, num2, num3){
  const maxNum = Math.max(num1, num2, num3);
  const minNum = Math.min(num1, num2, num3);

  return [maxNum, minNum];
}

let numArray = test(80, 60, 70);

console.log("Max = " + numArray[0]);

// Max = 80

console.log("Min = " + numArray[1]);
// Min = 60

可変長引数で都度変わる引数の数に対応する

JavaScript では関数で引数を記述するときに、引数の前に '...' を付けると、呼び出し側から渡されてきたすべての値を要素とした配列が要素に格納される('...'を付けた引数を Rest Parameter と呼ぶ)。

呼び出し側で2つ引数がわたされても、それ以外の数が渡されても受け取って取り扱うことができる。

function 関数名(...引数){
  ...
}

使用例

function calcSum(...num) {
  let sum = 0;
  for (let i = 0 ; i < num.length ; i++){
    sum += num[i];
  }

  return sum;
}

calcSum(1, 2, 3);
>> 6

calcSum(1, 2, 3, 4, 5);
>> 15

通常の引数と組み合わせて使用することできる

function 関数名(引数1, 引数2, ...引数3){
  ...
}

JavaScriptでは設置した引数に過不足があってもエラーにならない

  • JavaScriptでは設置した引数に過不足があってもエラーにならない
  • 渡されてきた値が多かった場合は単に使われず、少なかった場合に値が格納されなかった引数は undefined となる
  • ない値に対して参照が行われるとエラーとなる

再帰関数

  • 再帰関数とは関数内で自分自身の関数を呼びだすこと
function test(){
  test();
}

test();
  • 無限ループにならないようにこの条件になったら自分自身の呼び出しを終了するような記述が必要
function test(){
  if (条件式) {
    test();
  }

  return;
}

test();

コールバック関数

コールバック関数は、他の関数内で使用される関数。コールバック関数は、ある特定のイベントが発生したときに呼び出されたり、他の関数の一部として実行されたりする。

const numbers = [1, 2, 3, 4, 5];

function processNumber(number) {
  console.log(number * 2);
}

numbers.forEach(processNumber);

上記のforEach メソッドは、コールバック関数を受け取り、それぞれの要素に対してその関数を実行する使用例。このように、JavaScript では別途定義した関数を別の関数を呼びだす時に引数に指定して渡すことができる。

コールバック関数が主に使われるケース コールバック関数は主に非同期処理を行う時に使用される。呼び出した関数の処理が終わったあとなどにコールバック関数として指定した関数を実行させたい場合に使われる。

例:「一定時間経過したあとに特定の処理を実行して欲しい場合」「ボタンがクリックされたときに実行して欲しい処理を指定する」

高階関数

高階関数は、他の関数を引数として受け取るか、関数を返す関数。これにより、より柔軟なコードを書くことができる。 例えば、以下の高階関数 multiplyBy は、数値を受け取り、それを掛け算する新しい関数を返している。

function multiplyBy(factor) {
  return function (number) {
    return number * factor;
  };
}

const double = multiplyBy(2); // double は掛け算2を行う関数
const triple = multiplyBy(3); // triple は掛け算3を行う関数

console.log(double(5)); // 10
console.log(triple(5)); // 15

オブジェクトを使って複数のデータを扱う

書き方

変数 = { data1 : 値a, data2 : 値b }

  • オブジェクトではデータをプロパティ名(キー)と値のペアで管理する
  • 一組のプロパティ名と値で表されるデータのことをプロパティと呼ぶ
  • ブジェクトでは複数のプロパティをまとめて管理することができる
  • プロパティの値には数値や文字列などのプリミティブ型の値のほかに、配列や関数といったオブジェクト型の値を指定することができる
  • 値として関数を記述したプロパティのことをメソッドとも呼ぶ

値の取り出し方

「変数名.プロパティ」
const person = {
  firstName: "A",
  lastName: "B",
  age: 20,
};

console.log(person.firstName);

// A
「変数名['プロパティ']」
const person = {
  firstName: "A",
  lastName: "B",
  age: 20,
};

console.log(person["firstName"]);

// A

プロパティの追加

let mySize = {
  width: 400,
  height: 300,
};

console.log(mySize);

myBox.color = 'Red'; // 新しいプロパティを追加

console.log(mySize);
>> {width: 400, height: 300, color: 'Red'}

存在しないプロパティ名を指定して値を代入することができる。

プロパティを削除

オブジェクトからプロパティを削除したい場合には delete 演算子を使う。

delete オブジェクト.プロパティ名
delete オブジェクト['プロパティ名']

以下のように削除されていることがわかる

let mySize = {
  width: 400,
  height: 300,
};

console.log(mySize);

myBox.color = 'Red'; // 新しいプロパティを追加

console.log(mySize);
>> {width: 400, height: 300, color: 'Red'}

delete mySize.color;  // プロパティを削除
console.log(mySize);
>> {width: 400, height: 300}

オブジェクトのプロパティを順番に取得する

for...in 文でオブジェクトのプロパティ名を順に取り出しながら繰り返し処理を行うことができる。

let cat = {
  name:'しらたま', 
  age:5,
  hobby:'寝る'
};

for (key in cat){
  console.log('cat.'+ key + '=' + cat[key]);
}

// cat.name=しらたま
// cat.age=5
// cat.hobby=寝る

プロパティの一覧を取得する(keys)

オブジェクトに含まれるプロパティ名の一覧を取得するには Object オブジェクトの keys メソッドを使う。引数に指定したオブジェクトのキーを配列で返してくれるメソッド。

Object.keys(オブジェクト)

let cat = {
  name: "しらたま",
  age: 5,
  hobby: "寝る",
};

let keyArr = Object.keys(cat);
console.log(keyArr);

// ["name", "age", "hobby"]

forEach メソッドをあわせて使って値を順に取得する

配列の要素を順に取得する Array オブジェクトの forEach メソッドをあわせて使うことでプロパティの値を順に取得することもできる。

let cat = {
  name: "しらたま",
  age: 5,
  hobby: "寝る",
};

let keyArr = Object.keys(cat);
keyArr.forEach(function (element) {
  console.log(cat[element]);
});

// しらたま
// 5
// 寝る

複数のオブジェクトを配列にまとめる

配列の要素にオブジェクト(ハッシュ)を入れた場合の値の取り出し方。 配列の要素数を指定した後に上記の取り出し方をすればOK!

const persons = [
  { firstName: "A", lastName: "B", age: 20 },
  { firstName: "C", lastName: "D", age: 50 },
];

console.log(persons[0]["firstName"]);
console.log(persons[1]["firstName"]);

// A
// C

プロパティの値の一覧を取得する(values)

オブジェクトに含まれるプロパティの値の一覧を取得するには Object オブジェクトの values メソッドを使う 。引数で渡したオブジェクトの値を配列で返してくれる。

Object.values(オブジェクト)

let cat = {
  name: "しらたま",
  age: 5,
  hobby: "寝る",
};

let keyArr = Object.values(cat);
console.log(keyArr);

//  ['しらたま', 5, '寝る']

プロパティの名前と値の一覧を取得する(entries)

オブジェクトに含まれるプロパティの名前と値のペアの一覧を取得するには Object オブジェクトの entries メソッドを使う。プロパティごとに配列で返してくれる。

Object.entries(オブジェクト)

let cat = {
  name: "しらたま",
  age: 5,
  hobby: "寝る",
};

let keyArr = Object.entries(cat);
console.log(keyArr);

プロパティ名と値のデータからオブジェクトを作成する(fromEntries)

※ fromEntries メソッドは ECMAScript 2019 (ES10) から導入

プロパティと値のデータからオブジェクトを作成するには Object オブジェクトの fromEntries メソッドを使う。

Object.fromEntries(オブジェクト)

反復処理可能なオブジェクトを引数に指定する(Array や Map など)。このメソッドを実行すると、指定されたオブジェクトをもとに新しい Object オブジェクトが生成される。

指定するオブジェクトは、データのペアが2つあるものでなければならない。1つ目の値はプロパティ名として使用され、2つ目の値はそれに対応するプロパティの値として格納される

let catArray = [
  ["name", "しらたま"],
  ["age", 5],
  ["hobby", "寝る"],
];

let catObj = Object.fromEntries(catArray);
console.log(catObj);

// {name: 'しらたま', age: 5, hobby: '寝る'}

ひとつしか値がない要素がある場合はundefinedになる

let catArray = [
  ["name", "しらたま"],
  ["age"],
  ["hobby", "寝る"],
];

let catObj = Object.fromEntries(catArray);
console.log(catObj);

// 'しらたま', age: undefined, hobby: '寝る'}

複数のオブジェクトを一つにまとめる(assign)

複数のオブジェクトを一つにまとめたるには Object オブジェクトの assign メソッドを使う。

Object.assign(コピー先オブジェクト, コピー元オブジェクト1, コピー元オブジェクト2, ...)

let objA = { a: 800 };
let objB = { b: 350 };
let objC = { c: 600 };

let allObj = Object.assign(objA, objB, objC);

console.log(allObj);

// {a: 800, b: 350, c: 600}

console.log(objA);
// {a: 800, b: 350, c: 600}

上記のように3つのオブジェクトを1つにまとめることができる。注意すべきなのは元のオブジェクトも変更が加えられるという点です。

もし、変更を加えたくない場合は以下のようにする。

let objA = { a: 800 };
let objB = { b: 350 };
let objC = { c: 600 };

let allObj = Object.assign({}, objA, objB, objC);

console.log(allObj);
// {a: 800, b: 350, c: 600}

console.log(objA);
// {a: 8
tenn00}
  • 同じプロパティ名があった場合は後のキーと値で上書きされる

配列なので繰り返し文で取得することもできる

const persons = [
  { firstName: "A", lastName: "B", age: 20 },
  { firstName: "C", lastName: "D", age: 50 },
];

for (let person of persons) {
  console.log(person["firstName"]);
}

Webページに組み込む

HTMLでJSファイルを読み込む

HTMLの中で<script src="ファイル名.js"></script>タグを使ってファイルを読み込む。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>サンプル</title>
  </head>
  <body>
    <h1>サンプル</h1>
    <input type="test" />
    <button>実行</button>
    <p>結果表示</p>
    <script src="test.js"></script> // HTMLの中にscriptタグをつけてjsファイルを読み込む
  </body>
</html>

ブラウザにhtmlタグをドラッグ&ドロップすると表示が確認できる。もちろんまだjsファイルには何も書いていないので見たの表示はHTMLの情報のみが記述されている。

HTMLの要素を選択する

  • JavaScriptでHTML操作をするには、まず対象となる要素を選択する
  • そのために使うのがquerySelector(クエリセレクター)メソッド
    • セレクタとは要素を選び出す時の指示になる文字列のこと
    • p要素であれば「'p'」、h1要素を選びたければ「'h1'」と指定する
  • querySelector(クエリセレクター)メソッド

書き方 let 変数 = document.querySelector('文字列') - 正式にはwindow.document.querySelector()だがwindowは省略できる - querySelector(クエリセレクター)メソッドは見つけた要素のElementオブジェクトを返すのでその後に使うために変数にいれておく

innerTextで要素の中のテキストにアクセスする

  • 目的の要素を表すElementオブジェクトが取得できれば、あとはそれが持つメソッドやプロパティを使って操作できる
  • 要素の中のテキストはinnerTextプロパティから利用できる
  • 書き方 変数.innerText 変数にはElementオブジェクトを入れた変数。innerTextは要素内部のテキストを表す

  • 使い方

test.js

const element = document.querySelector("p");
element.innerText = "文字を変更しました。";

p要素を取得してテキストが書き換わったことがわかる。

うまくいかない場合はブラウザのデベロッパーツールのコンソールに表示されるので、そこを確認。

入力ボックスからデータを受け取る

先ほど入力していたHTMLのinput要素に入力された値を受け取って表示する。

  • input要素 <input type="text">

これです

  • その他のinput要素抜粋

    • type="radio"
    • type="password"
    • type="checkbox"
  • 入力ボックスに入力した値をJavaScriptで利用するにはvalueプロパティを利用する <input type="text">のtextボックスに入力された値は変数.valueで取得する

const input = document.querySelector("input"); 
const element = document.querySelector("p");

element.innerText = input.value;
  1. inputをセレクタとしてドキュメントに問い合わせて、見つけた要素を新規作成した変数に入れる
  2. pをセレクタとしてドキュメントに問い合わせて、見つけた要素を新規作成した変数に入れる
  3. 変数inputの値を変数elementのinnerTextプロパティに入れる

しかし、まだこの状態だと「実行」を押しても何もなく、元々あった文字列も消えている。

ユーザーのアクションに反応するイベント

  • JavaScriptWebブラウザが読み込まれた瞬間に実行される
  • 実行した時点ではinput要素には何も入力されていない
  • 入力されていないinput要素から値を取得して、p要素のテキストとして読み込まれたため実行結果が消えている状態
  • これを解決するには「イベント」を使ってユーザーが何かをしたタイミングで実行させる必要がある

①ユーザーが実行ボタンをクリック(イベント)

② 連動してコードを実行させる

イベントと関数を結びつける

ElementオブジェクトのaddEventLisnerメソッドを使う

  • 書き方 変数.addEventListener("イベントタイプ", 関数); 1つめの引数にイベントタイプ、2つめの引数に関数を指定する。関数を指定する第2引数の書き方は、アローとブロックを指定する。

  • イベントタイプ 今回は'click'を指定する。

様々なイベントタイプがある

  • 第2引数の関数の書き方
変数.addEventListener("イベントタイプ", () => {
  実行したい処理;
});
  • 実演
const input = document.querySelector("input");
const button = document.querySelector("button");
const element = document.querySelector("p");

button.addEventListener("click", () => {
  element.innerText = input.value;
});

例外処理

プログラムの中でエラーが発生した場合、例外に対する処理を設定していない場合にはプログラムがその時てで強制終了してしまう。そのために例外処理を使う。

try...catch文を使った例外処理

以下のように計算をする際に数値だけではなく長整数が混ざってしまった場合は以下のようにエラーになる。

function sum(num1, num2){
  let sum = num1 + num2;
  return sum;
}

console.log('開始');

let result = sum(10, 8n);
console.log(result);

console.log('終了');

// 開始
// TypeError: Cannot mix BigInt and other types, use explicit conversions

このままだとプログラムが止まって終わってしまうので、適切に例外を捉えて処理を行う。

try...catch文の使い方

try{
  例外が発生する可能性がある文を記述
} catch(e) {
  例外をキャッチしたときに実行される処理
}
  • catch のあとの括弧の中に記述した変数 e (変数名は任意)には、例外がスローされたときに設定された値が格納される
  • 通常は Error オブジェクト(自分で例外をスローする場合には任意の値を指定できるので Error オブジェクトとは限らない)
function sum(a, b){
  let sum;
  try{
    sum = a + b;
  } catch(e) {
    console.error(e);
    return console.log('エラーになりました');
  }

  return sum;
}

console.log('開始');

console.log(sum(10, 8));
console.log(sum(10, 8n));

console.log('終了');

// 開始
// 18
// TypeError: Cannot mix BigInt and other types, use explicit conversions
// エラーになりました
// 終了

このようにエラーとなりつつも終了まで実行されていることがわかる。

try...catch...finally文を使った例外処理

try...catch...finally 文を使用すると、 try ブロックの中で例外がスローされたかどうかに関わらず、必ず最後に実行する処理を記述することができる。

try{
  例外が発生する可能性がある文を記述

} catch(e) {
  例外をキャッチしたときに実行される処理

} finally {
  最後に実行される処理
}

finally は、 try ブロックの処理が終わったあと、また例外がスローされた場合は catch ブロックの処理が終わったあとで必ず実行される。

function calc(a, b) {
  let sum;
  try {
    sum = a + b;
  } catch (e) {
    console.error(e);
    return console.log("エラーになりました〜");
  } finally {
    console.log("わいは何がなんでも登場するんや");
  }
  return sum;
}

console.log("開始");

console.log(calc(1, 9));
console.log(calc(1, 9n));

console.log("終了");

// 開始
// わいは何がなんでも登場するんや
// 10
// TypeError: Cannot mix BigInt and other types, use explicit conversions
// エラーになりました〜
// わいは何がなんでも登場するんや
// 終了

catchの部分でreturn console.log("エラーになりました〜");とreturnで返して終わってもわいは何がなんでも登場するんやという文字が出現してきます。1回目の通常の処理にももちろん登場

try ブロックや catch ブロックの中で retnrn 文や break 文などが実行されて処理が try...catch...finally 文から別のところへ移動する場合であっても、移動が行われる前に実行

正規表現

※ここではあくまでJavaScript正規表現書き方を記述するので正規表現そのものはまとめていません

正規表現リテラルを使用

/パターン/
/パターン/フラグ

このようにスラッシュで囲って使う

RegExpコンストラクタを使用

RegExp オブジェクトのコンストラクタを使用して正規表現オブジェクトを作成する方法

new RegExp(パターン[, フラグ])
  • 1 番目の引数には正規表現リテラルを指定orパターンを表す値を文字列として記述
  • フラグを設定する場合は 2 番目の引数に一つまたは複数のフラグを文字列として記述
  • 正規表現のパターンがあらかじめ決まっているのであれば基本的に正規表現リテラルを使って正規表現オブジェクトを作成するのがシンプル

JavaScriptで使えるフラグ

正規表現ではフラグ(またはオプション)を設定することで文字列がパターンにマッチする仕組みを変更することができる。

/パターン/フラグ
フラグ フラグ名 説明
g Global 通常、正規表現は最初に一致した文字列だけを探しますが、g フラグを設定するとすべての一致を探します。
i IgnoreCase パターンで大文字と小文字を区別しなくなります。例えば、/a/ の場合は /A/ にもマッチします。
m Multiline 複数行モード。メタ文字の ^ と $ が文字列の先頭と末尾だけでなく、各行の先頭と末尾にもマッチするようになります。
s DotAll メタ文字の . は通常、改行文字にはマッチしませんが、s フラグを設定すると改行文字にもマッチします。
u Unicode 通常、正規表現で \uhhhh は UTF-16 のコードユニットを表しますが、u フラグを設定すると \u{hhhh} や \u{hhhhh} を Unicode コードポイントとして使用できます。
y Sticky lastIndex プロパティが示す位置より後ではなく、lastIndex プロパティの位置からのみ一致するかどうかを判定します。

文字列が正規表現とマッチするかテスト

RegExp オブジェクトのインスタンスメソッドの test は、対象の文字列が正規表現とマッチするかテストしtrue または false を返す。

正規表現オブジェクト.test(文字列)
let regexp = /cat/;
let str1 = 'My cat likes to nap in the sun.';
let str2 = 'I adopted a new kitten last week.';

console.log(regexp.test(str1));
// true

console.log(regexp.test(str2));
// false

正規表現にマッチした文字列を取得

execメソッド exec メソッドは、対象となる文字列が正規表現とマッチした場合に、マッチした文字列を取得する

正規表現オブジェクト.exec(文字列)

[郵便番号を抜き出す例]

let regexp = /[0-9]{3}-[0-9]{4}/;

let text = "My postal code is 123-4567.";

console.log(regexp.exec(text));

// ['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]
  • 引数に指定した文字列が正規表現とマッチした場合は、マッチした文字列が格納された配列を返す
  • マッチしなかった場合は null を返す
  • 戻り値の配列にはインデックス 0 の要素にパターン全体にマッチした文字列、インデックス 1 以降の要素には設定したキャプチャグループで囲んだパターンにマッチした文字列が格納される

抜き出した値を取得するには配列なので以下のようにする

let regexp = /[0-9]{3}-[0-9]{4}/;

let text = "My postal code is 123-4567.";

let result = regexp.exec(text);

console.log(result[0]);

// 123-4567

エラーの場合には以下のようにな値が配列の要素0に格納されているのがわかる

let regexp = /[0-9]{3}-[0-9]{4}/;

let text = "My postal code is 111.";

let result = regexp.exec(text);

console.log(result[0]);

// Uncaught TypeError: Cannot read properties of null (reading '0')

エラーになってしまうので条件分岐または例外処理をすると良い

let regexp = /[0-9]{3}-[0-9]{4}/;

let text = "My postal code is 111.";

let result = regexp.exec(text);

if (result !== null) {
  console.log(result[0]);
} else {
  console.log("Not Match.")
}


// Not Match.

パターンにキャプチャグループが含まれる場合

キャプチャグループ(Capture Group)は、正規表現正規表現パターン)内で一部のパターンにマッチしたテキストを個別に取り出すための仕組み。キャプチャグループは、正規表現内の括弧 () で囲まれた部分にあたる。

正規表現パターンの一部にマッチした文字列を個別に取得したり、後で参照したりする場合に使用される

const pattern = /(\d{2})-(\d{2})-(\d{4})/;
const date = "12-31-2022";

const match = pattern.exec(date);
if (match) {
  const day = match[1]; // キャプチャグループ1から日を取得
  const month = match[2]; // キャプチャグループ2から月を取得
  const year = match[3]; // キャプチャグループ3から年を取得

  console.log(match[0]); // 12-31-2022
  console.log("Year: " + year); // Year: 2022
  console.log("Month: " + month); // Month: 31
  console.log("Day: " + day); // Day: 12
}

マッチした文字列の位置を参照

exec メソッドを実行して文字列が正規表現にマッチした場合、マッチした文字列の先頭文字のインデックスが戻り値として取得した配列の index プロパティに設定されいる。

これです:['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]

let regexp = /[0-9]{3}-[0-9]{4}/;

let text = "My postal code is 123-4567.";

console.log(regexp.exec(text));

// ['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]

index: 18のように文字列の何個目の要素でマッチしているかがわかる。プロパティなので.indexで取り出せる。

let regexp = /[0-9]{3}-[0-9]{4}/;

let text = "My postal code is 123-4567.";

let result = regexp.exec(text);

// ['123-4567', index: 18, input: 'My postal code is 123-4567.', groups: undefined]

console.log(result.index);

// 18

正規表現にマッチした文字列のインデックスを取得

String オブジェクトのインスタンスメソッド search は、対象の文字列が正規表現とマッチした場合、マッチした最初の文字列の先頭文字のインデックスを返す。

文字列.search(正規表現オブジェクト)

let regexp = /[0-9]{3}-[0-9]{4}/g;

let text1 = "My postal code is 123-4567.";
let text2 = "123-4567 is my postal code.";

console.log(text1.search(regexp));
// 18

console.log(text2.search(regexp));
// 0

このように正規表現にマッチした文字列のインデックスを返してくれている。該当箇所が二箇所あっても最初にマッチしたインデックスしか返さないことに注意する!

正規表現にマッチするものがない場合

-1という値が返ってくる。

let regexp = /[0-9]{3}-[0-9]{4}/g;

let text1 = "My postal code is 123-4567.";
let text2 = "My postal code is nothing.";

console.log(text1.search(regexp));
// 18

console.log(text2.search(regexp));
// -1

正規表現にマッチしたすべての文字列を取得

文字列.match(正規表現オブジェクト) - String オブジェクトの match メソッドは、正規表現にマッチした文字列を配列で取得正規表現にgフラグ必要) - 正規表現のパターンにキャプチャグループが含まれていた場合は、キャプチャグループにマッチした文字列も併せて取得

let regexp = /[0-9]{3}-[0-9]{4}/g;

let text1 = "My postal code is 123-4567. My postal code is 765-4321.";

result = text1.match(regexp);

console.log(result);
// ['123-4567', '765-4321']

console.log(result[0]);
// 123-4567

console.log(result[1]);
// 765-4321

グローバル関数

parseInt関数(文字列を整数に変換)

parseInt(文字列 [, 基数])
  • JavaScript のグローバル関数のひとつである parseInt は文字列を整数に変換した値を返す
  • 対象の値が文字列でない場合は文字列に変換したあとで整数に変換される
  • (Number オブジェクトにグローバル関数の parseInt 関数と同じ動作をする Number.parseInt メソッドがある)
  • 第一引数に指定した文字列を整数に変換
  • 第二引数(省略可能)で整数の基数を指定(10進数の場合は10)。 2 から 36 までの数値で指定可能
  • 初めの文字が数値に変換できない場合は NaN
  • 文字列の先頭にある空白を取り除く
  • 先頭に + または - がある場合は符号として扱う
  • 数値でない文字が現れる直前までの文字列を整数に変換
parseInt("20", 10);
// 14

parseInt("  -100cm", 10);
// -100

parseInt("Hi", 10);
// NaN

parseInt('100', 2);
// 4

parseFloat関数(文字列を浮動小数点数に変換)

parseFloat(文字列)
  • 引数に指定した文字列を浮動小数点数に変換
  • 引数が文字列でない場合は文字列に変換したあとで浮動小数点数に変換
  • parseInt 関数とは違い paseFloat 関数では0xや 0Xで始まる文字は 16 進数の文字とは認識されない
parseFloat("20");
// 20 数値はそのまま

parseFloat("  1.2345cm");
// 1.2345

parseFloat("3.2e2");
// 320.0

parseFloat("Hello2024");
// NaN

isFinite関数(値が有限か無限かを判定する

isFinite(値)

isFiniteは対象の値が有限の値か無限の値かを調べてtrue または false を返す

  • Number オブジェクトでisFiniteメソッドがありより厳格に有限の数値かどうかを調べられる
  • 引数に指定した値が有限の値かどうかを調べ、値が NaN ・正の無限大( Infinity )・負の無限大( -Infinity )であれば false 、それ以外は true を返す
  • 値が数値型ではない場合はまず数値型に変換されたあとで調べる
isFinite(Infinity);
// false

isFinite(-Infinity);
// false

isFinite(NaN);
// false

isFinite(20);
// true

isFinite(0);
// true

isFinite(-5);
// true

isFinite('Hi'); 
// false 数値に変換されるとNaN

isFinite('100'); 
// true 数値に変換される

isFinite(''); 
// true  数値に変換すると 0

DOM操作

ながくなってきたので別記事にしています。

hirano-vm4.hatenablog.com

イベント処理

こちらも記事が長くなってきたので、別記事にまとめています。

hirano-vm4.hatenablog.com

日付・時刻・曜日を扱う方法【Dateオブジェクト】

こちらも記事が長くなってきたので、別記事にまとめています。

hirano-vm4.hatenablog.com

MDN web docs

MDN Web Docs(Mozilla Developer Network Web Docs)は、Mozillaが提供するウェブ開発に関する公式かつ包括的なサイトです。

MDNは、Web技術やプラットフォームに関する情報、ガイド、リファレンス、チュートリアルなどを提供してくれているのでさらにステップアップしていくために必須なサイトになるので最後にリンクを貼って終わりたいと思います。

JavaScript | MDN

【対策はfreezeメソッド】Rubyの定数は再代入・変更ができるので注意が必要

はじめに

前提

まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の初学者になりますのでご注意ください。

学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。

現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。

hirano-vm4.hatenablog.com

Rubyの定数は書き換えができてしまうので注意が必要

学習を進めていく中で定数を扱うタイミングがありました。

「定数」と文字だけみると変更できない印象を持ちますが、Rubyにおける定数は警告はでるものの、変更ができてしまうため、変更されたくない場合は対策が必要になります。

特に再代入できるだけでなくミュータブルなオブジェクト(文字列・配列・ハッシュなど)であれば定数の値を変更できてしまうので注意しなければならないことを学びました。

この記事では定数が変更できてしまう点について自分でコードを打っていろいろと確認してみたので、自己学習の記録も兼ねてアウトプットしている記事になります。

結論:freezeメソッドでオブジェクトを凍結(内容の変更を禁止)する

結論は「freezeメソッドを使ってオブジェクトを凍結する」になります。凍結されたオブジェクトを変更しようとすると例外 FrozenError を発生させることができます。

instance method Object#freeze

ただし、配列やハッシュを使う場合は使い方に注意が必要だと感じました。理由はこれ以降の対策の部分で解説したいと思います。

そもそもなぜRubyの定数は変更ができてしまうのか調べてみた

理由を検索していたらRubyを作った松本行弘氏がQuoraの質問で以下のように回答で以下のように解説していました。

Q.なぜRubyでは定数も再代入可能で、わざわざfreezeを使わないとだめになっているのですか?ハナから再代入不可にしなかった理由は?

この質問は、定数という言葉が示すと考えられるふたつの役割が混同されています。そして、Rubyではこのふたつの役割は明確に異なるものです。

第一の役割は、参照している値(オブジェクト)が変化しない、というものです。Rubyの定数はこの役割だけを持ち、大文字から始まる名前は定数として、その参照先を変えないことになっています。質問のうち、「再代入可能で、ハナから再代入不可にしなかった」というのはこの役割を示しています。

これは確かにその通りで、Rubyでは定数に再代入すると、警告こそ出力されますが、それを無視さえすれば、定数の値を書き換えることができます。

なぜそうなっているかというと、Rubyをアプリ組込みに利用して、たとえばRubyを組み込んだエディタを開発した場合、Rubyで記述した設定ファイルに書いてある定数が書換不能でエラーになってしまった場合、設定ファイルとしての使い勝手が悪くなることが想定されたからです。そこで、定数であるにも関わらず、書き換え禁止は警告のみにとどめ、やろうと思えば書き換えられるが紳士協定として、定数とするという方針にしました。

定数のもうひとつの意味は、「参照先が書き換わらない」という意味です。Cのconstはこちらの意味も持っていますが、多くのオブジェクトが書換え可能(mutable)なRubyでは、定数にはこの役割は与えられていません。定数の参照先のオブジェクトが書き換わるのを禁止したければ、「わざわざfreezeを使わないとだめ」です。これはRubyの「定数」とは無関係です

定数には

1.「参照している値(オブジェクト)が変化しない」

2.「参照先が書き換わらない」

の2つの役割があり、Rubyの定数は「参照している値(オブジェクト)が変化しない役割」だけを持っているため再代入や変更ができると説明されていました。

なるほど。難しいけど興味深い…。

定数の変更ができてしまう事例と対策

クラスをfreezeして変更を防ぐ事例

class Cafe
  COFFE_PRICE = 300

  COFFE_PRICE = 500
end

p Cafe::COFFE_PRICE

#=> warning: already initialized constant Cafe::COFFE_PRICE
#=> warning: previous definition of COFFE_PRICE was here
#=> 500

実行してみると再代入後の値が返って変更ができていることが確認できます。

クラス外からも変更できました。

class Cafe
  COFFE_PRICE = 300
end

Cafe::COFFE_PRICE = 500
p Cafe::COFFE_PRICE

#=> warning: already initialized constant Cafe::COFFE_PRICE
#=> warning: previous definition of COFFE_PRICE was here
#=> 500

クラス自体をfreezeすると変更を防止できる

class Cafe
  COFFE_PRICE = 300
end

Cafe.freeze
Cafe::COFFE_PRICE = 500
p Cafe::COFFE_PRICE #=> can't modify frozen #<Class:Cafe>: Cafe (FrozenError)

しかし、私が学習しているフィヨルドブートキャンプでメンターもしていて、有名なチェリー本を書いている伊藤さんによれば「Rubyの場合、普通は定数を上書きする人はいないためクラスをfreezeまでする例は少ない」とのことです。

しかし、変更できるという点は知っておく必要はありそうです。

定数(配列)が変更されてしまう例と対策

定数に配列を入れた場合で実際にコードを入力してみると以下のように変更できることが確認できました。

MEMBER = ['Foo', 'Bar', 'Baz']

MEMBER[0].upcase!

p MEMBER #=> ["FOO", "Bar", "Baz"]

追加もできる

MEMBER = ['Foo', 'Bar', 'Baz']

MEMBER.push('Piyo') 

p MEMBER #=> ["Foo", "Bar", "Baz", "Piyo"]

削除もできる

MEMBER = ['Foo', 'Bar', 'Baz']

MEMBER.pop

p MEMBER #=> ["Foo", "Bar"]

freezeメソッドを使って凍結すると?

freezeを使って凍結すると変更に対してFrozenErrorが返ってくるようになる。

MEMBER = ['Foo', 'Bar', 'Baz'].freeze

MEMBER.push('Piyo')

p MEMBER #=> can't modify frozen Array: ["Foo", "Bar", "Baz"] (FrozenError)

しかし、この状態ではまだ完全に変更を防ぐことができているわけではないことに注意!

配列やハッシュをfreezeしても配列・ハッシュそのものは凍結できても各要素については凍結されていない。

MEMBER = ['Foo', 'Bar', 'Baz'].freeze
 
MEMBER[0].upcase!

p MEMBER #=> ["FOO", "Bar", "Baz"]

このように変更できることがわかります。要素に対する変更も防ぎたい場合は以下のように、各要素に対してもfreezeする必要があります!

MEMBER = ['Foo', 'Bar', 'Baz'].map(&:freeze).freeze

MEMBER[0].upcase!

p MEMBER #=> can't modify frozen String: "Foo" (FrozenError)

定数(ハッシュ)が変更されてしまう例と対策

追加できる

FILE_TYPE = {
  'file' => '-',
  'directory' => 'd',
  'link' => 'l'
}

FILE_TYPE.store('Foo', 'Bar')

p FILE_TYPE #=> {"file"=>"-", "directory"=>"d", "link"=>"l", "Foo"=>"Bar"}

削除もできる

FILE_TYPE = {
  'file' => '-',
  'directory' => 'd',
  'link' => 'l'
}

FILE_TYPE.delete('link')

p FILE_TYPE #=> {"file"=>"-", "directory"=>"d"}

変更もできる

FILE_TYPE = {
  'file' => '-',
  'directory' => 'd',
  'link' => 'l'
}

FILE_TYPE['link'].upcase!

p FILE_TYPE #=> {"file"=>"-", "directory"=>"d", "link"=>"L"}

freezeすると凍結できるが要素までは防げない

ハッシュ自体は凍結されている

FILE_TYPE = {
  'file' => '-',
  'directory' => 'd',
  'link' => 'l'
}.freeze

FILE_TYPE.delete('file')

p FILE_TYPE #=>  can't modify frozen Hash: {"file"=>"-", "directory"=>"d", "link"=>"l"} (FrozenError)

要素に対しての変更はできてしまう

FILE_TYPE = {
  'file' => '-',
  'directory' => 'd',
  'link' => 'l'
}.freeze

FILE_TYPE['link'].upcase!

p FILE_TYPE #=> {"file"=>"-", "directory"=>"d", "link"=>"L"}

ハッシュの場合はeachしても長ったらしいですし、mapは配列の戻り値になっていまいます。以下のように要素ごとにfreezeするのもなんか不恰好。

FILE_TYPE = {
  'file' => '-'.freeze,
  'directory' => 'd'.freeze,
  'link' => 'l'.freeze
}.freeze

FILE_TYPE['link'].upcase!

p FILE_TYPE #=> can't modify frozen String: "l" (FrozenError)

transform_valuesという便利なメソッドを発見したので以下のようにもできました。

transform_valuesはすべての値に対してブロックを呼び出した結果で置き換えたハッシュを返します。キーは変化しません。

Hash#transform_values (Ruby 3.2 リファレンスマニュアル)

FILE_TYPE = {
  'file' => '-',
  'directory' => 'd',
  'link' => 'l'
}.transform_values(&:freeze).freeze

FILE_TYPE['link'].upcase!

p FILE_TYPE #=> can't modify frozen String: "l" (FrozenError)

最後に

以上、Rubyの定数について深掘りしてみました。

今回調べてみて定数には

「参照している値(オブジェクト)が変化しない」

「参照先が書き換わらない」

という2つの役割がそもそもあり、Rubyの定数は 「参照している値(オブジェクト)が変化しない役割 だけを持っているため、再代入や変更ができる」 という理由を確認することができました。

またその対策にfreezeメソッドを使う方法があるが、ハッシュや配列に対してfreezeしても各要素には凍結が適用されないことにも注意が必要だと感じました。

【merge済】プログラミング初心者がOSS活動に挑戦してIssue→PRまでやってみた

はじめに

前提

まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。

現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。

hirano-vm4.hatenablog.com

何が伝えたいかというと、まだ学習中の身である私が、OSS活動に参加し、GitHubでIssue・PRをOSSの開発者に送る経験をしたということです。

OSS活動というと「技術力と経験に長けた方々がひしめく異世界の活動」で私のような学習中のレベルの人間は参加するべきはないというイメージを持っていました。

しかし、そんなことはなくOSS活動は誰でも参加できる活動」であることを体験を通して感じることができました。

この体験をアウトプットすることで、OSS活動に興味があっても、まだ自信のない学習中の方やビギナーの方の背中を押せる内容になれば良いなという思いを込めて発信したいと思います。

OSS活動とは

オープンソースソフトウェア(Open Source Software)は、頭文字を取ってOSSと略されます。作成者がソースコードを無償で公開していて、利用や改変、再配布が自由に許可されているソフトウェアのことを言います。

またOSS活動とは、オープンソースソフトウェアの開発と維持管理に取り組む活動です。

OSS活動には、新しいオープンソースソフトウェアの開発、既存の製品のメンテナンス、関連するドキュメンテーションの更新など、さまざまな取り組みが含まれます。


OSSは、誰でも自由に改良し、再配布できるという特徴があるため、ユーザーの中から誰かがソースコードのバグを見つけ、改善して修正するというサイクルが世界中で行われています。

この活動により、ソフトウェアの安定性が向上し、低コストで高品質な開発が実現できるというメリットがあるそうです。


私が現在学んでいるプログラミング言語の1つであるRubyも、ブログなどがつくれる有名なWordPressOSSです。

我々が使うものは多くのOSSの上に成り立っていると言っても過言ではありません。

イベントの背景・参加の動機

参加しているフィヨルドブートキャンプオンラインプログラミングスクールのフィヨルドブートキャンプとOSS Gateのコラボレーション企画開催!!というイベントがあり、これは参加するしかない!

と思い立って申込開始と同時に申し込みしました😄

OSS Gateさんは、OSS開発に参加する「入り口」を提供する取り組みをしている団体です。

OSS開発に未参加の人を参加する人へ、少し参加したことがある人を継続的に参加する人へ。そうやってOSS開発に参加する人を継続的に増やしていく。それが「OSS Gate」の目的です。


また私は元々ブロックチェーンの技術にも興味があり、学習のアウトプットも兼ねてブロックチェーンのノードを作って運用をしています。

ブロックチェーンは、運営がいなくても成り立つ非中央集権的な仕組みとなっています。なので開発などはOSSとなっていて、まさにOSS活動に支えられているのでこの活動に興味がありました。

イベントの概要

ワークショップやセッションの内容

当日は以下のようなスケジュールで行われました。

興味深いトピックと学びのポイント

このイベントに参加してみて1番頭に残ったのは「それぞれができることを、できる範囲で貢献する」という言葉です。

冒頭でもお伝えしたように、私はOSS活動というと「技術力と経験に長けた方々がひしめく異世界の活動」で私のような学習中のレベルの人間は参加することができない、というイメージを持っていました。同じような印象を持っている方もきっと多いのではないでしょうか?

しかし、さまざまなレベルの人がいて・状況や環境(時間的制約など)もさまざまです。「各々ができる範囲で貢献する」という言葉を聞いて私の中で一気にハードルが下がりました。

またプログラミングのレベルがビギナーであれば、ビギナーにしか気付けないこともあります。

OSS活動の流れ

1.まずはWorklogを作成

OSS Gate Workshopで用意されているWork logを作成。このワークショップでは作業の記録や気付きなどを細かく投稿しながら進め、適宜アドバイスをもらいながら進めていきます。

リンクから飛んでいただけると雰囲気を掴んでもらえると思います。

2.OSSの選定

まずは今回貢献するOSSを選定します。自分の使っている身の回りのものもOSSである可能性があります。ブラウザやエディターの拡張機能や、Rubyのgemなどももちろん対象です。

私はRuby on Railsの言及機能(モデルの自己結合&多対多のアソシエーション)実装のプラクティスの歓迎要件になっていた、「文字列にURLが含まれていたら自動でハイパーリンクになる」という機能を実装するために使ったrails_autolinkを選定しました。

3.ライセンスの確認

選定したらOSSであるかのライセンスを確認します。公式サイトやGitHubを確認します。私が選定したgemのライセンスはGitHub上で確認しました。

今回私の選んだOSSMIT Licenseということがわかりました。MIT LicenseOpen Source Initiative で検索しでてくればOSSということになります。

Licenseの中にも以下の条件に従う限り、無償で提供されます。本ソフトウェアおよび関連する文書ファイル(以下、「ソフトウェア」と呼びます)のコピーを取得した任意の個人に、ソフトウェアを制限なく扱い、使用、複製、変更、統合、公開、配布、サブライセンス許諾、販売する権利を含むがこれに限定されない権利を付与します。…(省略)という旨の表記が確認がとれました。

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:(一部抜粋)

4.ユーザーとしてOSSを利用して気付きを得る

まずはユーザーとしてOSSを利用します。その中で何か改善できることはないか、問題がないかを確認していきます。

ひとまずGitHubのREADME通りに進めていきます。そこでREADMEの指示通りに動かしてもエラーになる、意図した挙動にならない等がでてくればIssueの候補になります。

5.気付きをIssueやPRという形で貢献する

貢献できそうな内容があればそれをまとめてIssueを作って投稿します。

エラーになるまでに行った手順、またエラーであればそれぞれの実行環境によって挙動が変わる可能性もあるので、エラーになった自分自身の実行環境についてもしっかり具体的に伝えます。

意外と「具体的」というのが難しく、「無意識に前提だと思い込んでいると、つい伝えない情報がでてしまうので注意しましょう」と講義の中でもアドバイスがありました。相手にエスパーさせなくてもエラーの再現ができるよう情報を過不足なく伝えることが大切とのことでした(OSのバージョンetc)。


貢献の方法は状況についての報告をするIssueでも良いですし、改善できる内容であればPRを出してみても良いとのことでした。

今回はOSS Gateということでまず日本語でIssueの内容を書いてそれをレビューしてもらい、ブラッシュアップしてから英語に翻訳してコントリビュートしました。

初めてのOSSコントリビューション

Issueの報告とPR

今回私が選んだrails_autolinkに特にエラーは見受けられなかったので、READMEの改善提案をしました。

現状のREADMEは以下のように使用例としてコードと実行結果のみが書かれています。

require 'rails_autolink'

auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
#     say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"

auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
# => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
#     or e-mail david@loudthinking.com"

auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
# => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"

auto_link("Go to http://www.rubyonrails.org <script>Malicious code!</script>")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> "

auto_link("Go to http://www.rubyonrails.org <script>alert('Script!')</script>", :sanitize => false)
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> <script>alert('Script!')</script>"

post_body = "Welcome to my new blog at http://www.myblog.com/.  Please e-mail me at me@email.com."
auto_link(post_body, :html => { :target => '_blank' }) do |text|
  truncate(text, :length => 15)
end
# => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.

もちろん、「読めばいいじゃん!」と言われればその通りですが何ができるのかを理解するのに少し時間がかかります。また一つずつが別の例なのか、手順なのかパッとみて判断できない状況のように感じました。

とくに私のようなビギナーであっても、瞬時にこのgemの魅力・実現できることが理解できるREADMEだと助かるな〜と感じたのでREADMEの改善提案することにしました。

私が作った改善提案は以下のようになりました。

基本的な使い方

require 'rails_autolink'

auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
#     say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"

URLのみをリンクに変換する

auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
# => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
#     or e-mail david@loudthinking.com"

メールアドレスのみをリンクに変換する

auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
# => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"

HTMLタグをサニタイズせずにリンクを生成する

## デフォルトではHTMLタグをサニタイズして、悪意のあるコードから保護
auto_link("Go to http://www.rubyonrails.org <script>Malicious code!</script>")
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> "

## :sanitize => falseをオプションにつけることでサニタイズさせない
auto_link("Go to http://www.rubyonrails.org <script>alert('Script!')</script>", :sanitize => false)
# => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> <script>alert('Script!')</script>"

リンクのカスタマイズとテキストの短縮

post_body = "Welcome to my new blog at http://www.myblog.com/.  Please e-mail me at me@email.com."
auto_link(post_body, :html => { :target => '_blank' }) do |text|
  truncate(text, :length => 15)
end
# => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.

このように簡単に1文を添えるだけで何ができるgem なのか瞬時に判断して必要な部分だけ確認することができます。

これもビギナーだからこそ感じる(楽したいだけ?😅)部分でもあるのかな〜なんて感じました。

これを英語に翻訳してIssueとしました。またサポーターの方から、今回はREADMEの修正で差分がでるほうが確認しやすいのでPRという形で出してみては?とアドバイスをもらいPRとしても提出してみました!

mergeされるかどうか見守っていきたいと思います(ドキドキ)。

今回の成果と貢献

提出したIssue

github.com

提出したPR

github.com

2023/10/19追記 無事mergeされました

無事mergeされました!READMEが少し見やすくなりました😊貢献できたことが嬉しいですね〜! 本当に良い経験になりました!

Merge pull request #86 from hirano-vm4/update_README · tenderlove/rails_autolink@2bcb324

感想とまとめ

今回、貴重な機会をいただいたフィヨルドブートキャンプOSS Gateの皆様に感謝です!本当にありがとうございました。

そして、挑戦した自分を少しだけ褒めたいと思います。何事も一歩踏み出してやってみることが大切だと思うので、できる範囲でこれからも挑戦していきたいと思います!


今回の参加を通じて伝えたいメッセージは、OSS活動は誰でも参加できる活動である」ということです。

OSS活動は「各自ができる範囲で貢献する活動」です。私自身、まだ学習途中で現状はビギナーレベルです。

しかし、ビギナーであるからこそ感じること・気づきを共有することで、プロフェッショナルの方々はより専門的な分野に集中できます。

このように、いろんな人が協力し合って成り立つのがOSS活動で、そしてこれが魅力なのかもしれません。

異なるバックグラウンドやスキルを持つ人々が協力し、成り立つ世界って素晴らしいと感じると同時に、恩恵を多大に享受していることに気付かされた1日でした。

参考文献とリソース

Rubyでクラスメソッドをprivateしても呼び出せるので注意が必要

はじめに

私は現在、フィヨルドブートキャンプで、未経験からプログラミング学習をしているものになります。

この記事は、自分自身が学習したことをまとめ、アウトプットすることを目的として書いていますので、間違いがある可能性がありますのでご注意ください。

classでprivate以下に記述したとしてもクラスメソッドはprivateにならない

Rubyでクラス内にprivateを記述し、その配下にクラスメソッドを定義しても外部から呼び出せてしまうので注意が必要です。

class Cat
  private
  
  def self.hello
    'meow'
  end
end

Cat.hello #=> "meow"

このように呼び出せてしまいました。

privateメソッドが有効なのはインスタンスメソッドだけです。

クラスメソッドをprivateの範囲にいれるには2つ方法があります。

方法1:class << self構文を使う

class Cat
  class << self
    private

    def hello
      'meow'
    end
  end
end

Cat.hello #=> private method `hello' called for Cat:Class (NoMethodError)

方法2: private_class_methodを使う

class Cat
  def self.hello
    'meow'
  end
  private_class_method :hello
end

Cat.hello #=> private method `hello' called for Cat:Class (NoMethodError)

クラスメソッドを定義した後にpraivate_class_methodでprivateのメソッドを指定します。

意図しないメソッドの可視性に注意したい

自分で呼び出せると思っていた範囲以上にメソッドが呼び出せてしまうのは意図しないエラーやトラブルにつながる可能性もあるので基礎知識として知っておかなければならないことを勉強していて改めて感じました!

日々勉強ですね🔥

rubyで正規表現で扱う時に%rを使うと便利

はじめに

私は現在、フィヨルドブートキャンプで、未経験からプログラミング学習をしているものになります。

この記事は、自分自身が学習したことをまとめ、アウトプットすることを目的として書いていますので、間違いがある可能性がありますのでご注意ください。

ruby正規表現を使ってURLを抜き出す際にスラッシュが多くてみにくい問題

学習しているプラクティスの中で、文字列中の特定のURL(http://localhost:3000/reports/日報のid)を抜き出す場面がありました。

String#scan (Ruby 3.2 リファレンスマニュアル)メソッドを使ってhttp://localhost:3000/reports/日報のidを抜き出すコードは以下のようになります。

content.scan(/http:\/\/localhost:3000\/reports\/\d+/)

これでも実現はできるのですが、どうしても/(スラッシュ)が多くなるのでその都度、エスケープの\(バックスラッシュ)が多くなりとても見にくいです\(^o^)/

%rを使った書き方にするととても見やすい

書式は以下の通りです。

%r{パターン}

これを先ほどの例に当てはめると以下のようになります。

content.scan(%r{http://localhost:3000/reports/\d+})

これで非常に見やすくなりました。

他の記述パターン

%r で始まり、対になった区切り文字で文字の集合を囲うことで文字列を作成できるので以下のような記号を使うこともできます。

%r[パターン]
%r(パターン)
%r<パターン>
%r|パターン|
%r!パターン!
%r*パターン*

逆に区切り文字に使用した文字がパターン内で現れる場合にはエスケープが必要な点には注意が必要です。パターン内で使用されない区切り文字を使うのが良さそうです。