0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScript Primer

Last updated at Posted at 2025-01-15

JavaScriptの前提

  • どの実行環境でも動くものをECMAScriptと言う
  • 大文字と小文字を区別する
JS
const name = "tuna";
// 別の変数として認識
const NAME = "tuna"; 
  • strict mode
    • 古い構文などを禁止して安全にする実行モード
    • 明示的に書かない場合はすべてこれで実行される
JS
"use strict"; 

1.基本文法

コメント

  • 一行コメントと複数行コメントがある
JS
// 行コメント

/*
複数行コメント
複数行コメント
*/

変数宣言

  • const
    • 再代入できない変数
    • 初期値を指定しなければいけない
JS
const num = 10; 

// 一度に定義する場合
const a = "Java",
      b = "Script";
  • constは"定数"ではない
    • constで定義したオブジェクトの値などは再代入できる
JS
// オブジェクトの値は初期化後でも代入できる
const object = {
    key: ""
};

object.key = "新しい値";
  • let
    • 再代入可能な変数
    • 初期値なしで定義可能(=undefinedになる)
JS
// 初期値なし
let num = 10;
// 初期値あり

let count = 0;

// 再代入
count = 1;

変数宣言のルール

  1. 半角英字, _, $, 数字を組み合わせた名前にする
  2. 数字から開始しない
  3. 予約語を使わない
  4. varは使わない

値の評価

値の評価:入力した値を評価して返すこと

値の評価の確認方法

開発者ツールのconsoleを使う

consoleに入力した値は評価されて返ってくる

// 入力した値が評価され返ってくる
> 1
1

例)変数bookTitleを定義する

> const bookTitle = "Javascript Primer"; // 定義時はundefinedが返る
undefined

> bookTitle 
"Javascript Primer"

※REPL機能という

HTMLからJavaScriptを読み込む

<script src="ソースのパス"></script>で.jsファイルを読み込む

index.html
<html>
    <head>
    <meta charset="UTF-8">
    <title>example</title>
    <!-- JSファイル読み込み -->
    <script src="./index.js"></script> 
    </head>
    
    <body>
    </body>
</html>
index.js
1; // 評価はされるがconsoleには表示されない

console.log(1); // consoleAPIで表示する

console.log(引数)

コンソールに引数の評価結果を表示する

JS
console.log(1 + 1); // => 2

const bookTitle = "JavaScript Primer";
console.log(bookTitle); // => "JavaScript Primer"

構文エラー

SyntaxError: missing ) after argument list (at index.js:3:13)
→構文エラー、)が足りない場合

実行時エラー

ReferenceError: x is not defined
→実行中のエラー、xが定義されていない場合

データ型

前提

  • JavaScriptには値の型がある
  • リテラルとはデータを直接記述する構文
  • プリミティブ型オブジェクト合わせて7種
    • プリミティブ型
      • Boolean型
      • Number型
      • String型
      • null
    • オブジェクト
      • オブジェクト
      • 配列
      • 正規表現

型を調べる際は
console.log(typeof ~~)で調べる

JS
console.log(typeof "JavaScript"); // => "string"

プリミティブ型(4つ)

真偽値リテラル(Boolean)

JS
true;
false;

数値型(Number)

整数リテラル(4種類)
  • 10進数 0, 1, 2
  • 2進数 0b + 0と1の値
  • 8進数 0o + 0~7の値
  • 16進数 0x + 0~fの値
JS
console.log(123); // 123
console.log(0b1111); // 15
console.log(0o10); // 8 
console.log(0xffff); // 65535
浮動小数点リテラル
  • 少数点を含む数値 3.1415
  • 指数を含む数値 2e8
JS
console.log(3.14); // 3.14
console.log(2e10); // 2^10
BigInt
  • 数値リテラルの最大値2^53-1より大きな整数を扱う際に使う
  • 数値+n1n, 9007199254740992n
数値リテラルのNumeric Separator

桁を見やすくするために_で数値を分けられる

JS
1_000_000_000_000;

文字列リテラル(String)

  • 文字列リテラルは""''で囲む
  • 複数列の文字列は``で囲む(テンプレートリテラル)
JS
"JavaScript";

`複数の
文字列は
こう入れる`;

nullリテラル

nullは「値が無い」を示す値。
※undefinedは似ているが、リテラルではなくグローバル変数

JS
const foo = null;
console.log(foo); // null

nullなしで空の変数を参照はできない

JS
bar 

image.png

オブジェクト

  • キーとバリューのセットを持つもの
  • キーのことをプロパティと呼ぶ
  • プロパティの参照はドット記法,ブラケット記法がある

オブジェクトの作成と参照

JS
const obj = {
    "key": "value",
    "number": 1
};

console.log(obj) // => {key: 'value', number: 1}
JS

console.log(obj.key); // => "value" 
console.log(obj["number"]); // => 1 

配列リテラル

配列は順序をつけて格納できるオブジェクトである

JS
// 配列
const array = ["a", "b","c"];
console.log([2]); // => "c"

正規表現リテラル

正規表現とは

  • 文字列の組み合わせを照合するためのパターンの表記のこと。
  • リテラルでは\\で囲む
JS
const numberRegExp = /\d+/; // 一文字以上の数字
console.log(numberRegExp.test("123")) // => true 

ラッパーオブジェクト

プリミティブ型はプロパティにアクセスしようとすると自動的にラッパーオブジェクトに変換される

例)文字列strに対してstr.lengthで長さを調べる

JS
const str = "文字列";
console.log(str.length); // => 3

演算子

  • 演算を行う処理を表す
  • 演算を行う対象をオペランドと呼ぶ

演算子の種類

  • 二項演算子
    • +, -, *, /, **, %
  • 単項演算子
    • +1, -1
    • ++, --
  • 比較演算子
    • ==, ===, >, >=など比較
  • 論理演算子
    • &&, || ,!などand, or, not論理演算
  • ビット演算子
    • &, |, ^, ~など符号付32ビット整数の演算
  • 代入演算子
    • =
  • 条件(三項)演算子
    条件式 ? Trueのとき処理する式 : Falseのとき処理する式;

比較と論理と三項演算のみ以下まとめる

比較演算子

厳密等価 ===

同じ型、同じ値の時trueを返す

JS
console.log(1 === 1); // true
console.log("aaa" === "aaa"); // true
console.log("aaa" === "bbb"); // false

オブジェクトは同じ参照の時tureを返す

JS
const objA = {};
const objB = {};
console.log(objA === objA); // true
console.log(objA === objB); // false
厳密不等価 !==

異なる値または異なる方のときtureを返す

JS
console.log(objA !== objB); // true
等価、不等価

基本的な挙動は厳密等価と同じだが、

  • 暗黙的な型変換をして比較する
  • nullundefinedを比較するとtrueが返る

あまり使うべきではない。

JS
console.log(undefined == null); // true
console.log("1" == 1); //true

論理演算子

  • AND && かつ

    • 左辺がtureなら右辺の評価結果を返す
    • 左辺がfalseなら左辺を返す
  • OR || または

    • 左辺がtrueならそのまま左辺の値を返す
    • 左辺がfalseなら右辺を返す

短絡評価=すでに決まった値があれば次を評価しない仕組み

JS
// 左辺がtrueなので、続けて右辺が評価される
true && console.log("このコンソールログは実行されます");
// 左辺がすでにfalseなので、右辺は評価されない
false && console.log("このコンソールログは実行されません");

// 左辺がすでにtrueなので、右辺は評価されない
true || console.log("このコンソールログは実行されません");
// 左辺がfalseなので、次に右辺を評価する
false || console.log("このコンソールログは実行されます");

三項演算子

条件式 ? Trueのとき処理する式 : Falseのとき処理する式;

JS
const valueA = true ? "a" : "b"; // ?の前がtrueなので"a"
console.log(valueA); 
const valueB = false ? "a" : "b"; // ?の前がfalseなので"b"
console.log(valueB);

暗黙的な型変換

  • JavaScriptでは型変換が自動で起こる仕様がある
  • ==文字列の結合など
  • 意図しない挙動に繋がるので避ける対策する
    • 明示的に型変換する
    • 比較には===を使う
JS
// 暗黙的な型変換が起こる例
console.log(1 == true);  // ture
console.log(1 + "2"); // 12 

明示的な型変換の方法

Boolean型への変換 Boolean()

Boolean(引数)で引数をtrueかfalseに変換する

JS
const text = "abc"; 
console.log(Boolean(text)); // ture
falsyな値

暗黙的な型変換によってfalse扱いされる値をfalsyと呼ぶ

  • false
  • undefined
  • null
  • 0
  • 0n
  • NaN
  • ""(空文字)
JS
let x; // undefined
if (!x) {
    console.log(x + "はfalsy");
}; // undefined はfalsy

より正確に真偽値を得るには===で比較すべし

JS
let y; // undefined
if (y === undefined) {
    console.log(x + "はfalsy");
}; // undefined はfalsy

String型への変換String()

  • String(引数)で文字列に変換
  • オブジェクトに対しては使用しない
  • あくまでプリミティブ型の文字列変換
JS
const z = false;
console.log(String(z)); // "false"

String(undefined); // "undefined"

文字列→数値変換

  • Number()
  • Number.parseInt()
  • Number.parseFloat()
JS
-// ユーザからの入力を受け取る
const input = window.prompt("数値を入力して下さい", "42");
console.log(input); // "42"または"入力文字"

// 数値に変換
const num = Number(input);
console.log(typeof num, num); // Number 42

parseInt() parseFloat()
数字以外(undefinedも)を渡されるとNaNを返す

JS
console.log(Number.parseInt("20px")) // 20
console.log(Number.parseInt("aaa")) // NaN

image.png

NaN(Not a Number)とは

Number型と互換性がない値をNumberに変換すると発生する'値'(※Number型)

何と演算しても結果がNaNになってしまうので混入を防がなければならない

NaNの判定にはNumber.isNaN()を使う

関数宣言

基本の書き方
function 関数名(仮引数1, 仮引数2...) { 処理 }

JS
// 入力を倍にする関数
function double(num)  {
    num = Number(num);
    return num * 2;
}

// windowで入力 → console出力
const input = window.prompt("数値を入力して下さい");
console.log(double(input));
  • return 関数の実行結果を返す
  • 省略か処理を書かない場合はundefinedを返す
  • 引数が少ないときは余った仮引数にundefinedが入る
JS
function func1() {}
console.log(func1()); // undefined

function func2() {
  return;
}
console.log(func2()) // undefined

引数が余る場合

JS
function argumentsToArray(x, y) {
  return [x, y];
}
console.log(argumentsToArray(1)); // [1, undefined]

デフォルト引数

仮引数に値が与えられない時の引数
function 関数名 (仮引数=デフォルト引数) {}

JS
// デフォルト引数
function addPrefix(text, prefix = "デフォルト:") {
    return prefix + text;
}
console.log(addPrefix("aaa", "カスタム:"));
console.log(addPrefix("aaa"));

可変長引数

  • 任意の数を受け取れる引数
  • 書き方は (...仮引数)
  • スコープ内で仮引数と書き、入力の配列を受け取る
JS
function fn(...arg) {
    // argsに配列として入る
    console.log(arg);
}
fn("a", "b", "c"); // ["a", "b", "c"]

複数の仮引数と使える

JS
function fn(arg1, ...args) {
    console.log(arg1); // "a"
    console.log( args); // ["b", "c", "d"]
}
fn("a", "b", "c", "d"); 

Spread構文 配列の展開

  • 関数(...配列)というように渡す
  • 展開されて引数に渡される
JS
// Spread構文 配列を展開して関数に渡す
function fn(x, y, z) {
  console.log(x);
  console.log(y);
  console.log(z);
}

const array = [1, 2, 3];
fn(...array); // 1 2 3 

分割代入(Destructuring assignment)

  • const {プロパティ名} = オブジェクト
  • プロパティ名の変数 に同じプロパティ名のプロパティ値が入れられる
  • 一気に変数に入れられるので便利

分割代入ナシ

JS
const user = {
    "id":"123",
    "userName": "Taro"
} 
const id = user.id; // idに"123"
const userName = user.userName; // userNameに"Taro"

console.log(id);
console.log(userName);

分割代入アリ

JS
const user = {
    "id":"123",
    "userName": "Taro"
} 
const {id, userName} = user; // idに"123", userNameに"Taro"が入る

console.log(id);
console.log(userName);

関数での分割代入

  • function 関数名({ プロパティ名 }) {}
  • オブジェクトのプロパティ名の値を仮引数として受け取る
JS
// userオブジェクトのidプロパティの値を仮引数idとして受け取る
function printUserId({ id }) { 
    console.log(id)
}

printUserId(user); // "123"

関数の分割代入は**[配列]**もできる

JS
function print([first, second]) {

関数式

  • 関数はオブジェクトなので、としても扱える(第一級関数"ファーストクラスファンクション"という)
  • 式なので変数に入れたりできる
  • 関数式functionArrow Functionで定義する

関数式の書き方① 変数名 = function() {};

const 変数名 = function() {};
const 変数名 = function 関数式名() {};

  • 関数式は関数名を省略できる
  • 名前を持たない関数を無名関数という
  • 再帰的に関数を呼び出す場合などに使う(外部から呼ぶ必要がないから)
JS
// 無名の再帰関数(0になるまで1引いて前の数値と掛け算)
const factional = function innerFact(n) {
    if (n === 0) {
        return 1;
    }
    return n * innerFact(n - 1);
};

console.log(factional(3)); // 6

関数式の書き方② 関数名 = () => {};

const 変数名 = (仮引数) => {return 返り値};
const 変数名 = () => {};

  • Arrow Fancrionという書き方
  • =>を使い無名関数を定義する
  • 省略記法を使って短く記述できる
JS
// Arrow Function
const fnA = () => {};

const fnB = x => {x * 2}; // 引数が1つなら()を省略可
const fnC = x => x * 2;  // 一行なら{ブロック}も省略可

特徴

  • thisが静的に決定
  • newできない
  • argumentsを参照できない

コールバック関数

  • 関数に引数として渡される関数
  • コールバック関数を引数に使う関数・メソッドを高階関数と呼ぶ
  • 非同期処理でよく利用する
JS
// コールバック関数
const array2 = [1, 2, 3];
const output  = (value) => {
    console.log(value);
} ;

array2.forEach(output); // 

// コールバック関数を直接引数として定義する
const array3 = [1, 2, 3];
array3.forEach(value => {
    console.log(value);
});

メソッド

  • オブジェクトがプロパティとして持つ関数のこと
  • 定義 : {メソッド名 : function(){処理}}
  • 呼出 : オブジェクト.メソッド名()
JS
// メソッド
const objC = {
  method1: function () {
    // メソッド1の処理
  },
  method2: function () {
    // メソッド2の処理
  }
};

呼び出し

JS
const objHello = {
  funcHello: function () {
    console.log("Hello World");
  }
};

objHello.funcHello(); // メソッドfuncHelloを呼ぶ

メソッドの短縮記法(基本これで書く)

  • ES2015より基本となった書き方
  • オブジェクトリテラル{}の中に
  • { メソッド名() {処理} };
JS
// メソッドの短縮記法
const obj = {
  method() { // メソッド名 ()
    return "this is method";
  },
};
console.log(obj.method());

式と文

JavaScriptプログラムは式と文からできている

式(Expression)

  • 値を生成し変数に代入できるもの
  • 式を評価すると評価値が得られる
  • リテラルや関数式、1 + 1などの演算も式
JS
console.log(1); // 1という数値リテラル(式)
console.log(1 + 1); // 1 + 1(式)の評価値は2


const fn = function() { // 関数式も式の一つ
    return "hello";
};

文(Statement)

  • 処理する一ステップのことでセミコロン; で区切る
  • for文if文など文のひとつ
  • 式は文になれる→式文

式文

  • 文を書ける場所には式を書ける
  • 文となった式式文という

関数宣言と関数式の違い

  • 関数宣言は
  • 関数式は(; をつける)
JS
function learn(){ // 関数宣言
}

const read = function() { // 関数式
};

条件分岐

  • if文やfor文
  • 条件によって処理を切り替える

if文

  • if (条件式) {処理}
  • (条件式)Trueのとき処理される
  • falsyな値はFalseと判断される
JS
if ([]) {
  console.log("配列はTrue");
}
if ({}) {
  console.log("オブジェクトはtrue");
}
if ("もじ") {
    console.log("文字列はtrue");
}
// falsyな例
if (!"") {
    console.log("空文字はfalse");
}
if (!null) {
    console.log("nullはfalse");
}

else、else if文

  • 条件に一致しない部分をelseで指定する
  • 複数条件の場合else ifと書く
JS
// else else if
const version = "ES6";
if (version === "ES5") {
  console.log("ECMAScript 5");
} else if (version === "ES 6") {
  console.log("ECMAScript 2015");
} else if (version === "ES7") {
  console.log("ECMAScript 2016");
}

swich case 文

  • switch (条件式) {case1: 処理1; case2: 処理2;...}
  • 厳密等価===条件式とcaseを比較
  • 一致したら処理する
  • 必ずbreakをセットで使う(関数の処理内ならばreturnでも良い)
JS
- switch ("jp") {
  case "jp":
    console.log("こんにちは");
    break;
  case "us":
    console.log("Hello");
    break;
  case "cn":
    console.log("你好");
    break;
  default:// どの条件にも合致しない時
    break; 
}

ループ 反復処理

while文

  • 条件式の評価値がtrueなら反復処理をする
  • 条件式がfalseになったとき処理を抜ける
  • while (条件式){ 処理 }
JS
let x = 0;
console.log(`xの初期値: ${x}`);
while (x < 10) {
  console.log(x);
  x += 1;
}

console.log(`ループ処理終了 x : ${x}`);

do-while

  • whileの処理が先に実行されるバージョン
  • do { 処理 } while(条件式);
JS
let y = 20;
do {
  console.log(y);
  y -= 1;
} while (y > 10); // 20~11まで表示される

for文

  • 繰り返し範囲を指定した反復処理をかく
  • for (初期化式; 条件式; 増分式) { 処理 }
JS
// forでsum関数を実装する例
function sum(numbers) {
  let total = 0;
  for (let i = 0; i < numbers.length; i++) { // 配列の長さ分繰り返す
    total += numbers[i];
  }
  return total;
}

console.log("合計は:" + sum([1, 2, 3, 4, 5])); // => 合計は15

forEach: 配列の反復処理

  • 配列の各要素それぞれに順に処理をするメソッド
  • 書き方:配列名.forEach( 引数 => {処理});
  • 配列の先頭からコールバック関数の引数に要素が入る
JS
// arrayのforEach
const num_array = [1, 2, 3];
num_array.forEach(num => { // numはコールバック関数の引数
  console.log(num); // => 1, 2, 3
});

sum関数をforEachで書く例

JS
// sum関数をforEachで実装する
function sum(numArray) {
  let total = 0;
  // numにnumArrayがひとつづつ入る
  total = total + num;
  numArray.forEach((num) => {});
  return total;
}

console.log("合計は:" + sum([1, 2, 3, 4, 5]));

some関数

  • 配列.some(引数 => {ture/fasleを返す目関数});
  • または配列.some(別でfunction定義したコールバック関数);
  • tureが返った瞬間に反復を終了する
JS
// some関数
function isEven(num) {
  return num % 2 === 0;
}
const numbers = [1, 5, 10, 15, 20];
console.log(numbers.some(isEven)); // => true

filter(配列値をフィルターする)

  • 配列.filter(コールバック関数);
  • コールバック関数がTrueを返した要素のみを追加した新しい配列を返す
JS
function isEven(num) {
  return num % 2 === 0;
}

const numbers = [1, 5, 10, 15, 20];
// isEvenがTrueの値のみnubersから取り出す
console.log(numbers.filter(isEven)) // => [10, 20]

オブジェクトに対する反復処理

for in()

  • オブジェクトのプロパティに対して反復処理をする
  • ※意図しないプロパティまで列挙されてしまうので、なるべく使わない方がいい
  • for (プロパティのキー in オブジェクト) {プロパティに対する処理}
JS
// for-inでオブジェクトのプロパティを列挙
const objA = {
  "a": 1,
  "b": 2,
  "c": 3
}

for (key in objA) {
  const value = objA[key];
  console.log(`key:${key}, value:${value}`);
} 
// key:a, value:1 
// key:b, value:2 
// key:c, value:3

for of文

  • オブジェクトの値を取り出して反復処理
  • for(変数 of イテラブル) {処理}
  • イテラブルとは、反復処理の動作が定義されたオブジェクトのこと(配列、文字列、Map、Setなど)
JS
arrayX = [1, 2, 3];

for (value of arrayX) {
  console.log(value)
}
// 1
// 2
// 3

オブジェクト詳細

  • オブジェクトとはキーバリューが対になったもの
  • {"キー(プロパティ)": "値"};で定義
  • プロパティは""を省略して書ける
JS
const obj = {
    "key": "balue"
};
JS
// クオートの省略
const obj = {
    key: "balue"
};

変数名として利用できない記号をキーに含める時は"obj-a"の様にクオートで囲む

オブジェクトの値の省略

  • プロパティ名値に指定する変数名が同じ場合、値を省略できる
JS
const fruit = "apple";
fluits = {
  fruit
}
console.log(fluits); // {fruit: 'apple'}

Object ビルドインオブジェクト

  • =あらゆるオブジェクトの基になる実行環境に用意されたオブジェクト
  • new Object() で空のオブジェクトを作成※ {}(リテラル) と同じ
JS
const newObj = new Object();
console.log(typeof newObj); // => Object

オブジェクトを作成するとは、
Objectからインスタンスオブジェクトを作成すること。

プロパティの参照の注意

  • プロパティ名は暗黙的に文字列に変換される。
  • ブラケット記法ではプロパティ名に変数を使って参照できる
JS
const obj = {
    key: "value",
    123: 456,
    "my-key": "my-value"
};

console.log(obj[123]); // => 456
console.log(obj["123"]); // => 456
JS
const languages = {
    ja: "日本語",
    en: "英語"
};

cont myLang = "ja" // 変数をキーとして参照
console.log(langages[myLang]); // 日本語

変数へオブジェクトの値を一括で入れる(分割代入)

  • 分割代入を使って変数に値を入れる
  • const {変数1, 変数2...} = Obj;
  • プロパティ名と同じ変数に同じ値が入る
JS
const languages = {
    ja: "日本語",
    en: "英語"
};
const { ja, en } = languages;
console.log(ja); // => "日本語"
console.log(en); // => "英語"

プロパティの追加

  • オブジェクト.新しいプロパティ=値
  • 新しいキーとバリューを追加できる
  • むやみにプロパティを追加するのは✖
  • (なるべく最初の宣言の形を維持する)
JS
const newObj = new Object();t
// newKeyと値を追加
newObj.newkey = "new value.";
console.log(newObj.newkey); //=> new value.

プロパティの削除

  • delete オブジェクト.プロパティ(キー)
JS
delete newObj.newkey;
console.log(newObj); // => {} 空になる

Object.freeze() オブジェクトの固定

  • オブジェクトの変更を不可にする
  • プロパティの追加削除,値の変更が出来なくなる
JS
// オブジェクトの値を代入不可にする
const freezeObj = Object.freeze({key: "value"});
freezeObj.key = "new value";
console.log(freezeObj.key); //=> TypeError

image.png
TypeError: "key" is read-only

プロパティの存在を確認

  • 存在しないプロパティにアクセス=undefinedが返りエラーは出ないから値がundefinedの場合混乱する
  • **in, Object.hasOwn()**を使って確認する

in演算子

  • プロパティ名 in オブジェクト;
  • turefalseが返る
JS
const obj = { key: undefined };
// `key`プロパティを持っているならtrue
if ("key" in obj) {
    console.log("`key`プロパティは存在する");
}

Object.hasOwn()メソッド

  • Object.hasOwn(オブジェクト, "プロパティ");
  • 対象のプロパティが存在すればtureが返る(inと同様)
JS
// objに"key"プロパティが含まれるか確認
Object.hasOwn(obj, "key"); // ture false 

?. Optional Chainingでプロパティの存在確認

  • ネストしたプロパティは存在確認が大変
    • (widget.window.title) みたいな
  • ?.を使ってプロパティの値を取る
  • 値が存在すれば、なければその時点でundefinedを返す
JS
// ifを使ったプロパティの存在確認
 if (widget.window !== undefined && widget.window.title !== undefined) {
    //widget.windowとwidget.window.titleを順にみないといけない
};

// ?.で存在確認
widget?.window?.title; // undefinedかtitleの値が返る

三項演算子を使ってプロパティが定義済みか確認する例

JS
function printWidgetTitle(widget) {

// widget.window.titleが定義済み=>値
// widget.window.titleが未定義=>右辺の処理
    const title = widget?.window?.title ?? "未定義";
    console.log(`ウィジェットのタイトルは${title}です`);
}

オブジェクトの静的メソッド

  • Objectビルドインオブジェクトそのものに実装されているメソッド
  • 静的メソッド(スタティックメソッド)
  • toString hasOwnなど..

静的メソッドは、インスタンス元のオブジェクトから呼び出せるメソッドのことを言う。

例)

  • Object.keys: キーの列挙
  • Object.values: 値の列挙
  • Object.entries: [キー: 値]の列挙
JS
const testObj = {
  one: 1,
  two: 2,
  three: 3,
};

console.log(Object.keys(testObj)); // ['one', 'two', 'three']
console.log(Object.values(testObj)); // ['one', 'two', 'three']
console.log(Object.entries(testObj)); // [["one", 1], ["two", 2], ["three", 3]]

オブジェクトのマージ

  • Object.assign(ターゲット, obj1, obj2...)
  • オブジェクトの内容をコピーまたはマージする
JS
const objectA = { a: "a" };
const objectB = { b: "b" };

// 空配列mergedにA, Bの合わせ新しく定義
const merged = Object.assign({}, objectA, objectB); // { a: "a", b: "b" }
// objAは変更されmarged===objAとなる
const merged = Object.assign(objectA, objectB);

Object.assign()を使う場合は、
ターゲットに空オブジェクトを指定すると元の配列には影響がないので、一般的に上の様にする。

spread構文でのマージ

JS
const objX = {a: "a"};
const objY = {b: "b"};

const mearged = {
  ...objX,
  ...objY
};

console.log(mearged); // {a: "a", b: "b"}

オブジェクトのコピー

  • オブジェクトのコピーにもObject.assign({}, コピー元)として使う
JS
const copyObj = Object.assign({}, obj); // コピーされる

shallow copy

assignメソッドでできるのは shallow copy(浅いコピー) で、
ネストされたオブジェクトは複製されない。

deep copy

プロパティ値まで再帰的にコピーする場合は再帰処理を用いて自分でdeep copyを実装する

  • shallow copyする
  • もしキーにオブジェクトがあれば再帰的にコピー
JS
function deepClone(obj) {
    const newObj = shallowClone(obj);
    // プロパティがオブジェクト型であるなら、再帰的に複製する
    Object.keys(newObj)
        .filter(k => typeof newObj[k] === "object")
        .forEach(k => newObj[k] = deepClone(newObj[k]));
    return newObj;
}

配列

  • JavaScriptの配列は可変長
  • 値の追加や削除が出来る
    • push, pop
  • 配列のArray.メソッドには破壊的非破壊的メソッドがある。

Array.length

  • 配列の要素数を返す
JS
// 配列のメソッド
const arrayA = [1, 2, 3];
console.log(arrayA.length); // => 3

疎な配列と密な配列

  • 配列に空の要素がある配列→疎な配列
  • 疎を参照するとundefinedが返る
JS
// 疎な配列
const arrayB = [4, , 6];
console.log(arrayB[1]) // => undefined

Array.at(index):要素の参照

  • 配列の要素を参照する
  • Array.at(-1)など負の数で後ろから参照
  • (Array[Array.length -1]より楽)
JS
const array = ["a", "b", "c"];
//
console.log(array.at(0)); // => "a"
console.log(array.at(1)); // => "b"
// 後ろから1つ目の要素にアクセス
console.log(array.at(-1));

※配列[-1]と書くと、大抵の場合はundefinedが返ってしまう(無いプロパティへのアクセス)

Array.isArray(obj):配列かオブジェクトかの判断

  • true false が返る
  • typeof演算子では配列も[Object]と判定されるので判定できない
JS
const obj = {};
const array = [];
console.log(Array.isArray(obj)); // => false
console.log(Array.isArray(array)); // => true

分割代入

  • 配列もオブジェクト同様、分割代入が出来る
JS
const arrayN = ["first", "second", "third"];
const [first, second, third] = arrayN;

console.log(first); // first
console.log(second); // second
console.log(third); // third

hasOwnで疎かUndefinedか判定する

  • 配列は参照外、空、undefinedを参照するとどれもundefinedが返る
  • 指定したIndexが空なのかundefinedかを見分けるには
  • Object.hasOwn(配列, index)
JS
const arrayB = [4, , 6];
console.log(Object.hasOwn(arrayB, 1)); // false

falseなら要素自体が存在しない

indexOf lastIndexof:要素でインデックスを取得

  • Array.indexOf(要素)でインデックスの位置が返る
  • 一致するものが無いとき-1が返る
JS
const animals = ["サル", "", ""];
const birdIndex = animals.indexOf("");
console.log(birdIndex); // => 1

const fishIndex = animals.indexOf("")
console.log(fishIndex); // => -1

array.find: 条件に一致する要素を取得

  • 配列名.find(コールバック関数)
  • コールバック関数には条件を入れる
  • 一致する要素そのものが返る
JS
const colors = [{ color: "red" }, { color: "green" }, { color: "blue" }];

// find
const greenColor = colors.find((obj) => {
  return obj.color === "green";
});


console.log(greenColor); // {color: 'green'}


// 10より大きい`count`プロパティを持つ最初のオブジェクトを取得
const firstRecord = records.find((record) => {
    return record.count > 10;
});
console.log(firstRecord); // { date: "2020/12/2", count: 11 }

array.findIndex: 条件に一致するIndexを取得

  • 配列名.findIndex(コールバック関数)
  • コールバック関数には条件を入れる
JS
// findIndex
const blueColor = colors.findIndex((obj) => {
  return obj.color === "blue";
});
console.log(blueColor); // 2

Array.slise:範囲を指定して取得

  • 配列名.slise(始点, 終点);
  • 新しい配列として範囲を取り出す
JS
// slise
const numArray = [1, 2, 3, 4, 5];
console.log(numArray.slice(1, 4)); // [2, 3, 4]

Array.includes():要素が含まれているか

  • 配列に要素が含まれている場合、trueを返す
JS
const array = ["Java", "JavaScript", "Ruby"];
// `includes`は含まれているなら`true`を返す
if (array.includes("JavaScript")) {
    console.log("配列にJavaScriptが含まれている");
}

配列への追加・削除

array.push()末尾に追加

  • 配列名.push(追加要素)
  • 先頭への追加は
    • 配列名.unshift(追加要素)

array.pop():末尾を削除

  • 配列名.pop()
  • 先頭の削除は
    • 配列名.shift()
JS
const array = ["A", "B", "C"];
array.push("D"); // "D"を末尾に追加
console.log(array); // => ["A", "B", "C", "D"]

const poppedItem = array.pop(); // 最末尾の要素を削除し、その要素を返す
console.log(poppedItem); // => "D"
console.log(array); // => ["A", "B", "C"]
JS
const array = ["A", "B", "C"];
array.unshift("S"); // "S"を先頭に追加
console.log(array); // => ["S", "A", "B", "C"]

const shiftedItem = array.shift(); // 先頭の要素を削除
console.log(shiftedItem); // => "S"
console.log(array); // => ["A", "B", "C"]

array.splice(): 任意のインデックスの追加・削除

  • array.splice(インデックス, 削除する要素数);
  • array.splice(インデックス, 削除する要素数, ...追加する要素);
JS
const array = ["a", "b", "c"];
// 1番目から1つの要素("b")を削除
array.splice(1, 1);
console.log(array); // => ["a", "c"]
console.log(array.length); // => 2
console.log(array[1]); // => "c"
// すべて削除
array.splice(0, array.length);
console.log(array.length); // => 0

array.concat():配列の結合

  • 結合元配列.concat(結合したい配列)
  • 合体した新しい配列が作成される
JS
const array = ["A", "B", "C"];
const newArray = array.concat(["D", "E"]);
console.log(newArray); // => ["A", "B", "C", "D", "E"]

...: 配列の展開

  • spread構文で配列を任意の位置で展開
  • concatと異なりどこでも結合できる
JS
const charArray = ["a", "b", "c"];
const charsArray = ["x", ...charArray, "x"];
console.log(charsArray); // ['x', 'a', 'b', 'c', 'x']

array.flat:配列のフラット化

  • 多重の配列[[[]][]にする
  • flat化する深さは array.flat(深さ) で指定
  • 全階層フラットにする場合、引数にInfinityを指定する
JS
const multiArray = [[[1], 2], 3];
console.log(multiArray.flat(2)); //  [1, 2, 3]

array.lengthを利用した要素削除

  • 配列要素を一括削除する時、lengthプロパティに0を代入する
  • array.length = 0 // []空の配列に

破壊的・非破壊的メソッド

  • 配列操作には2つある
    • Mutable(破壊的)なメソッド
    • immutable(非破壊的)なメソッド
  • 出来るだけ非破壊の利用が望ましい
    • 破壊的:対象の配列自体を操作
    • 非破壊的:対象のコピーを操作する

image.png
https://jsprimer.net/basic/array/

例)pop push splise→破壊的
 .at(-1) ... toSpliced→非破壊的

配列の反復処理

forEach

  • 配列の要素を取出し先頭から処理する
  • 返値を返さない
  • (元の配列は操作しない)
JS
const array = [1, 2, 3];
const doubleArray = array.forEach((value, index, array) => {
 return value * 2;
});
console.log(doubleArray); // undefined

map

  • 配列の要素を先頭から処理する
  • 処理した結果を適応した新しい配列を返す
JS
const array = [1, 2, 3];
const doubleArray = array.map((value, index, array) => {
  return value * 2;
});
console.log(doubleArray); // [2, 4, 6]

filter

  • 配列から不要な要素を取り除く
  • コールバック関数はtrue falaseを返し
  • trueの要素だけ集めた新しい配列を返す
JS

const filterArray = array.filter((num) => {
  return num % 2 == 0;
}) 

console.log(filterArray); // [2]

reduce

  • 配列要素の合計値を返す
  • reduce(一つ目の値, 2つ目の値) => {return 一つ目の値 + 2つ目の値}
  • 配列の前から順に残りを足していくイメージ
JS
const sumValue = array.reduce((acc, curentValue) => {
 console.log(`acc:${acc}, curent:${curentValue}`);
 // acc:1, curent:2
 // acc:3, curent:3
 return acc + curentValue;
});
console.log(sumValue); // 6

groupBy

  • 配列の要素を条件でグループ分けし新たな配列を作成する
  • (フィルターとは違い条件に合致しないものもグループ化する)
JS
const array = [1, 2, 3, 4, 5];
const grouped = Object.groupBy(array, (currentValue) => {
    // currentValueが偶数なら"even"、そうでないなら"odd"の配列に追加される
    return currentValue % 2 === 0 ? "even" : "odd";
});
console.log(grouped.even); // => [2, 4]
console.log(grouped.odd); // => [1, 3, 5]

メソッドチェーン

  • [対象].mesod1().mesod2()...
  • メソッドをつなげて処理を連続させること→メソッドチェーンという
  • 簡潔に処理を書ける
JS
const array = ["a"].concat("b").concat("c");
console.log(array); // => ["a", "b", "c"]

例)

  • filterで年を絞り込み
  • mapで結果をまとめて返す
JS
// ECMAScriptのバージョン名と発行年
const ECMAScriptVersions = [
    { name: "ECMAScript 1", year: 1997 },
    { name: "ECMAScript 2", year: 1998 },
    { name: "ECMAScript 3", year: 1999 },
    { name: "ECMAScript 5", year: 2009 },
    { name: "ECMAScript 5.1", year: 2011 },
    { name: "ECMAScript 2015", year: 2015 },
    { name: "ECMAScript 2016", year: 2016 },
    { name: "ECMAScript 2017", year: 2017 },
];
// メソッドチェーンで加工処理を並べる
const versionNames = ECMAScriptVersions
    // 2000年以下のデータに絞り込み
    .filter(ECMAScript => ECMAScript.year <= 2000)
    // 各要素から`name`プロパティを取り出す
    .map(ECMAScript => ECMAScript.name);
console.log(versionNames); 
// => ["ECMAScript 1", "ECMAScript 2", "ECMAScript 3"]

文字列

文字へのアクセス

  • 配列風のアクセスと.at()がある
  • 文字列[index]
  • 文字列.at(±num)
JS
// 文字列
// 配列でアクセス
const str = "ABCD";
console.log(str[1]); // B
console.log(str[2]); // C

// String.prototype.atでアクセス
console.log(str.at(0)) // A
console.log(str.at(1)) // B
console.log(str.at(-1)) // D

atはStoringプロトタイプオブジェクトのメソッド。
carAtというメソッドもある(atとの違い:負のとき空文字を返す)

文字列の結合・分解

  • .split("") :文字列の分解
  • .join("") :文字列の結合
JS
// split 文字列の分解
const strings = "赤・青・黄";
let spStr = strings.split("");

console.log(spStr); // ["赤", "青", "黄"]
JS
// join 文字列の結合
spStr = spStr.join("&");
console.log(spStr); // 赤&青&黄

同時に使うことがよくある

jS
const tea = "玄米茶";
spTea = tea.split("").join("");

console.log(spTea); // 玄★米★茶

正規表現でのsplit()

正規表現でマッチを探してsplitできる

JS
// 1つ以上のスペースの正規表現➢ /\s+/
const space = "a  ba   c";
spSpace = space.split(/\s+/);

console.log(spSpace); // ['a', 'ba', 'c']

文字列の大小

JavaScriptは文字コードとしてUnicode、エンコードする方式としてUTF-16を採用している

  • 文字コード:文字を01のビットに変換したもの(Unicode)
  • エンコード:文字コードをさらにコンピュータ用に変換すること(UTF-8, UTF-16)

JS
// CよりDが、文字コードは後ろ
console.log("ABC" > "ABD"); // => false

文字列はコードユニットの並びで、
先頭から比較される。( コードユニットは例だと3つ)

文字の切り出し

文字列から任意の部分を切り出す

  • slice()
  • substring()

slice(始点,終点)

JS
const alphabet = "abcdefg";

console.log(alphabet.slice(0, 3)); // abc
console.log(alphabet.slice(2, 5)); // cde
console.log(alphabet.slice(-2)); // fg 

負の数は後ろから数える

substring(始点, 終点) ※負の数は"0"扱い

JS
console.log(alphabet.substring(0)) // abcdefg
console.log(alphabet.substring(3, 5)) // de
console.log(alphabet.substring(-1)) // abcdefg

いずれも非破壊的なメソッドで機能の違いもないので好みで使う。


文字列の検索

  • IndexOf()
  • lastIndexOf()

IndexOf("検索したい文字列")

  • 前方から検索して一致したインデックスを返す

lastIndexOf("検索したい文字列")

  • 後方から検索して一致したインデックスを返す
JS
const fruits = "ばななリンゴぶどうリンゴオレンジ";

console.log(fruits.indexOf("リンゴ")); // 3
console.log(fruits.indexOf("ぶどう")); // 6
console.log(fruits.lastIndexOf("リンゴ")); // 9 後ろ側のリンゴの先頭index

いずれのメソッドも一致しない場合-1を返す

JS
console.log(fruits.indexOf("ブドウ")); // -1
console.log(fruits.lastIndexOf("ブドウ")); // -1
  • 応用例
JS
// "いちご"が無い場合、indexOfは-1を返す
if (fruits.indexOf("いちご") != -1) {
  console.log("いちごがあります!");
} else {
  console.log("いちごはありません");
}

文字列の置換

特定の文字列を別の文字(列)に置き換える

  • replace()
  • replarceAll()

replace( "検索対象", "置きかえ文字")
replace( /正規表現/, "置きかえ文字")

JS
// 直接指定
const phrase = "にわにはにわにわとりがいる";
console.log(phrase.replace("にわとり", "ハト"));
// => にわにはにわハトがいる

// 正規表現
console.log(phrase.replace(/にわ/g, "ハト"));
// => ハトにはハトハトとりがいる

replaceAll("検索対象", "置きかえ文字")

  • replace()との違い
    • replace: 最初の一致しか変換しない
    • replaceAll: すべての一致を置きかえる
JS
const text = "0120-117-117";
const result = text.replaceAll("-", "($&)");
console.log(result);
// => 0120(-)117(-)117

コールバック関数を用いた複雑な変換

  • replace, replaceAllは第2引数にコールバック関数を渡せる
  • repllace("対象", ()=> { 処理 });
  • repllaceAll("対象", ()=> { 処理 });

複雑に年月日などの置き換えが可能

JS
function toDateJa(dateString) {
  // マッチした対象のみコールバック関数で置換処理
  return dateString.replace(
    /(\d{4})-(\d{2})-(\d{2})/g,
    (all, year, month, day) => {
      // allはマッチした文字列全体が入っている(未利用)
      return `${year}${month}${day}日`;
    }
  );
}
console.log(toDateJa("2024-01-02")); // 2024年01月02日

code point と code unit

  • JavaScriptはUTF-16でエンコードしたcode unit単位で文字を認識する
  • code point = unicodeに対応した1文字1IDの単位(実質的な1文字)
JS
// code point
const aiu = "あイウ";
console.log(aiu.codePointAt(0)); // 12354
console.log(aiu.codePointAt(1)); // 12452
// 16進数表示
console.log(aiu.codePointAt(0).toString(16)); // 3042
console.log(aiu.codePointAt(1).toString(16)); // 30a4

※JavaScriptは
code unitで文字を取扱うメソッドとcode pointで取扱うメソッドがある。

code unitの注意点

  • code unit = 1文字ではない!
  • 絵文字など複数code unitで表される文字がある
  • サロゲートペア という
JS
const ringo = "🍎"
console.log(ringo.length); // 2

※例として🍎はlength(code unit)が2つ

code pointで数える・扱うには?
  • array.from()で配列に変換する
  • 正規表現で\u{unicode}フラグを使う
JS
const ringo = "これは🍎です"
console.log(ringo.length); // 7

const codePoints = Array.from(ringo);
console.log(codePoints); // ['こ', 'れ', 'は', '🍎', 'で', 'す']
console.log(codePoints.length); // 2

文字数(code point数)がlengthで出力

JS
console.log("\u{1F34E}"); // => "🍎"

正規表現はデフォルトでcode unit単位で処理される。
code pointでmatchなどさせるなら\uでUnicodeであることを示す。


ラッパーオブジェクト

  • プリミティブ値のプロパティメソッドにアクセスすると自動で生成されるオブジェクト

以下は明示的に生成しているが、
自動で作成され参照後破棄されている。

JS
// testStrはプリミティブだが、メソッドが呼べる
const testStr = new String("input Value");
console.log(testStr.toUpperCase()); // INPUT VALUE
JS
// 自動でラッパーオブジェクトに変換される
const testStr = "input Value";
console.log(testStr.toUpperCase()); // INPUT VALUE

プロパティとメソッドって違うん?

プロパティ≒データとメソッドのこと
データの方をデータプロパティという(.lengthとか)

動作 説明
プロパティアクセス str.length ラッパーオブジェクトのプロパティにアクセスする。
メソッドの呼び出し str.toUpperCase() ラッパーオブジェクトのプロパティ(関数)を実行する。

ラッパーオブジェクトを持つ型

  • nullundefined以外のプリミティブ
ラッパーオブジェクト プリミティブ型
Boolean 真偽値 truefalse
Number 数値 12
BigInt BigInt 1n2n
String 文字列 "文字列"
Symbol シンボル Symbol("説明")

スコープ

  • スコープとは変数関数アクセス可能な範囲
  • スコープ内の変数や関数は他スコープからアクセスできない

関数スコープ

  • 関数内の仮引数やローカル変数はその関数内でのみ使える
JS
function testFunc(x) {
  console.log(x);
}
testFunc("hello"); // "hello"
console.log(x); // x is not defined

ブロックスコープ

  • ブロック {} で囲まれたスコープ
  • ifforなどで使われているのと同じ
JS
{ // ブロックスコープ
  let blockStr = "abc";
}
console.log(blockStr); // blockStr is not defined

for of文などはループ毎にスコープを作成する

スコープチェーン

  • ネストされたスコープで変数や関数を参照する際
  • 内側=>外側に向かってたどる仕組みのこと
  • 内側をINNER, 外側をOUTERと呼ぶ場合アリ
JS
{
    // OUTERブロックスコープ
    {
        // INNERブロックスコープ
    }
}

内側のスコープから外側の変数や関数は
参照できる
外側から内側は参照できない

グローバルスコープ

  • どのブロックからも参照できる一番外のスコープ
  • ここに変数を定義するとグローバル変数となる
JS
const globalVariable = "グローバル変数"
// ビルドインオブジェクトもグローバル
Array;
isNaN;

!変数の隠蔽に注意

  • 内側のスコープ外側の変数名を定義すること
  • => 意味が上書きされるので先の定義を参照できなくなる

関数などを活用して小さなスコープでプログラムを書く事が重要。


var functionの変数巻上げ

  • varfunction
  • 定義の前に参照できる巻上げが起こる
  • ※Hoisting(ホイスト)と呼ばれる

varの巻き上げ

  • varは宣言より手前で参照できてしまう
  • スコープも無視され、最も近い関数スコープの先頭に宣言されたような挙動がおこる
JS
// var num; ←ここに巻き上げ
console.log(varNum); // undefined
{
  var varNum = 123;
  console.log(varNum); // 123
}

避け方
varを使わずletを利用する

JS
console.log(varNum); // varNum is not defined
// reference Errorとなり分かる
{
 let varNum = 123;
 console.log(varNum); // 123
}

関数function()の巻き上げ

  • 関数宣言より先に呼び出せる挙動
  • 関数そのものが先頭に移動したような形になる
  • varの巻き上げに比べ悪影響は少ない
JS
hello(); // => "Hello"

function hello(){
    return "Hello";
}

※ただしvar hello = ()=> {}のような関数式varの巻き上げを食らうので注意

即時実行関数(IIFE)

  • グローバルスコープを汚さないための関数の書き方
  • 初期化処理などにも使う
  • varでも書けるが、昨今は{}const, letを使って書く
JS
(function() {
    // 関数のスコープ内でfoo変数を宣言している
    var foo = "foo";
    console.log(foo); // => "foo"
})();

即時実行関数(IIFE)の書き方

  • (無名関数{})();で即時実行される
JS
(function() {
  // 関数の内容
})();

constを使った例

JS
(function() {
    const foo = "foo";
    console.log(foo); // => "foo"
})();

この場合、グローバルスコープを汚さない目的なら以下の様にブロックを使えばよい

JS
{
// 上の即時実行関数と同じ処理
    const foo = "foo";
    console.log(foo); // => "foo"
}

クロージャ

  • 関数が外スコープの変数を参照している限り、その変数の値を覚えている仕組み
  • => 関数内の変数に状態を作り出せる

クロージャの書き方

  1. 関数を定義
  2. 関数内に状態保持の変数を作成
  3. return状態保持の変数を操作する関数
JS
function createCounter() {
  // クロージャの保持対象は'count'変数
  let count = 0;

  // createCounterはincrement関数を返す
  return function increment () {
    count = count + 1;
    console.log(count);
  };
}

// 呼び出しは新たな変数に代入し()で処理を呼ぶ
const counter1 = createCounter();
console.log(counter1) // ƒ increment () 

counter1(); // 1
counter1(); // 2

プロパティVSクロージャ

状態はオブジェクトとプロパティでいいのでは?

➢プロパティは外部から参照・操作できる

変数の状態を隠蔽したい時は
クロージャを利用する

  • クロージャの例
JS
function createCounter() {
  let count = 0;  // この変数は外部からアクセスできない
  return function() {  
    count++; 
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 1
  • オブジェクト&プロパティの例
JS
const counter = {
  count: 0,  // countプロパティを作成
  increment: function() {
    this.count++;  
    console.log(this.count);
  }
};

counter.increment(); // 1

// countは外部から直接アクセスできる
console.log(counter.count);  // 3

this キーワード

  • this呼出し方によって意味が変わるキーワード
  • ある時はオブジェクト、又ある時はプロパティundefinedなど...
  • コードの簡略化やオブジェクトの関係を簡潔に表すために使う

strict modeのthis

  • 通常、トップレベルに書かれたthisは```グローバルオブジェクトを指す
    • ブラウザ:Window
    • node.js:Global
JS
// ブラウザ
console.log(this); // => Window
// node
console.log(this); // => Global

実行コンテキスト(※環境)によって示すオブジェクトが変わるのでバグの原因になる

strict modeならthis= undefined

  • strict modeではthisundefinedが返される
  • モジュールstrict modeが常に有効。よってthisundefinedになる
HTML
<script type="module">
// 実行コンテキストは"Module"
console.log(this); // => undefined
</script>

関数とメソッドにおけるthis

  • 関数とメソッドにおいてはArrow Function通常の関数で挙動が違う
  • 通常の関数:呼び出し時に決定
  • ArrowFanction:定義した時に決定

メソッド呼出し(通常の関数)

  • メソッドは何らかのオブジェクトに属する
  • thisは呼出し時に決まる
JS
const person = {
    fullName: "Brendan Eich",
    
    sayName: function() { 
        return this.fullName; // `person.fullName`と書いているのと同じ
    }
};

console.log(person.sayName()); // "Brendan Eich"

上記例では、this
ベースオブジェクト(person)を示す文言になる

※ベースオブジェクトとは
  • オブジェクト.メソッド()
  • オブジェクト.データプロパティ
  • として呼ぶ際の左のオブジェクト.の部分

呼出し時にthisが決まる弊害

通常関数のthis呼出し時に参照元が決まるから、問題が起こる

  • メソッドを外部の変数に代入して実行したとき
    • オブジェクトのスコープ外なのでthisが行方不明になる(globalを指しちゃう)
JS
const person = {
    fullName: "Brendan Eich",

    sayName: function() { 
        return this.fullName; // `person.fullName`と書いているのと同じ
    }
};
// `person.fullName`を出力する
console.log(person.sayName());

const say = person.sayName;
// personのsayNameだけ取出しsayに代入
// `this`はundefinedとなってしまう
console.log(say());

対処法

  • Functionオブジェクトの専用メソッドでthisを指定する
    • 関数.call(thisの対象, 引数...);
    • 関数.apply(thisの対象, [引数]);
    • 関数.bind(thisの対象, 引数);

※bindは関数にthisを拘束した新たな関数を作成

JS
"use strict";
function say(message) {
    return `${message} ${this.fullName}!`;
}
const person = {
    fullName: "Brendan Eich"
};
// いずれもthisを明示的に指定するメソッド

console.log(say.call(person, "こんにちは"));
console.log(say.apply(person, ["こんにちは"]));
const sayPerson = say.bind(person, "こんにちは");
// "こんにちは Brendan Eich!"

その他、callback関数でthisを使う場合も問題がある

Arrow Functionのthis

  • Arrow Functionでは1つ外側のthisを引き継ぐ
  • 定義時にこのthisは固定され文脈により変更されない
JS
const obj = {
  method() {
    const arrowFunction = () => {
    // methodにおけるthis(=obj)を引き継ぐ
      return this;
    };
    // methodはarrowFunction()を返す
    return arrowFunction();
  },
};

console.log(obj.method()); // obj

静的なthisの決定を確認

JS
const arrow = obj.method(); 
// functionと違いthisが静的に決定する為、Globalではなくobjが返る
console.log(arrow); // obj

クラス

  • クラス動作状態を定義した構造のこと
  • インスタンスクラスから生成したオブジェクト
  • インスタンスはクラスに定義した構造を継承する

クラスの定義方法(2つ)

  • クラス宣言 class ClassName{}
  • クラス式 variable = class Classname{}
JS
// クラス宣言文
class MyClass {
  constructor() {
  }
}

// クラス式
const AnonymousClass = MyClass {
    constructor()
}

クラス定義では
慣習的に大文字で始まるパスカルケースを使う。例) MyNewClass


クラス式ではクラス名を省略できる

無名クラスという書き方

JS
const MyClass = class {
  constructor() {
  }
}

constructor関数

コンストラクタとはクラス=>インスタンスを作成する時に、
インスタンスの状態の初期化をするメソッドのこと。

  • constructor(){コンストラクタの処理}
  • (クラスインスタンス化したとき自動的に呼び出される)
JS
class MyClass {
  constructor() {
  // 初期化に関する処理
  }
}

class MyClass {
  // コンストラクタの省略
}

コンストラクタは処理が無ければ省略可能である

クラスのインスタンス化new

  • クラスからインスタンス作成することをインスタンス化という
  • new class名(); →でインスタンス化

インスタンスがどのクラス空作成されたかチェックする際はinstansof:が便利。

JS
// インスタンス化
const myClass = new MyClass();
// 即時でもインスタンス化可能
console.log(new MyClass()); // MyClass {}[[Prototype]]: Object

console.log(myClass instanceof MyClass); // true

処理のあるコンストラクタの例

以下の例でコンストラクタは

  1. インスタンス化時、仮引数x yを受け取る
  2. this(生成したコンストラクタ)にプロパティx yを設定する
  3. x yに仮引数値を代入
JS
class Point {
  constructor(x, y) {
    // this.x, this.yは
    // インスタンスのプロパティになる
    this.x = x;
    this.y = y;
  }
}

console.log(new Point(1, 2)); // {x: 1, y: 2}

this はコンストラクタにおいて生成したコンストラクタを示す

注意:コンストラクタ関数でオブジェクトを返すのは✖

コンストラクタでreturnした値はnewした際の返り値になる
➢本来newは生成したインスタンスを返すべきなので、好ましくない。

JS
class Square {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    return { x, y }; // 別のオブジェクトを返す
  }
}

console.log(new Square(100, 200)); 
// Squareクラス"ではない"クラス{x: 100, y: 200}

プロトタイプメソッド

  • JavaScriptのクラスが持つメソッド
  • あるクラスから作成されたコンストラクタ間で参照が共有される
  • (クラスのメソッドは全てこれ)
メソッドの定義

メソッド名(){メソッドの処理}

JS
class Counter {
  constructor() {
  // thisはCounterのインスタンスを示す
    this.count = 0;
  }
  // incrementメソッドを定義
  increment() {
    this.count++;
  }
}
メソッドの呼出し

コンストラクタ.メソッド(引数)

JS
const counterA = new Counter();
const counterB = new Counter();

// メソッド呼出し
counterB.increment(); // countプロパティ: 0 -> 1

プロパティの参照:コンストラクタ.プロパティ

JS
// プロパティ確認
console.log(counterB.count) // 1

メソッドの共有

CounterAとCounterBでは、
incrementメソッドは共有されている

JS
// 以下はtureになる
console.log(CounterA.increment === counterB.increment);

この様にプロトタイプオブジェクトの仕組みでは、
同クラスの別インスタンス同士でメソッドの参照を共有する

アクセッサプロパティgetter setter

  • クラスのプロパティを参照・代入する際に使うメソッド
    • 参照は getterを使う
    • 代入は setterを使う

(メソッドだが使い方はプロパティチックな為、アクセッサプロパティと呼ぶ)

なぜアクセッサプロパティが必要なのか?

秘匿性や安全性、利便性にメリットがあるから。

目的 説明
データのカプセル化 内部データを隠しつつ安全にアクセス・更新する。
動的な処理の追加 値の取得・設定時にバリデーションや計算を行える。
データの整合性確保 不正な値の設定を防ぎ、一貫性を保つ。
柔軟なAPI設計 外部からの使い方を変えずに内部実装を調整可能。

書き方

  • コンストラクタ定義後
  • get プロパティ名(){return 返値}
    • ※ returnはgetterにおいて必須
  • set プロパティ名(仮引数){set処理}
    • ※ 値を返す必要は無い
JS
class NumWrapper {
  constructor(value) {
    return (this._value = value);
  }
  // getter
  get value() {
    return this._value;
  }
  // setter
  set value(newValue) {
    this._value = newValue;
  }
}

get set呼び出し

  • get
    • インスタンス名.プロパティ名
  • set
    • インスタンス名.プロパティ名 〇〇処理
JS
// インスタンス化
const numWrapper = new NumWrapper(10);

// getで表示
console.log(numWrapper.value); // 10

// setで代入
numWrapper.value = 30;
console.log(numWrapper.value); // 30
注意:_から始まるプロパティ

プロパティには直接参照されたくない場合があり、_valueの様にする慣習があった
(現在は**#value**のようなPrivate class fieldという機能が用意されている)

クラスフィールド

  • インスタンスの持つプロパティをより簡単に指定するための構文
  • 外部からアクセス出來るクラスフィールドを、publicクラスフィールドという

プロパティをプロパティ = 初期値と書ける

JS
class MyTestClass {
  // クラスフィールドによるプロパティ
  property1 = "Class Field";
  // コンストラクタによるプロパティ
  constructor(arg) {
    this.property2 = arg;
  }
}

const myNewClass = new MyTestClass(2);
console.log(myNewClass.property1) // "Class Field"
console.log(myNewClass.property2) // 2

初期値を省略したプロパティ(クラスフィールド利用。省略で初期値はundefinedとなる)

JS
class Loader {
    loadedContent;
    load() {
        this.loadedContent = "読み込んだコンテンツ内容";
    }
}

クラスフィールドのthis

クラスフィールドのthisクラスのインスタンスを示す

JS
class Counter {
    count = 0;
    // thisはCounterインスタンスを示し、
    // upはそのincrementメソッドを参照している
    up = this.increment;
    increment() {
        this.count++;
    }
}

この性質どう使う?
➢ArrowFunctionと組み合わせて、

関数としてメソッドを実行する際におこる、thisの呼出し時決定の性質によるエラーに対処できる

JS
class Counter {
  count = 0;

  increment() {
    // thisはCounterインスタンスを指す
    this.count++;
  }
}

const newCounter = new Counter();

// incrementを変数に代入し、次で呼出す
// thisがスコープを外れundefinedになる
// エラーが発生
const increment = newCounter.increment; 
increment(); // Uncaught TypeError

上記はメソッドを関数として呼ぶ際、
thisがglobalオブジェクトを指してしまう**ことが原因。
(strict modeではundefined)

対処

クラスフィールド上でメソッド呼出しをArrowFunctionで定義すると、thisが固定される

JS
class Counter {
  count = 0;

  // countUpフィールドに関数式
  // thisがインスタンスに固定される
  countUp = ()=> {
    this.increment(); 
  }
  increment() {
    this.count++;
  }
}

const newCounter = new Counter();

const up = newCounter.up; 
up; // 正しくincrement

Privateクラスフィールド

  • クラスフィールドを#fieldと記述
  • インスタンス化後にクラス内からのアクセスに限定できる※プロパティ参照などできない
  • getter setterと併せて使える
JS
class TestClass {
  _value; // フィールド定義
}

const testClass = new TestClass();
console.log(testClass._value); // undefined

上記例では_valueフィールドにアクセスできる

private化
#valueで参照した際構文エラーでアクセス不可にできる

JS
class TestClass {
  #value; // フィールド定義
  constructor() {
    this.#value = undefined;
  }
}

const testClass = new TestClass();
console.log(testClass.#value); // error

静的メソッド

  • クラスをインスタンス化せず利用できるメソッド
  • クラスメソッドとも呼ぶ
  • メソッドの前にstaticをつける

数値を受けとりインスタンス化する静的メソッドofを実装した例

JS
class ArrayWrapper {
  constructor(array = []) {
    this.array = array;
  }

// ...valueで入力を配列に変換して受け取る(残余引数)
  static of(...items) { 
    return new ArrayWrapper(items); // ArrayWrapperをインスタンス化
  }

  get length() {
    return this.array.length;
  }
}

const ArrayWrapperA = new ArrayWrapper([1, 2, 3]);

// new でインスタンス化せずofメソッドを利用する
// of内でインスタンスを生成
const ArrayWrapperB = ArrayWrapper.of(1, 2, 3);
console.log(ArrayWrapperB.length)

また上記ofnew ArrayWrapperは、thisでも良い

JS
static of(...items){
    return new this(items);
}

静的クラスフィールド

  • クラス自体(Prototypeオブジェクト)に設定されたフィールド(プロパティや変数)
  • static フィールド名
JS
//  静的クラスフィールド
class Colors {
  static red = "";
}

// インスタンス化せずクラスフィールドを呼び出す
console.log(Colors.red) // "赤"

また、#(private)をつけて外部から秘匿もできる

JS
//  静的クラスフィールド
class Colors {
  static #red = "";
}


// 非公開のプロパティにアクセスしているので
// 未定義undefinedが返る
console.log(Colors.red) // undefined

プロトタイプに定義したメソッド、インスタンスに定義したメソッド

  • プロトタイプに定義したメソッド
    • クラスにメソッド構文method(){}で定義
    • プロトタイプオブジェクトとして保存
    • インスタンス間で共有
  • インスタンスに定義したメソッド
    • クラスから作成したインスタンス毎に定義されるメソッド
    • アロー関数method = ()=> {}で定義

プロトタイプチェーン

  • インスタンスにプロトタイへの参照を記録する
  • インスタンスからプロパティを探す際、インスタンス➢プロトタイプオブジェクトまで探索する機能
JS
class MyClass {
    method() {
        console.log("プロトタイプのメソッド");
    }
}
const instance = new MyClass();
// インスタンスには`method`プロパティがないため、プロトタイプオブジェクトの`method`が参照される
instance.method(); // "プロトタイプのメソッド"
// `instance.method`の参照はプロトタイプオブジェクトの`method`と一致する
const Prototype = Object.getPrototypeOf(instance);
console.log(instance.method === Prototype.method); // => true

  • class1.prototypeclass2.prototype は異なるオブジェクトで、個別にメソッドやプロパティを持つ。
  • 両者は共通で Object.prototype を参照し、Object.prototype のメソッドは利用可能。
  • クラス間でメソッドやプロパティを共有するには、継承を使う必要がある。
  • 継承により、サブクラスの prototype は親クラスの prototype を参照する。

継承

  • クラスの構造や機能を引き継いだクラスを作ること
  • extendsキーワードを使う

    extends Parentで親クラス(継承元)のメソッドを継承し呼び出す例
JS
class Parent {
    name; // プロパティ定義
  hello = (name) => {
    this.name = name; //プロパティに代入
    console.log(`hello, ${this.name}`);
  };
}

class Child extends Parent {
    // Parentのメソッドhelloを継承
}

const childClass = new Child;
// 変数として呼出し
const x = childClass.hello;
x("wawawa"); // hello, wawawa

super() 親クラスのコンストラクタを継承

  • 単純な継承のみなら、自動でコンストラクタ・メソッドは勝手に継承される
  • super()を使う場合↓
    • 子クラスで独自のコンストラクタを定義する
    • メソッドをオーバーライドする
JS
class Parent {
  constructor(...args) {
    console.log("constructor1:" + args);
  }
}

// ---Parentコンストラクタを引き継いだ上
// ---独自に処理を追加する
class Child extends Parent {
  constructor(...args) {
    // 親クラスにもargsを引き継ぐ
    super(...args);
    console.log("constructor2:" + args);
  }
}

const child = new Child(1,2); 
// constructor1:1,2
// constructor2:1,2

super()をつけないで新たなコンストラクタを定義するとエラーになる

JS
class Child extends Parent {
  constructor(...args) {
    console.log("child constructor:" + args);
  }
}

image.png


クラスフィールド(プロパティ)の継承

  • クラスのフィールドもインスタンスに継承される
  • この場合インスタンスのフィールドとして定義される

※親クラスでprivateなフィールドは継承先でもprivateとなる

JS
class Parent {
  field = "field.";
  #privateField = "private field.";
}

class Child extends Parent {
  // フィールドは両方継承
  showPrivate(){
    console.log(this.#privateField);
  }
}

const child = new Child();
console.log(child.field); // "field."
child.showPrivate; // SyntaxError: Private field

※そもそもフィールドメソッドは異なる

  • フィールド:クラスにおけるプロパティ。状態を保持するためのもの
  • メソッド:クラスやオブジェクトにおける振る舞いを定義するもの

継承=すべてを複製”ではない!”

  • メソッドは親クラスのものを再利用(参照)する(メソッドチェーン)
  • フィールドはコンストラクタに複製されたものを参照する
特徴 フィールド メソッド
役割 インスタンスの状態を保持するプロパティ インスタンスの動作を定義する関数
格納される場所 インスタンスごとに格納される プロトタイプチェーンを通じて共有される
インスタンスごとの独立性 インスタンスごとに独立した値を持つ すべてのインスタンスで共有される

superプロパティ

  • 子クラスのプロトタイプメソッドから親クラスのプロトタイプメソッドを呼び出す
JS
class Parent {
    method() {
        console.log("Parent.prototype.method");
    }
}
class Child extends Parent {
    method() {
        console.log("Child.prototype.method");
        // `this.method()`だと自分(`this`)のmethodを呼び出して無限ループする
        // そのため明示的に`super.method()`を呼ぶことで、Parent.prototype.methodを呼び出す
        super.method();
    }
}

matome:クラスとプロトタイプとインスタンスの関係

  • クラス
    • 構造の設計図のようなもの
    • 値と振る舞いを定義されている
  • プロトタイプオブジェクト
    • インスタンス間で共通するデータやメソッドを持つオブジェクト
    • "クラス"を定義した段階で自動作成される
  • インスタンス
    • クラスからできた実体
    • 独自のメソッドやフィールドを持てるし、プロトタイプのデータやメソッドを参照し利用したりもできる

例外処理 try...catch

例外発生時の特別な処理をする

  • try{対象} cach(error){エラー時の処理}
  • finaly{}
    • errorに関係なく必ずtry節の後に実行される処理
JS
try {
  console.log("step1"); // step1
  // error原因の処理
  undefinedFunction();

  // error以降は実行されない
  console.log("step2");
} catch (error) {
  console.log(error.message); // undefinedFunction is not defined
} finally {
  console.log("step3"); // step3
}

throw エラーオブジェクトを投げる

  • throw new Error()でエラーを明示的にで投げる(Errorは他の基本的なエラーも使える)
  • catch節でエラーを受け取り
JS
try {
  // catchのerror変数に投げたエラーが格納される
  throw new Error("例外が投げられました");
} catch (error) {
  console.log(error.message); // "例外が投げられました"
}

Error()の部分は他の種類のエラーも用いる場合がある

  • Errorオブジェクト
    • Error : あらゆるエラー
    • Reference Error : 存在しない参照へのアクセス
    • Syntax Error : 存在しない構文
    • Type Error : 型が期待と異なる

Reference Errorの例

JS
// reference error
try {
  console.log(x);
} catch (error) {
    console.log(error instanceof ReferenceError); // true
    console.log(error.name) // "Reference Error"
    console.log(error.massage) // "エラーメッセージ"
}

これらの内臓されたエラーをビルドインエラーという

```console.error

  • console.error()でスタックトレースを表示できる。console.logよりデバッグに有利
  • ※スタックトレース:エラーの詳細を追跡したログ
JS
function fn() {
    console.log("メッセージ");
    console.error("エラーメッセージ");
}

fn();

image.png


非同期処理

  • 非同期的なタイミングで実行される処理
  • ⇔同期処理。タスクの順番通り処理する
  • メインスレッドで実行される

setTimeout()

  • 指定した時間経過後にコールバック関数を実行する
  • setTimeout(()=> {callBack}, ミリ秒);
    • ※一般的にはArrowFanction形式で引数渡す
JS
setTimeout(()=> {
    console.log("5秒後に表示");    
}, 5000);

非同期によって、処理の完了を待たず次を実行できる

JS
setTimeout(() => {
  console.log("task B");
}, 5000);

console.log("task C");
// task A
// task C
// task B

非同期の例外処理

  • 非同期処理で発生したエラーはtry...catchで捕捉できない。
JS
try {
    setTimeout(() => {
throw new Error()
      }, 5000);
} catch (error) {
    // エラー時処理はスルー
        console.log("エラーが発生しました")
}
// try内を実行後に非同期処理
// エラーが5秒後に発生。

↓catchの内容はスキップされる

image.png

このエラーをキャッチするのであれば以下の様に、setTimeout内でtry...catchする

JS
setTimeout(() => {
  try {
    throw new Error();
  } catch (error) {
    console.log("エラーが発生しました");
  }
}, 5000);

Promeiseオブジェクト

  • 関数内などで

    • return new Promise(resolve, reject) =>{ 非同期処理したい内容 }
  • 関数を実行時に

    • 関数名().then(()=>{A}).catch((B)=>{})
    • Aには成功時にしたい処理
    • Bには失敗時にしたい処理

例:ランダムに処理が成功・失敗するコード

JS
function fetchData() {
    return new Promise((resolve, reject) => {
      const isSuccess = Math.random() > 0.5; // 成功するか失敗するかをランダムに決める
  
      setTimeout(() => {
        if (isSuccess) {
          resolve("データの取得に成功しました!"); // 成功時
        } else {
          reject("データの取得に失敗しました..."); // 失敗時
        }
      }, 2000); // 2秒後に結果を返す(非同期処理のシミュレーション)
    });
  }

image.png

基本的な書き方は以下

JS
function asyncFunc() {
  return new Promise((resolve, reject) => {
    // 非同期でしたい処理
    // なんらかの結果がでる
    if (成功条件) {
      resolve(); // これが実行されると成功
    } else {
      reject(); //これが実行されると失敗
    }
  });
}

// 呼出し
asyncFunc()
  .then(() => {
    成功時の処理;
  })
  .catch(() => {
    失敗時の処理;
  });

Promiseオブジェクトはresolve()reject()の実行で状態が変化。
実行中・成功・失敗の状態を表す。

  • resolve()関数が実行されると成功(Fulfilled)状態
  • reject()関数が実行されると失敗(Rejected)状態
  • 成功も失敗もまだない(保留中)(Pending)状態

image.png

Promiseチェーン

  • Promiseによる非同期処理をメソッドチェーンで繋ぐこと
  • 連続で非同期処理を実行できる
JS
Promise.resolve()
  // promiseインスタンスが返る
  .then(() => {
    console.log("成功1");
  })
  .then(() => {
    console.log("成功2");
  })
  .then(() => {
    console.log("成功3");
  })
// 成功1
// 成功2
// 成功3

ちなみに失敗が間に挟まると、処理を書かない限りcatch処理成功とし、続くthenは実行される。

JS
Promise.reject()
  // promiseインスタンスが返る
  .then(() => {
    console.log("成功1");
  })
  .then(() => {
    console.log("成功2");
  })
  .catch(()=>{
    console.log("失敗!")
  })
  .then(() => {
    console.log("成功3");
  })
// 1, 2はスキップ
// 失敗!
// 成功3

例)ユーザ情報を得るために、メソッドチェーンを利用する

JS
// サーバーからユーザー情報を取得する関数
function getUserInfo(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const user = { id: userId, name: "Taro", age: 25 }; // ダミーデータ
      resolve(user); // 成功時、ユーザー情報を返す
    }, 1000); // 1秒後にユーザー情報を返す
  });
}

// ユーザーのプロフィールを更新する関数
function updateProfile(user) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      user.name = "Taro Yamada"; // 名前を変更
      resolve(user); // 更新後の情報を返す
    }, 1000); // 1秒後に更新された情報を返す
  });
}

// 実行例
getUserInfo(1)
  .then((user) => {
    console.log("ユーザー情報:", user); // ユーザー情報を表示
    return updateProfile(user); // ユーザー情報を使ってプロフィールを更新
  })
  .then((updatedUser) => {
    console.log("更新後のユーザー情報:", updatedUser); // 更新後の情報を表示
  })
  .catch((error) => {
    console.error("エラー:", error); // エラーがあれば表示
  });
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?