tkrd-stack

学習ノート

自分の理解を深めるための技術メモ

← 一覧に戻る

JavaScript

JavaScript とは

ブラウザ向けスクリプト言語。

元々は web ページに動きを付けるためのプログラム言語。多くのブラウザー以外の環境でも使用されている。

特徴

  • インタプリタ
  • シングルスレッド
  • オブジェクト思考
  • 命令型
  • 関数プログラミング

など多くの特徴があり、使用するためには幅広い知識が必要になるが同時に様々なことをすることも可能。 JavaScript はプログラミング言語である Java とは別物である。

ES5 と ES6

Javascript のバージョン。

ES とはECMA Internationalによって標準化された Javascript。クロスブラウザ問題を受けて国際的に標準化しようよという感じ。

ES5 と ES6 では書き方が違う部分がある。

Javascript の組み込み方

html に script 要素を書く

  • body 要素の配下
    • コンテンツとコード混在するので可読性/保守性の観点から bad。何度書いても、最後にひとまとまりとして処理するので大丈夫。
<body>
  // --- 任意のコンテンツ ---
  <script></script>
  // --- 任意のコンテンツ ---
  <script></script>
</body>
  • body 要素(閉じタグ)の直前

    • ページ高速化の手法として body の閉じタグ直前に配置する。ブラウザはスクリプトの読み込みや実行が完了するまでコンテンツを描画しないため、スクリプトに時間が掛かればページの描画に時間が掛かる。コンテンツの描画を終えた後にスクリプトを読み込み、実行できる。
    <body>
      <script></script>
      // --- 任意のコンテンツ ---
    </body>
    
  • head 要素の配下

    • 関数定義の script がある場合に事前に読み込ませておく必要がある。
<body>
  // --- 任意のコンテンツ ---
  <script></script>
</body>

どれが望ましいかというと、2 番目を基本にしてそれで賄えない場合は 3 番目を利用する。

外部スクリプトをインポート

  • 可読性と保守性が良い。
  • レイアウトとスクリプトを分離することでコードを再利用しやすい。

外部ファイルとして別に定義できる。

<script src="path"></script> // pathはjsファイルの場所

以下のような書き方は注意。

<script src="path"></script>
// 以下スクリプトは無視される。
<script>
  console.log("hello");
</script>

strict モード

JavaScript は仕様として存在しているが、現在では安全性や効率面で利用すべきではない構文が存在する。

そういった JavaScript の落とし穴をエラーとして検知してくれる仕組みのこと。

strict モードの有効化はスクリプトの先頭に”use strict”;を追加する。

"use strict";
// 以下コードがstrict対象

また、ブロックの先頭に追加することも可能。

function getName() {
  "use strict";
  // 以下コードがstrict対象
}

altJS

JavaScript には深い歴史があり、ブラウザで動く言語といえば実質 JavaScript のみ。これを今から新しい言語で置き換えるのは現実的ではない。この背景から JavaScript の代替として開発された言語のことで開発者にとってコードの読みやすさ、保守性、パフォーマンスなどのメリットがある。

  • TypeScript
  • CoffeeScript
  • Dart

など

script 要素

非同期/同期処理

スクリーンショット 2024-12-03 11.20.21.png

安全に外部スクリプトをロード

外部からのリソースが悪意ある第三者によって改ざんされないように検証するのが integrity 属性。接頭辞-ハッシュ値の形式で指定する。利用できる接頭辞はハッシュの種類(sha256、sha384、sha512)

<script
  src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
  integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
  crossorigin="anonymous"
></script>

命名規約

コードを書くときの決まり事。

あらかじめ知っておくことで、「これは変数だな」とか「これは関数だな」と素早く知ることができる。

識別子

スクリプトの中での名前のこと。

  • Unicode 文字、ドル記号、アンダースコア、数字から構成。
  • 1 文字目は数字ではないこと。
  • 大文字小文字は区別される。
  • 予約語ではないこと

より良い命名の為の指針

留意良い例悪い例備考
名前から中身が類推しやすいname,title,messagea, b, z
長すぎない、短すぎないkeywordkw, keyword_for_site_search
1 文字目アンダースコアは特別な意味を持つので使わないname_name
記法の統一firstName, lastName-名前の記法ルール
ローマ字ではなく英単語で命名name,title,messagenamae, taitoru

名前の記法ルール

対象命名規約
変数キャメルケースfirstName
定数全大文字ROUTES
メソッドキャメルケースfindFirstName()
クラスパスカルケースPerson
プロパティキャメルケースlastName
コンポーネントパスカルケールRadioInput

使えない名前

  • 予約語・・・if which など
  • 許可されていない記号
  • 数字で始まる名前

マジックナンバー

名前がつけられていない値のこと。コードの意図を曖昧にする原因となるので避けるべきです。

変数

変数とは

データを一時的に保存するためのの入れ物。

正確には PC のメモリに格納されている。メモリには場所を表す番号(アドレス)が振られていてそこに値を格納している。

変数宣言

let 命令

  • let 変数名 = 値;
let msg = "こんにちは"; // 変数宣言
console.log(msg); // 処理 結果:こんにちは

「=」はイコールではなくて「代入」を意味する。

変数の値を取り出すことを「参照」という。

「べからず」な変数宣言

仕様上書けてしまうが、この書き方はやめておこうという宣言方法。

  • 初期値のない宣言
let msg;

変数には undefined が割り当てられる。

初期化忘れのものになるので良くない。

  • 複数の宣言を列挙
let mag = "hello",
  msg = "goodnight";

デバッグ時にステップ実行を阻害。

  • var 命令による宣言
var msg = "hello";

バグの遠因。

  • let, var を省略した宣言。
msg = "hello";

アプリ全体で変数が有効になってしまう。strict モードでは許可されないなど。

var, let, const

これらの使い分けは const を優先して利用すべき。コードを書いていく上でし代入しなければならない状況はあまりありません。演算・加工の結果の値は意味が変わってしまうので別の変数に代入すべき。

説明変数、要約変数

コードの意図を把握しやすくする変数。

let member = "田中太郎,女,25,aaaaa@gmail.com";
if (member.split(",")[1] === "女") {
} // 直観的でない
let member = "田中太郎,女,25,aaaaa@gmail.com";
let gender = member.split(",")[1];
if (gender === "女") {
} // 直観的

プライベート変数

_(アンダーバー):オブジェクトの外からアクセスできない変数のこと。

_変数名;

演算子

演算子について

演算子とは

演算子(オペレーター)はリテラルに対してどんな演算をするかを表す記号。

算術演算子

数学的な演算機能を持つ演算子。

演算子概要備考
+加算3 + 5 → 8
-減算5 - 2 → 3
*乗算2 * 3 → 6
/除算10 / 2 → 5
**べき乗2**4 → 16
%剰余10 % 4 → 2
++(インクリメント)前置加算a=2; ++a; → 3加算してから出力
++(インクリメント)倒置加算a=2; a—; → 2出力してから加算
—(デクリメント)前置減算a=1; —a → 0減算してから出力
—(デクリメント)倒置減算a=1; a— → 1出力してから減算
// 加算
console.log("java" + "script"); //javascript
console.log("10" + "1"); //101
console.log("54" + 3); //543
console.log(1 + "2" + 3); //123
console.log(1 + 2 + "3"); //33

// 除算
console.log("java" - "script"); //NaN (Not a Number)
console.log("10" - "1"); //9
console.log("5" - 6); //-1

JavaScript は内部的に計算は 2 進数を用いて計算してるため、小数点を用いた演算は誤差を生み出してしまうことがある。

console.log(0.1 * 3); // 0.30000000000000004

無限循環相と言い、2 進数表記で数値が無限に繰り返されてしまうことによる誤差。

厳密に演算結果を得るためには、値を整数ににして小数に戻す。

console.log(0.1 * 10 * 3); //0.6

代入演算子

変数に値を代入するための演算子と複合代入演算子。

| 演算子 | 概要 | 例 | | ------ | --------------------------------------------------------- | --------------------- | --------------------------------------- | -------- | --- | ------ | | = | 変数に値を代入 | x = 1 | | += | 左辺の値に右辺の値を加算したものを代入 | x = 3; x += 2 →5 | | -= | 左辺の値に右辺の値を減算したものを代入 | x = 3; x -=2   →1 | | *= | 左辺の値に右辺の値を乗算したものを代入 | x = 3; x *=2   →6 | | /= | 左辺の値に右辺の値を除算したものを代入 | x = 6; x *=2   →3 | | %= | 左辺の値に右辺の値を除算した余りを代入 | x = 3; x %=2   →1 | | **/ | 左辺の値に右辺の値を乗算したものを代入 | x = 3; x **=2   →9 | | &&= | 左辺の値が true の場合にだけ右辺を代入 | x = 1; x &&=5 → 5 | | | | = | 左辺の値が false の場合にだけ右辺を代入 | x = 0; x | | =6 → 6 | | ??= | 左辺の値が null もしくは undefined の場合にだけ右辺を代入 | x = null; x ??= 7 → 7 |

比較演算子

左辺と宇円を比較し、結果を true もしくは false で返す。

別名、関係演算子。

演算子概要備考
==左辺と右辺の値が等しい場合は true1 == 1 → true
1 == “1” → true等価演算子
左辺と右辺の値が等しくない場合は true1 ≠ 1 → false
1 ≠ 2 → true厳密な等価演算子。演算子よりも優先して使った方が良い。
<左辺が右辺より小さい場合は true1 < 2 → true
左辺が右辺以下の場合は true1 ≤ 2 → true
2 ≤ 2 → true
>左辺が右辺より大きい場合は true3 > 2 → true
左辺が以下の場合は true3 ≥ 2 → true
2 ≥ 2 → true
===左辺、右辺の値とデータ型が同じ場合は true5 === 5 → true
5 === “5” → false
≠=左辺と右辺の値が等しくない場合、もしくはデータ型が異なる場合は true5 ≠= “5” → true
5 === 5 → false
?:条件演算子。(10 === 1) ? 1 : 0 → 03 項演算子
??null 合体演算子。
左辺が null でなければその値、左辺が null であれば右辺の値を返す。null ?? 5 → 5

比較演算子を使うときもデータの基本型と参照型で違いが発生するので注意。

let data1 = ["name", "age", "gender"];
let data2 = ["name", "age", "gender"];
console.log(data1 == data2); // false

配列は参照型で変数の参照値(メモリのアドレス)を格納しているため、中身が一緒でもそれぞれに割り振られている値が違うため false を返す。

配列に中身を比較する場合は

  • for 文で個々の要素を比較する。
  • Json.stringfy メソッドで文字化した結果を比較する。

論理演算子

| 演算子 | 概要 | 例 | 備考 | | ------------- | --------------------------------- | -------------- | --------------------------------------- | -------------- | ------- | | && | 左右の式が共に true の場合は true | x && y → false | | x && x → true | 積  AND | | | | | 左右の式のどちらかが ture の場合は true | x && y → false | 和  OR | | !  | 式が false の場合は true | !x → true | 排他  NOT |

&&と||では「左辺だけ評価されて右辺が評価されない」場合がある。これをショートカット演算子という。

let x = 5;
let y = 10;

// 左辺が評価されfalseになるので右辺は評価されない
if (x === 1 && y === 10) {
}

// 左辺が評価されtrueになるので右辺は評価する必要がない
if (x === 5 || y === 10) {
}

…演算子

引数時の配列を展開してくれる。

let num = [1, 2, 3, 4];
let sum = function (num1, num2, num3, num4) {
  return num1 + num2 + num3 + num4;
};
console.log(sum(...num)); // 10

分割代入

配列、object を分解し保有している要素、プロパティ値を変数に分解するための構文。

配列の分割代入

let a, b;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20

// スワッピング
[a, b] = [b, a];
console.log(a); // 20
console.log(b); // 10

// 個数が異なる場合
let data = [1, 2, 3, 4, 5];
let [x0, x1, x2] = data; // x0=1 x1=2 x2=3

let data = [1, 2];
let [x0, x1, x2] = data; // x0=1 x1=2 x2=undefined

// 残余パターン
let rest;
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest); // 30, 40, 50

[first, ...rest] = "hello";
console.log(rest); // ['e','l','l','o']

オブジェクトの分割代入はプロパティ名でバインドされる。また、目的のプロパティが存在しなかった場合に備えて「変数名=値」として規定値を設定しておくことも可能。

let book = {
  title: "ためになる本",
  publisher: "為成出版",
  price: 1600,
};

let { price, title, memo = "為" } = book;

console.log(title); // ためになる本

入れ子になっても基本的な考え方は同じ。

let book = {
  title: "ためになる本",
  publisher: "為成出版",
  price: 1600,
  other: {
    keywd: "お金",
    page: 145,
  },
};

let {
  price,
  title,
  other: { page },
} = book;

console.log(page); // 145

変数名:別名とすることで別の変数に値を格納することができる。

let book = {
  title: "ためになる本",
  publisher: "為成出版",
  price: 1600,
};

let { title: subjest, publisher: company } = book;

console.log(subjest); // ためになる本
console.log(company); // 為成出版

…演算子で分解されなかった残りのプロパティをまとめて取得できる。

let book = {
  title: "ためになる本",
  publisher: "為成出版",
  price: 1600,
};

let { title: subjest, ...rest } = book;

console.log(subjest); // ためになる本
console.log(rest); // { publisher: "為成出版", price: 1600}

その他の演算子

上記のいずれにも分類されない演算子。

演算子概要
,左右の式を続けて実行
deleteオブジェクトのプロパティや配列の要素を削除
in指定されたプロパティがオブジェクトに存在するか判定
instanceofオブジェクトが指定されたクラスのインスタンスか判定
new新しいインスタンを生成
typeof対象の基本型のデータ型を取得
void返り値に何も返さない。

式と文

式(Expression)とは

式(Expression)は値を生成し、変数に代入できるもの。

console.log(1); // 1という式
console.log(1 + 1); // 1+1という式
const sum = 1 + 1; // 式を変数に代入
const fn = function () {
  // 関数式を変数に代入
  return 1;
};

文(Statement)とは

文(Statement)は処理する 1 ステップが 1 つの文。文末にセミコロンを付けるとこで文と文を区切っている。


const isTrue = true; // 式
if (isTrue) {
  // 処理
}
const err = if(isTrue){}; // これは文であり、変数に代入できない。

式文と言い式は文になれる。

ブロック文は{}で囲んだ部分をブロックという。

{
  文;
  文;
}

文(Statement)のルール

  • 文の末尾には;(セミコロン)を付ける。
console.log("hello");
console.log("hello"); // 読みづらい
  • 文の途中でインデント、改行を含めることができる。
console.log("hello");

ただし、可読性を考えた場合に改行は以下のルールに則った方がよい。

  1. 文が 80 列を超えた場合
  2. 左括弧、カンマ、演算子などの直後
  3. 改行した場合は、インデントを加える。
  4. 大文字と小文字は厳密に区別される。
console.log(); // OK
Console.log(); // NG

コメント

スクリプトの動作に関係のないメモ書きのこと。誰でも何をしているコードなのか変わるようにコメントを書く。

記法概要
//一行コメント
/* */複数行コメント
/** */ドキュメンテーションコメント

ドキュメンテーションコメントはコードのドキュメント歯を目的としている特殊なコメント。

コメントに何を書くべきか

  • 複雑なコードの意図
  • クラス、関数などブロック単位の要約
  • 後で作業すべき課題(TODO)、不具合(FIXME)、最適化すべき点(OPTIMIZE)、レビューすべき点(REVIEW)
  • コードの翻訳は書くべきではない。

リーダシブルコードを嫁

定数

定数とは

再代入のできない変数。なので、後から内容を変更できてしまう場合があることに注意。

コードの中で意味のある値にあらかじめ名前を付けておく仕組み。

  • const 命令

    const 定数名 = 値;
    

    定数名は大文字のアンダースコア形式。

    const TAX = 1.1;
    let price = 100;
    console.log(price * TAX); // 110
    

    基本型と参照型の挙動の違い

    // 基本型
    const TAX = 1.1;
    TAX = 1.5; // error
    
    // 参照型
    const data = [1, 2, 3];
    data = [4, 5, 6]; // error
    data[1] = 10; // 再代入可能
    

変数の参照型の時の話から、参照型は変数に参照先のアドレスを格納している。そのため、参照先のアドレスはそのままで中身だけの変更ということが可能になっている。

let と const の使い分け

const を優先して利用。

理由は再代入の機会がそれほどないし、let 命令されていると読み手が後から変数の変更を意識しなければならなくなる。

もし再代入が発生すればあとから const から let に変更する方法がおすすめ。

使いどころはコンポーネントの定義でもある。

データ型

データ型について

データ型とは

データの種類のこと。

文字列、数値、真偽値などなど。

  • 静的型付け言語 変数に使いたいデータ型を宣言する必要がある。
  • 動的型付け言語 変数に異なるデータ型の値を代入することが可能。
    let greeting = "こんにちは、JavaScript";
    console.log(greeting); // こんにちは、JavaScript
    greeting = 100;
    console.log(greeting); // 100
    

データ型の分類

大きく基本型(プリミティブ型)と参照型(構造型)に分類される。

分類データ型概要
基本型number数値
基本型bigintnumber を超える数値
基本型stringクォートで囲まれた 0 個以上の文字
基本型booleantrue(真) / false(偽)
基本型symbol一意の値
基本型null / undefined値が空、未定義
参照型arrayデータの集合(各要素に index でアクセス可能)
参照型objectデータの集合(各要素に名前でアクセス可能)
参照型function処理の集合

基本型と参照型の違い

取り扱うデータが違う。

  • 基本型は値そのものが変数に格納。
  • 参照型は変数の参照値(メモリのアドレス)を格納。
// 基本型
let x = 1;
let y = x;
x = 2; // xを2に置き換える
console.log(y); // 1

基本型のそのままの値が変数に直接格納されるので、変数 x の値を変数 y に引き渡す場合にもその値がコピーされる。

// 参照型
// その1
let data1 = [0, 1, 2];
let data2 = data1;
data1[0] = 5; // data1の0を5に置き換える
console.log(data2); // [5, 1, 2]

// その2
let data1 = [0, 1, 2];
let data2 = data1;
data1 = [10, 20, 30]; // 参照先の変更
console.log(data2); // [0, 1, 2]

参照型は格納先を表す参照値が変数に格納されるので、参照先の値を変えてしまうとその格納先を参照している変数すべてに影響を与える。(その 1)

しかし、参照先の変更を行えばほかの変数に影響はされない。(その 2)

boolean

boolean(論理値)を表す。論理値は true と false の 2 値。

JavaScript では以下の値を暗黙的に false となる値。

  • 空文字
  • 数値の0
  • NAN
  • null
  • undefined

これをfalsyな値ともいう。

number

number(数値)を表す。

  • 整数
    • 10 進数
    • 16 進数 接頭辞:0x
    • 8 進数 接頭辞:0o
    • 2 進数 接頭辞:0b
  • 浮動小数点 小数点数だけでなく指数表現も存在する。
    • <仮数部> e <符号><指数部>

      3.14e5 = 3.14 × 105(5 は指数)

数値セパレータ

ES2021 以降。

桁数の大きな数値を読みやするするため区切り文字(‗)を使用できる。16 進数や浮動小数点でも使用可能。

let num = 1_234_567;

string

string(文字列)を表す。

シングルクォーテーションもしくはダブルクォーテーションで区切る。

"hello"; // シングルクォーテーション
"hello"; // ダブルクォーテーション

シングルクォーテーションもしくはダブルクォーテーションを string として使いたい場合

  • 一方のクォートで囲む。
    console.log("He's is hero");
    console.log('u r "good" player');
    
  • エスケープ処理する。 エスケープ処理とは意味ある文字を無効化すること。
    console.log("He's is hero");
    

エスケープシーケンス

(バックスラッシュ)であら和得される文字のこと。

文字概要
\bバックスペース
\f改ページ
\n改行
\r復帰
\tタブ文字
\v垂直タブ
\Enter行を継続
\b アックスラッシュ
\’シングルクォート
\”ダブルクォート
\xXXLatin-1 文字 (XX は 16 進数)
\uXXXXUnicode 文字 (XXXX は 16 進数)
\u{XXXX}4 桁の 16 進数を超える Unicode 文字

エスケープシーケンスを使うことで文字列中に改行を入れれる。

console.log("こんにちは。 \nJavaScript!!");
//こんにちは。
//JavaScript!!

テンプレート文字列

ES2015 で追加。

`(バッククォート)で文字列を囲むことで可能。

以下のことが可能。

  • 複数行にまたがる文字列
  • 文字列への変数の埋め込み
let name = "tanaka";
let intro = `初めまして。${name}と言います。
 よろしくお願いします!!`;
console.log(intro);
// 初めまして。tanakaと言います。
// よろしくお願いします!!

エスケープシーケンスでしか表せない改行などもそのまま表現できるようになった。

array

array(配列)はデータの集合。

変数に対して一つの値だけを持つのではなく、複数の値を持てる。

値の集合を一つの変数で管理できるので、見やすくしたり一括で操作したりすることができる。

変数名 = [値1,値2,...]; // arrayの宣言
変数名[インデックス番号] // 要素へのアクセス(ブラケット構文)

配列中の値を要素と言い、要素の位置を添え字(インデックス)という。

let programming = ["JavaScript", "PHP", "Ruby", "Python", "Perl"];
console.log(programming[0]); // JavaScript

インデックスは「0」からスタートする。

prigramming[0] = "java"; // JavaScriptをjavaに上書き
prigramming[10] = "Rust"; // 新しい要素を追加

配列のインデックスが飛び番号になった場合、間の要素には空の配列が生成される。これを疎な配列という。

prigramming[5] = "java"; // undefined

入れ子の配列

配列の値として配列が入っている構造。

let date = [
  ["javascript", "js"],
  ["python", "py"],
  ["ruby", "rb"],
];
console.log(data[0][1]); // js

object

配列に対して object は名前をキーにしてアクセスできる配列でデータの可読性が高い。別名:連想配列、ハッシュ

個々のデータはプロパティと言い、object は名前と値を持つプロパティ群。

変数名 = {
  プロパティ名1: 値1,
  プロパティ名2: 値2,
};
const fruit = {
  apple: "リンゴ",
  orange: "オレンジ",
  grape: "ブドウ",
};

名前をキー、値をバリューという。

入れ子の object

const fruit = {
  apple: {
    name: "リンゴ",
    color: "red",
  },
  orange: "オレンジ",
  grape: "ブドウ",
};

プロパティ

オブジェクトの key と value のセットのこと。

	apple: "リンゴ",  // apple=key, "リンゴ"=value

key には文字列だけを設定することができる。

aplle: "リンゴ" //OK
"aplle": "リンゴ" //OK
12345: "リンゴ"  //NG

プロパティへのアクセス方法

ドット記法とブラケット記法の 2 通りある。

fruit.apple; //ドット記法
fruit["apple"]; //ブラケット記法

ドット記法の方が楽に書けるが、以下の場合にブラケット記法を用いる。

  • key にハイフンや空白などがある
  • プロパティ名が予約語(let private class)
  • 変数の値をプロパティ名に使う
let obj = { "wakayama-ken": "wakayama" };
console.log(obj["wakayama-ken"]); // wakayama
let obj = { false: 0 };
console.log(obj[false]); // 0
let wakayama = "wakayama";
let obj = { "wakayama-ken": "wakayama" };
console.log(obj["wakayama-ken"]); // wakayama

イミュータブル(immutable)

オブジェクトは改変することができない。このような行為を破壊的という。

let str = "abcde";
str[0] = "x"; // 破壊的
console.log(str); // abcde

メソッド

オブジェクトのプロパティに格納されている関数。

const obj = {
"name": getName() {
  return "wakayama";
}
const name = obj.getName(); // wakayama

名前空間としてのオブジェクト

名前空間(Namespace)とはプログラミングにおける概念の一つ。

同じ名前のものが複数存在しないように仕分ける仕組み。

Javascript であればオブジェクトでラップする。

名前空間を使わない場合。

function sum(num1, num2) {
  return num1 + num2;
}
function substract() {
  return num1 - num2;
}

すべてがグローバル変数として存在しているため、グローバルスコープ内に同じ名前や変数があると、名前の衝突が発生する。

名前空間を使った場合。

let MYFUNCTION = {
  function sum(num1, num2) {
	  return num1 + num2;
  }
  function substract() {
    return num1 - num2;
  }
}
let operation = MYFUNCTION.sum(5, 9);
conosle.log(operation); // 14

MYFUNCTION を使うことでグローバルスコープから隔離し、名前の衝突を避けている。

function

パラメータとして引数を渡すことによって、決められた処理を行いその戻り値を結果として返す仕組み。

関数もオブジェクトの一つ。

定義方法

function sum(a, b) {
  return Number(a) + Number(b);
}
const sum = function (a, b) {
  return Number(a) + Number(b);
};

関数リテラルは匿名関数や無名関数とも言われる。

let sum = (a, b) => {
  return Number(a) + Number(b);
};

アロー関数

引数に関数を取る場合に、簡単に記述できる関数記述方法。

(引数) => {
  関数の処理;
};
let func = () => {};

undefined と null の違い

undefined

  • ある変数が宣言済みであるが値がない。
  • 未定義のプロパティを参照。
  • 関数で結果が返されなかった。
let x;
console.log(x);
let obj = { name: "tanaka" }; // undefined
console.log(obj.age); // undefined

null

  • 変数が宣言されているが、中身が空。
  • 意図的に null を宣言しないと発生しない。
let num = 10;
num = null;
console.log(num); // null

構造化プログラミング

構造化プログラミングについて

構造化プログラミングとは

アプリケーション開発においてコードを理解しやすく、効率的にしやすくするためのプログラミング手法。

プログラムの構造は大きく 3 つに分類される。

  • 順次 記述順に処理を実施する。
  • 選択 条件によって処理を分岐する。
  • 反復 条件が満たされるまで処理を繰り返す。

これらを組み合わせながらプログラムを作っていくことを構造化プログラミングという。この構造を制御構文を使うことで実装することができる。

制御構文

条件分岐(選択)

if 命令

「もし~ならば A、そうでなければ B」という命令。{}で囲まれた部分をブロックという。

  • if ブロック
  • else ブロック
if (条件式) {
  A: 条件式がtrueの場合に実行する処理;
} else {
  B: 条件式がfalseの場合に実行する処理;
}
let x = 10;
if (x > 5) {
  console.log(x); // 10
} else {
  consolo.log("10より小さい値です。"); //10より小さい値です。
}

else ブロックの省略可能。

let x = 10;
if (x > 5) {
  console.log(x); // 10
}

else if 命令

if ブロックに else if ブロックを追加することで、「もし~ならば A、もし~ならば B、そうでなければ C」のような複数分岐の実装が可能。

if (条件式1) {
  A: 条件式1がtrueの場合に実行する処理;
} else if (条件式2) {
  B: 条件式2がtrueの場合に実行する処理;
} else {
  C: 上記の条件式がfalseの場合に実行する処理;
}
let x = 10;
if (x < 5) {
  console.log(x);
} else if (x >= 5) {
  console.log(x); // 10
} else {
  consolo.log("10より小さい値です。");
}

複数の条件に合致しても実行されるのは最初の条件式に当てはまった 1 つだけ。

let x = 10;
if (x > 5) {
  console.log("ここが実行される。"); // ここが実行される。
} else if (x >= 5) {
  console.log("ここは実行されない。");
} else {
  consolo.log("10より小さい値です。");
}

switch 命令

厳格な等価演算子による多岐分岐が可能。

switch(式) {
  case 値1:
    処理を実行。
    break;
  case 値2:
    処理を実行。
    break;
  defaul:
    すべての式に値が合致しない場合に処理を実行。
}

case 句の break を忘れると次の case 句が実行されてしまうので、抜けていないか注意が必要。

let rank = "B";

switch (rank) {
  case "A":
    console.log("Aランクです。");
    break;
  case "B":
    console.log("Bランクです。"); // 実行
    break;
  case "C":
    console.log("Cランクです。");
    break;
  default:
    console.log("ランク外です。");
    break;
}

あえて case 句を書かずに一緒に条件の判断をしてしまう方法もある。これをウォークスルーという。条件式でいうと or の意味を持つ。

let rank = "B";

switch (rank) {
  case "A":
  case "B":
  case "C":
    console.log("上位ランクです。");
    break;
  default:
    console.log("下位ランクです。");
    break;
}

繰り返し処理(反復)

while 命令、do…while 命令

条件式が true である間はループを繰り返す。

  • while 命令 前置判断
  • do…while 命令 後置判断
while (条件式) {
  条件式がtrueの時に実行される命令;
}
do {
  条件式がtrueの時に実行される命令;
} while (条件式);

例文では while ブロックと do ブロックの最後に x 変数をインクリメントしている。while の条件式が「x < 5」としており、x の値に変化がなければ無限ループを引き起こしてしまうためである。無限ループは PC への負荷を与えてしまうため避けるべき事象。

let x = 0;
while (x < 5) {
  console.log(x); // 0 1 2 3 4
  x++;
}
let y = 5;
while (y < 5) {
  console.log(y); // 実行されない
  y++;
}
let x = 0;
do {
  console.log(x); // 0 1 2 3 4
} while (x < 5);

let y = 5;
do {
  console.log(y); // 5
} while (y < 5);

for 命令

指定回数だけループ

for (初期化式; ループ継続条件式; 増減式) {
  処理;
}
  • 初期式 カウンター変数の初期をする。最初のループで 1 度だけ実行される。
  • ループ継続条件式 ループを継続するための条件式。
  • 増減式 カウンター変数を増減する演算子や複合代入演算子を指定。
for (let i = 0; i < 5; i++) {
  console.log(`数は${i}です。`); // 数は0です。数は1です。数は2です。数は3です。数は4です。
}

カンマ演算子を利用することで、初期式、ループ継続条件式、増減式に複数の式を指定することもできる。

式は先頭から順に実行される。

ループ継続条件式では一つの条件が先に終了してしまえば、残りの条件式は実行されない。

for (let i = 0, j = 5; i < 5, j < 7; i++, j++) {
  console.log(`i: ${i}. j:${j}.`);
}
// i: 0. j:5.
// i: 1. j:6.

for…in 命令

オブジェクトや配列の要素を先頭から順に処理する。

仮変数はオブジェクトのキー(インデックス番号)を一時的に格納するための変数。仮変数には要素そのものが格納されるわけではないので注意。

for (仮変数 in オブジェクト) {
  命令;
}
let fruits = {
  apple: "リンゴ",
  orange: "オレンジ",
  grape: "ブドウ",
};

for (fruit in fruits) {
  console.log(`${fruit}=${fruits[fruit]}`);
}
/*
apple=150
banana=100
orange=250
*/

for…in の問題点

  • for…in 命令では反復の順序が保証されない。
  • 仮変数にはインデックス番号が格納されるだけなのでコードがあまりシンプルにならない。

for…of 命令

配列の要素を順に処理するための命令。配列ライクなオブジェクトやイテレーター、ジェネレーターなど(反復可能なオブジェクト)も処理できます。

for (仮変数 of 反復可能なオブジェクト) {
  命令;
}

仮変数には実際の値が入るため for…in 命令よりもコードがシンプルに見える。

let fruits = ["apple", "oragen", "banana"];

for (fruit of fruits) {
  console.log(fruit);
}
/*
apple
orange
banana
*/

for..of 命令でも分割代入を使うことができる。

let books = [
  ["男飯", "飯出版"],
  ["技術は手から", "技能出版"],
  ["english is easy", "英語出版"],
];

for (let [title, publisher] of books) {
  console.log(`${title}:${publisher}`);
}
/*
男飯:飯出版
技術は手から:技能出版
english is easy:英語出版
*/

ループ制御命令

反復処理中にループを中断したいやある周回だけをスキップしないなどの制御をする命令。

break 命令

ループを強制終了する。

for (let i = 0; i < 50; i++) {
  if (i > 10) {
    console.log(i); // 11
    break;
  }
}

continue 命令

ループをスキップする命令。

for (let i = 0; i < 4; i++) {
  if (i == 2) {
    console.log("skip"); // i=2の時はifブロック以下をスキップ
    continue;
  }
  console.log(i);
} /*
0
1
skip
3
*/

label 構文

ラベルを break,continue に付与すると指定されたループから脱出できる。

if ブロック中に switch がありその中に break 命令がある場合などに有効。

ブロックは以下の関数からは break できない。

ラベル名:
escape: for (let i = 1; i <= 3; i++) {
  for (let j = 1; j <= 3; j++) {
    console.log(`${i}:${j}`);
    if (i == 3) {
      break escape;
    } // i=3になったらescapeに脱出
  }
}

例外処理

try…catch…finally 命令

開発時には想定しないエラーが発生する。エラーが発生した場合はコード全体が停止してしまうので、それを回避するための命令。

try {
  例外が発生するかもしれない処理;
} catch (例外情報を受け取る変数) {
  例外が発生したら実行される命令;
} finally {
  れ外の有無に関係なく実行する処理;
}
// 例外処理が発生した場合
let i = 1;

try {
  i = i * j;
} catch (e) {
  console.log(`${e.name}:${e.message}`);
} finally {
  console.log("処理が終了しました。");
}
/*
ReferenceError:j is not defined
処理が終了しました。
*/

// 例外処理が発生しない場合
let i = 1;
let j = 4;

try {
  i = i * j;
} catch (e) {
  console.log(`${e.name}:${e.message}`); // skipされる。
} finally {
  console.log("処理が終了しました。");
}
/*
処理が終了しました。
*/

以下のような書き方が可能。

  • try…catch
  • try…finally
  • try…catch…finally

throw 命令

例外処理を自分で発生させる命令。

throw new Error(エラーメッセージ);
let x = 1;
let y = 0;

try {
  if (y == 0) throw new Error("0で除算できません。");
  let z = x / y;
} catch (e) {
  console.log(e.message);
}

Error オブジェクトの代わりのオブジェクトも存在する。

objectエラーの原因
EvalError不正な eval 関数
RangeError値が許容は荷を超えている
ReferenceError宣言されていない変数へのアクセス
SyntaxError文法エラー
TypeError指定された値が期待しているデータ型ではない
URIError不正な URI

Global オブジェクト

Global オブジェクトとは

Global オブジェクトについて

JavaScript が自動的に生成する便宜的なオブジェクトで、JavaScript でよく使うグローバル変数やグローバル関数を規定で提供している。そのため、オブジェクトのインスタンスはできない。 呼び出すときは静的な呼び出し方で使う。

変数名
関数名(引数, ...)

グローバルオブジェクトの実態は JavaScript の環境によって変化する。

詳細はMDN 公式を参照。

JSON オブジェクト

JSON オブジェクトとは

Symbol オブジェクト

Symbol オブジェクトとは

Symbol オブジェクトについて

ES2015 で追加された型。名前の通りシンボル(モノ)を作成するための型。 シンボルの目的は JavaScript の機能拡張にある。

シンボルの性質

let sym1 = Symbol("sym");
let sym2 = Symbol("sym");

console.log(typeof sym1); //symbol
console.log(sym1.toString()); //Symbol(sym)
console.log(sym1.description); //sym
console.log(sym1 === sym2); //false

シンボルを生成するのは Symbol 関数。コンストラクタでは生成できない。

Symbol([desc]); //desc:シンボルの説明

引数が同じシンボルの説明を表していても別々に作成されたシンボルとは別物と見なされる。つまり、1度作成したシンボルはそれとのみ等しくなり、全く同じシンボルは2度と作ることはできない。 またシンボルでは暗黙的な型変換(文字列 ⇔ 数値)ができない。しかし、boolean 型への変換は可能。

console.log(sym1 + ""); //TypeError: Cannot convert a Symbol value to a string
console.log(sym1 - 0); //TypeError: Cannot convert a Symbol value to a number
console.log(typeof !!sym1); //boolean

関数

関数について

関数とは

与えられた入力(パラメータ)に何らかの処理をおこない、その結果を返す仕組み。オブジェクトに属する関数はメソッドと呼んで区別するが役割は同じ。 関数には 2 種類ある。

  • 標準関数
  • ユーザー定義関数。

標準関数

JavaScript が標準で用意している関数。

ユーザー定義関数

自分で作成する関数。重複したコードを関数定義することで、コードを読みやすく、修正しやすくなる。function 命令で宣言された関数が、スコープの上に巻き上げられる。つまり、funtion 命令がスコープの先頭で定義されたとみなされる。(関数の巻き上げ)

ユーザー定義関数の基本

/* 定義 */
function(引数, ...) {
   ...statements...
   return 戻り値;
}
/* 呼び出し */
関数名(引数, ...);

関数名

命名規約に則ると、キャメルケースで記述する。また、関数の役割が一目見てわかるように「動詞+名詞」の形式で命名するのが一般的。 複数同士の連結は避けるべき。理由は関数の役割を一つに限定することで、再利用性、テストやりやすさなどが違う。

関数自身を再起的に呼び出す(再帰関数)

再帰関数(Recursive Function)とは、ある関数が自分自身を呼び出すこと。再帰関数を利用することで、階乗計算のように同種の手続きを何度も呼び出すような処理をコンパクトに表現できる。

function factorial(n) {
  if (n != 0) return n * factorial(n - 1);
  return 1;
}

console.log(factorial(5)); //120

関数の引数も関数(高階関数)

JavaScript の関数はデータ型の一種なので、関数そのものも文字列や数値などと同様に引数として渡すことや戻り値として返したりすることができる。関数を引数、戻り値として扱う関数のことを高階関数という。 大枠の機能だけを定義しておいて、詳細な機能は関数の利用者が自由に決めれる。

高階関数の基本

/* 大枠の機能 配列を走査する。 */
function arrayWalk(data, callback) {
  for (let [key, value] of data.entries()) {
    callback(value, key);
  }
}
/* 詳細な機能 配列のキーと値を表示*/
function showElement(value, key) {
  console.log(`${key}:${value}`);
}

/* 詳細な機能 配列の要素を合計する*/
let result = 0;
function sumElement(value, key) {
  return (result += value);
}

let list = [1, 2, 4, 8, 16];
arrayWalk(list, showElement);
arrayWalk(list, sumElement);
console.log(result);

コールバック関数

高階関数に引数として渡された関数のこと。

function hello() {
  console.log("hello");
}

function hi(callback) {
  callback();
}

hi(hello);
function A(text) {
  text();
  console.log("accept message");
}

A(function () {
  console.log("こんにちは");
});

処理の順序管理

関数呼び出しの順序を指定するのに役立ちます。

function A(text) {
  text();
  console.log("accept message");
}

A(function () {
  console.log("こんにちは");
});

非同期処理

サーバーからの応答を待たずに他の処理を進めることができ、応答が返ってきたタイミングで処理を実行できます。

function fetchData(callback) {
  setTimeout(() => {
    console.log("Data fetched");
    callback(); // 処理完了後にコールバック関数を呼び出す
  }, 2000);
}

function processFetchedData() {
  console.log("Processing fetched data...");
}

fetchData(processFetchedData);

プリミティブ型と参照型

JavaScript におけるプリミティブ型と参照型の違いが生まれたのは、メモリ効率とデータ管理のニーズが異なるためです。両者はそれぞれの特性に応じて適切な方法で処理されるよう設計されています。

1. メモリ効率の違い

プリミティブ型と参照型のデータでは、必要なメモリ量が異なるため、JavaScript エンジンはそれぞれに適した方法でメモリを管理しています。

  • プリミティブ型はシンプルで、固定サイズのデータがほとんどです。数値、真偽値、文字列などは小さいデータ量で済むため、コピーを作成してもメモリの負荷が少なく、処理が高速です。このため、値渡しが適しています。
  • 参照型は、配列やオブジェクトのように柔軟なサイズを持つデータです。例えば、配列やオブジェクトは要素やプロパティが増えると必要なメモリ量が変化するため、実際のデータではなく「参照」を渡すことでメモリを効率的に利用しています。もし参照型のデータをすべてコピーする設計だと、オブジェクトの大きさが大きくなるほど、メモリの使用量と処理速度に悪影響を及ぼしてしまいます。

2. データの性質と操作の違い

プリミティブ型と参照型は、データの用途と操作方法の違いによっても区別されています。

  • プリミティブ型は「不変な値」として扱われます。例えば、数値や文字列は変更されることがなく、代わりに新しい値が再代入されます。これは、「シンプルなデータはそのまま扱う」ように設計されているためです。値を直接保持しているため、計算や代入時に他のデータに影響を与えません。
  • 参照型は「可変なデータ」を表し、複雑なデータ構造を柔軟に管理するために、関数間やスコープ間で同じデータを共有することが多くなります。たとえば、オブジェクトや配列を参照渡しすることで、異なる関数からデータを操作したり、状態を共有したりすることができるため、データを効率的に管理できます。

3. 処理の効率とパフォーマンス

  • 値渡しであれば、単純な値をコピーするだけなので、処理が高速になります。プリミティブ型は短期間で完結するデータ操作が多いため、値渡しでも問題が生じにくく、コピーする方が処理の流れを簡潔に保てます。
  • 参照渡しの場合、大きなデータを操作する際にコピーせずに済むので、パフォーマンスが向上します。参照型はオブジェクト全体を操作することが多いため、元のデータにアクセスできる方が効率が良く、操作をまとめて行えるというメリットもあります。

結論

プリミティブ型と参照型の違いは、メモリ効率、データの性質、処理パフォーマンスに応じて生まれたものです。シンプルな値はそのままコピーし、複雑なデータは参照を通じて操作することで、効率的で柔軟なデータ管理を可能にしているわけです。この違いによって、JavaScript はよりパフォーマンス良く、メモリを節約しながらデータを操作できるように設計されています。

値参照とメモリ参照

分類データ型
プリミティブ型Number, String, Boolean, Undefined, Null, Symbol, BigInt
参照型Object, Array, Function, Date, RegExp, Map, WeakMap, Set, WeakSet

値渡しと参照渡しの違いをまとめると

  • 値渡し:データのコピーが渡されるので、関数内で変数を変更しても元の変数には影響がない。
  • 参照渡し:オリジナルのデータ(オブジェクトや配列などの参照)が渡されるので、関数内での変更が元のデータに影響を与える。

注意点

JavaScript では、プリミティブ型(数値、文字列、ブール値など)は値渡し、オブジェクトや配列などの参照型は参照渡しに似た挙動をします。ただし、JavaScript では実際には「参照そのもののコピー」を渡しているため、厳密には「参照渡し」

イベントリスナー

イベントの発生を待ってから応答する JavaScript の関数。

その他メモ

JavaScript を拡張した javascript や TypeScript などは直接ブラウザで実行できないため、ブラウザで実行可能なファイルにしてから、サーバーに公開する必要がある。(バンドル)

First-class Function (第一級関数)

あるプログラミング言語が第一級関数 (First-class functions)  を持つと言われる場合、その言語の関数がその他の変数と同様に扱われることを表します。例えば、こうした言語では、関数を他の関数への引数として渡したり、他の関数から返却したり、変数の値として代入したりすることができます。