3. JavaScript 基本文法
変数宣言
const TAX_RATE = 0.1; // 再代入不可(基本はconstを使う)
let total = 0; // 再代入可能
// var は使わない(スコープの問題があるため)
データ型・型変換
// ── 型の確認 ──
typeof "hello" // "string"
typeof 42 // "number"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object" ← 歴史的バグ、nullチェックには === null を使う
Array.isArray([]) // true
// ── 型変換 ──
Number("123") // 123
Number("abc") // NaN
Number("") // 0 ← 空文字は0になるので注意
parseInt("100円") // 100 ← 先頭の数値部分だけ変換
parseFloat("3.14")// 3.14
String(500) // "500"
Boolean(0) // false (0, "", null, undefined, NaN → false)
Boolean("text") // true (それ以外は全てtrue)
// ── NaNの判定 ──
Number.isNaN(value) // NaNかどうか(isNaN()よりこちらを推奨)
Number.isFinite(42) // true(有限の数値かどうか)
比較演算子
// === 厳密等価(型も値も一致)← 基本はこちらを使う
1 === 1 // true
1 === "1" // false(型が違う)
// == 抽象等価(型変換してから比較)← バグの元なので避ける
1 == "1" // true
0 == false // true
null == undefined // true
// 大小比較
5 > 3 // true
5 >= 5 // true
論理演算子
// AND / OR / NOT
true && false // false
true || false // true
!true // false
// 短絡評価(ショートサーキット)
const name = user && user.name; // userがtruthyならuser.nameを返す
const value = input || "デフォルト"; // inputがfalsyなら右辺を返す
// Null合体演算子 ??(null/undefinedのみ右辺を返す)
const qty = order.quantity ?? 1;
// || との違い: 0 || 1 → 1, 0 ?? 1 → 0(0はnullでもundefinedでもない)
truthy と falsy
// ── falsy な値(if文でfalseとして扱われる)──
// false, 0, ""(空文字), null, undefined, NaN の6つだけ
if (!false) { } // true
if (!0) { } // true
if (!"") { } // true
if (!null) { } // true
if (!undefined) { } // true
if (!NaN) { } // true
// ── truthy な値(上記以外はすべてtrue)──
if ("0") { } // true(文字列の"0"はtruthy!)
if ([]) { } // true(空配列もtruthy!)
if ({}) { } // true(空オブジェクトもtruthy!)
// ── POS実用例 ──
// 在庫があるかチェック(0はfalsy)
const stock = 0;
if (stock) {
console.log("在庫あり"); // ← stock=0 だと実行されない
}
// 数値の0を正しく判定するなら明示的に比較する
if (stock !== undefined && stock !== null) {
console.log(`在庫: ${stock}個`); // stock=0 でも実行される
}
// 入力値の空チェック(空文字はfalsy)
const input = document.getElementById('barcode').value;
if (!input) {
console.log("バーコードを入力してください");
}
条件分岐
// if - else if - else
if (price >= 10000) {
discount = 0.1;
} else if (price >= 5000) {
discount = 0.05;
} else {
discount = 0;
}
// 三項演算子
const label = stock > 0 ? "在庫あり" : "在庫切れ";
// switch
switch (paymentMethod) {
case "cash": handleCash(); break;
case "card": handleCard(); break;
case "ic": handleIC(); break;
default: handleOther();
}
// オプショナルチェーン ?.
const city = user?.address?.city; // 途中がnull/undefinedでもエラーにならない
const len = items?.length ?? 0; // ?.と??の組み合わせ
ループ
const items = ["りんご", "バナナ", "みかん"];
// for(インデックスが必要な場合)
for (let i = 0; i < items.length; i++) {
console.log(`${i}: ${items[i]}`);
}
// for...of(配列の要素を順に取得、最も読みやすい)
for (const item of items) {
console.log(item);
}
// forEach(コールバック関数で処理)
items.forEach((item, index) => {
console.log(`${index}: ${item}`);
});
// for...in(オブジェクトのキーを列挙)
const prices = { apple: 100, banana: 200 };
for (const key in prices) {
console.log(`${key}: ${prices[key]}`);
}
// while
let count = 0;
while (count < 5) {
console.log(count);
count++;
}
// break / continue
for (const item of items) {
if (item === "バナナ") continue; // スキップして次へ
if (item === "みかん") break; // ループを終了
}
関数
// 関数宣言(巻き上げ: 宣言前に呼び出し可能)
function calcTax(price, rate = 0.1) {
return Math.floor(price * rate);
}
// アロー関数(1行なら return 省略可)
const calcTax = (price, rate = 0.1) => Math.floor(price * rate);
// 複数行のアロー関数
const calcTotal = (items) => {
const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
const tax = Math.floor(subtotal * 0.1);
return { subtotal, tax, total: subtotal + tax };
};
// 関数式(変数に関数を代入、巻き上げされない)
const calcDiscount = function(price, rate) {
return Math.floor(price * rate);
};
// 即時実行関数 (IIFE)
(() => {
const secret = "外からアクセスできない";
})();
関数スコープ
// var は「関数スコープ」(関数の中でだけ閉じる)
function example() {
if (true) {
var x = 10; // if の外でもアクセスできてしまう
}
console.log(x); // 10 ← ブロックの外に漏れる!
}
// let / const は「ブロックスコープ」({} の中で閉じる)
function example2() {
if (true) {
let y = 10; // if の中だけ有効
const z = 20; // if の中だけ有効
}
// console.log(y); // ReferenceError(アクセス不可)
}
// ← だから var は使わず、const / let を使う
コールバック関数
// コールバック = 関数を「引数として」別の関数に渡すパターン
// 基本形: 処理完了後に呼び出す関数を渡す
function processOrder(order, callback) {
const total = order.price * order.qty;
callback(total); // 処理後にコールバックを実行
}
processOrder({ price: 500, qty: 3 }, (total) => {
console.log(`合計: ${total}円`); // "合計: 1500円"
});
// よく使われるコールバックの例
// 1. イベントリスナー(第2引数がコールバック)
btn.addEventListener('click', () => { /* この関数がコールバック */ });
// 2. forEach(引数の関数がコールバック)
items.forEach((item) => { /* この関数がコールバック */ });
// 3. setTimeout(第1引数がコールバック)
setTimeout(() => {
console.log("3秒後に実行");
}, 3000);
テンプレートリテラル
const name = "商品A";
const price = 1500;
// 変数埋め込み
const msg = `${name}の価格は${price}円です`;
// 式の埋め込み
const result = `税込: ¥${Math.floor(price * 1.1).toLocaleString()}`;
// 複数行HTML生成
const html = `
<div class="item">
<span class="name">${name}</span>
<span class="price">¥${price.toLocaleString()}</span>
</div>
`;
分割代入・スプレッド構文
// ── オブジェクトの分割代入 ──
const product = { name: "コーヒー", price: 350, stock: 20 };
const { name, price } = product;
// リネーム付き(変数名の衝突を避ける)
const { name: productName, price: unitPrice } = product;
// デフォルト値
const { discount = 0 } = product; // なければ0
// ネストした分割代入
const order = { customer: { name: "田中" } };
const { customer: { name: customerName } } = order;
// ── 配列の分割代入 ──
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3,4,5]
// 要素のスキップ(不要な位置はカンマだけ書く)
const [, , third] = ["a", "b", "c"]; // third="c"
// デフォルト値
const [x = 0, y = 0] = [42]; // x=42, y=0
// 関数の戻り値を分割代入(よく使うパターン)
function parsePrice(input) {
return [parseInt(input), input.includes("税込")];
}
const [amount, taxIncluded] = parsePrice("350円(税込)");
// 値のスワップ
let a = 1, b = 2;
[a, b] = [b, a]; // a=2, b=1
// ── 分割代入の実用例 ──
// 関数パラメータで直接分割代入
function printReceipt({ name, price, qty = 1 }) {
console.log(`${name} x${qty} = ¥${price * qty}`);
}
printReceipt({ name: "コーヒー", price: 350 });
// APIレスポンスから必要な部分だけ取得
const { data: { items, total }, status } = await fetch(url).then(r => r.json());
// ── スプレッド構文(配列) ──
const newItems = [...items, "新商品"]; // 末尾に追加
const prepend = ["先頭商品", ...items]; // 先頭に追加
const copy = [...items]; // 浅いコピー(元を変えずに操作したい時)
const concat = [...arr1, ...arr2]; // 結合
// 特定要素を除外して新しい配列を作る(イミュータブル削除)
const removed = items.filter((_, i) => i !== 2); // index2を除外
// 特定要素を置換して新しい配列を作る(イミュータブル更新)
const replaced = items.map((item, i) =>
i === 2 ? { ...item, price: 400 } : item
);
// ── スプレッド構文(オブジェクト) ──
const updated = { ...product, price: 400 }; // 一部上書き
const merged = { ...defaults, ...userConfig }; // マージ(後の値が優先)
// ── レスト構文(...を左辺で使う = 残りを集める) ──
const { id, ...others } = { id: 1, name: "A", price: 100 };
// id=1, others={ name:"A", price:100 }(特定キーを除外したい時に便利)
// 関数の可変長引数
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
sum(1, 2, 3); // 6
// 関数の引数に展開
const nums = [3, 1, 4, 1, 5];
Math.max(...nums); // 5
三項演算子 vs 論理演算 使い分け
// 2つの値から選ぶ → 三項演算子
const label = isActive ? "有効" : "無効";
// デフォルト値の設定 → ?? (null/undefined) か || (全falsy)
const qty = inputValue ?? 1;
// 条件付きで実行 → &&
isLoggedIn && showDashboard();
// 条件付きでプロパティを含める
const payload = {
name,
price,
...(discount > 0 && { discount }), // discountが0より大きい場合のみ含める
};
4. 文字列・数値操作
文字列メソッド
const str = " Hello World ";
// 検索
str.includes("World") // true 含まれるか
str.startsWith(" Hello") // true 先頭一致
str.endsWith(" ") // true 末尾一致
str.indexOf("World") // 8 位置(見つからない場合-1)
// 変換
str.trim() // "Hello World" 前後の空白除去
str.toUpperCase() // " HELLO WORLD "
str.toLowerCase() // " hello world "
// 切り出し
str.slice(2, 7) // "Hello" 開始位置〜終了位置(終了は含まない)
str.slice(-7) // "orld " 後ろから
// 置換
str.replace("World", "JS") // " Hello JS " 最初の1つ
str.replaceAll(" ", "") // "HelloWorld" 全て置換
// 分割・結合
"a,b,c".split(",") // ["a", "b", "c"]
["a", "b", "c"].join("-") // "a-b-c"
// パディング(桁揃え)
"5".padStart(3, "0") // "005" レシート番号等に便利
"Hi".padEnd(10, ".") // "Hi........"
// 繰り返し
"-".repeat(30) // "------------------------------"
数値・Math
// 丸め
Math.floor(3.7) // 3 切り捨て(税計算でよく使う)
Math.ceil(3.2) // 4 切り上げ
Math.round(3.5) // 4 四捨五入
Math.trunc(3.9) // 3 小数部除去
// 最大・最小・絶対値
Math.max(1, 5, 3) // 5
Math.min(1, 5, 3) // 1
Math.abs(-42) // 42
// ランダム
Math.random() // 0以上1未満のランダムな小数
Math.floor(Math.random() * 100) // 0〜99のランダムな整数
// 表示用フォーマット
(1500).toLocaleString() // "1,500" 3桁区切り
(0.1).toFixed(2) // "0.10" 小数点以下2桁
(1500).toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' })
// "¥1,500" 通貨フォーマット
日付(Date)
// 現在日時の取得
const now = new Date();
now.getFullYear() // 2026
now.getMonth() // 0-11(0=1月 ← 注意)
now.getDate() // 1-31(日)
now.getDay() // 0-6(0=日曜)
now.getHours() // 0-23
now.getMinutes() // 0-59
// 日付の生成
new Date('2026-04-10') // ISO形式
new Date(2026, 3, 10) // 年, 月(0始まり), 日
// 表示用フォーマット
now.toLocaleDateString('ja-JP')
// "2026/4/10"
now.toLocaleString('ja-JP')
// "2026/4/10 14:30:00"
now.toLocaleDateString('ja-JP', {
year: 'numeric', month: '2-digit', day: '2-digit',
weekday: 'short'
})
// "2026/04/10(金)"
// ISO文字列(APIに送る場合)
now.toISOString() // "2026-04-10T05:30:00.000Z"
// タイムスタンプ(ミリ秒)
Date.now() // 現在のタイムスタンプ
now.getTime() // Dateオブジェクトからタイムスタンプ
// 日付計算(ミリ秒単位で加減算)
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
// setDateを使った加算
const nextWeek = new Date(now);
nextWeek.setDate(nextWeek.getDate() + 7);
正規表現
// 基本パターン
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
emailRegex.test("user@example.com") // true
// 電話番号チェック
const phoneRegex = /^0\d{1,4}-?\d{1,4}-?\d{4}$/;
phoneRegex.test("090-1234-5678") // true
// 半角数字のみ
/^\d+$/.test("12345") // true
// matchで取り出し
const str = "注文番号: ORD-12345";
const match = str.match(/ORD-(\d+)/);
match[1] // "12345"
// replaceで変換
"2026-04-10".replace(/(\d{4})-(\d{2})-(\d{2})/, "$1年$2月$3日")
// "2026年04月10日"
文字列の検索で条件分岐
// 商品名に特定の文字列が含まれるか判定
const productName = "有機野菜サラダ";
if (productName.includes("有機")) {
console.log("オーガニック商品ラベルを表示");
}
// 検索キーワードで商品をフィルタリング
const keyword = searchInput.value;
const filtered = products.filter(p =>
p.name.includes(keyword)
);
// 大文字・小文字を区別しない検索
const query = "coffee";
const found = products.filter(p =>
p.name.toLowerCase().includes(query.toLowerCase())
);
// バーコードの先頭で分類判定
const barcode = "4901234567890";
if (barcode.startsWith("49")) {
console.log("日本の商品(GS1 Japan)");
}
// 複数キーワードのいずれかを含むか
const note = "冷蔵・要冷蔵";
const coldKeywords = ["冷蔵", "冷凍", "チルド"];
if (coldKeywords.some(kw => note.includes(kw))) {
console.log("要冷蔵商品");
}
6. 配列・オブジェクト
配列メソッド
const products = [
{ name: "コーヒー", price: 350, category: "drink" },
{ name: "サンド", price: 500, category: "food" },
{ name: "紅茶", price: 300, category: "drink" },
{ name: "ケーキ", price: 450, category: "food" },
];
// map: 変換して新しい配列を返す
const names = products.map(p => p.name);
// ["コーヒー", "サンド", "紅茶", "ケーキ"]
// filter: 条件に合う要素だけ抽出
const drinks = products.filter(p => p.category === "drink");
// find: 条件に合う最初の1つを返す(見つからなければundefined)
const coffee = products.find(p => p.name === "コーヒー");
// findIndex: 条件に合う最初のインデックス(見つからなければ-1)
const idx = products.findIndex(p => p.name === "紅茶"); // 2
// reduce: 集約(合計計算など)
const total = products.reduce((sum, p) => sum + p.price, 0); // 1600
// sort: 並べ替え(※元の配列を変更する → スプレッドでコピー推奨)
const sorted = [...products].sort((a, b) => a.price - b.price); // 安い順
const desc = [...products].sort((a, b) => b.price - a.price); // 高い順
// some: 1つでも条件を満たすか
products.some(p => p.price >= 500); // true
// every: 全て条件を満たすか
products.every(p => p.price < 1000); // true
// includes: 含まれるか(プリミティブ配列向け)
["cash", "card"].includes("cash"); // true
// indexOf: 要素のインデックスを返す(見つからなければ-1)
["cash", "card", "ic"].indexOf("card"); // 1
["cash", "card", "ic"].indexOf("qr"); // -1
// join: 配列を文字列に結合
["コーヒー", "サンド", "紅茶"].join("、"); // "コーヒー、サンド、紅茶"
["2024", "01", "15"].join("-"); // "2024-01-15"
配列の追加・削除・変換
const arr = [1, 2, 3];
// 追加・削除(元の配列を変更する)
arr.push(4); // 末尾に追加 → [1,2,3,4]
arr.pop(); // 末尾を削除 → [1,2,3] ※削除した値を返す
arr.unshift(0); // 先頭に追加 → [0,1,2,3]
arr.shift(); // 先頭を削除 → [1,2,3] ※削除した値を返す
// ── splice: 指定位置で削除/挿入(元の配列を変更する=破壊的) ──
// splice(開始index, 削除する個数, ...挿入する要素)
const cart = ["コーヒー", "サンド", "紅茶", "ケーキ"];
cart.splice(1, 1);
// cart → ["コーヒー", "紅茶", "ケーキ"] ※削除された ["サンド"] を返す
cart.splice(1, 0, "ジュース");
// cart → ["コーヒー", "ジュース", "紅茶", "ケーキ"] ※削除0個=挿入のみ
cart.splice(2, 1, "抹茶", "水");
// cart → ["コーヒー", "ジュース", "抹茶", "水", "ケーキ"] ※削除+挿入=置換
cart.splice(-1, 1);
// cart → ["コーヒー", "ジュース", "抹茶", "水"] ※負のindexは末尾から
// ── slice: 一部を切り出して新しい配列を返す(非破壊) ──
// slice(開始index, 終了index) ※終了indexの要素は含まない
const items = ["A", "B", "C", "D", "E"];
items.slice(1, 3); // ["B", "C"] index1〜2
items.slice(2); // ["C", "D", "E"] index2から最後まで
items.slice(-2); // ["D", "E"] 末尾から2つ
items.slice(); // ["A","B","C","D","E"] 全体のコピー
// POS実用例: ページネーション
const page = 2, perPage = 10;
const pageItems = allItems.slice((page - 1) * perPage, page * perPage);
// ── splice vs slice 早見表 ──
// splice → 元の配列を変更する(破壊的)、削除した要素を返す
// slice → 元の配列を変更しない(非破壊)、切り出した新配列を返す
// ── 元の配列を変更しない操作(イミュータブル) ──
const newArr = [...arr, 4]; // 末尾に追加
const without = arr.filter((_, i) => i !== 1); // index1を除外
const sliced = arr.slice(0, 2); // index0〜1を抽出
// flat: ネスト配列を平坦化
[[1,2], [3,4]].flat() // [1,2,3,4]
// flatMap: mapしてからflatする
orders.flatMap(o => o.items) // 全注文の商品を1つの配列に
// 重複排除
const unique = [...new Set([1,2,2,3])]; // [1,2,3]
メソッドチェーン
// filter → map → sort を連結
const result = products
.filter(p => p.category === "drink") // 飲料だけ
.map(p => ({ ...p, taxIncluded: Math.floor(p.price * 1.1) })) // 税込価格追加
.sort((a, b) => a.price - b.price); // 安い順
// reduce でグループ化
const grouped = products.reduce((acc, p) => {
(acc[p.category] ??= []).push(p);
return acc;
}, {});
// { drink: [...], food: [...] }
オブジェクト操作
const product = { name: "コーヒー", price: 350, stock: 20 };
// キー・値・エントリの取得
Object.keys(product); // ["name", "price", "stock"]
Object.values(product); // ["コーヒー", 350, 20]
Object.entries(product); // [["name","コーヒー"], ["price",350], ...]
// エントリからオブジェクトに変換
Object.fromEntries([
["name", "コーヒー"],
["price", 350]
]); // { name: "コーヒー", price: 350 }
// プロパティの存在チェック
"name" in product; // true
// 動的キー
const key = "price";
product[key]; // 350
// 動的キーでオブジェクト生成
const field = "category";
const obj = { [field]: "drink" }; // { category: "drink" }
JSON操作
// オブジェクト → JSON文字列
const json = JSON.stringify(product);
// '{"name":"コーヒー","price":350,"stock":20}'
// 整形出力(デバッグ用)
console.log(JSON.stringify(product, null, 2));
// JSON文字列 → オブジェクト
const obj = JSON.parse(json);
// ディープコピー(ネストしたオブジェクトのコピー)
const copy = JSON.parse(JSON.stringify(original));
// ※Date, function, undefinedは失われる。より正確にはstructuredClone()を使う:
const copy = structuredClone(original);
Map / Set
// ── Map: キーが何でも使えるハッシュマップ ──
const cart = new Map();
cart.set("product_101", { name: "コーヒー", qty: 2 });
cart.get("product_101"); // { name: "コーヒー", qty: 2 }
cart.has("product_101"); // true
cart.delete("product_101");
cart.size; // 要素数
for (const [key, value] of cart) {
console.log(key, value);
}
// ── Set: 重複のないコレクション ──
const categories = new Set(["food", "drink", "food"]);
// Set(2) { "food", "drink" }
categories.add("snack");
categories.has("food"); // true
categories.delete("food");
[...categories]; // 配列に変換
10. クラス構文とOOP 中級
Class基本(constructor / メソッド / getter・setter)
class CartItem {
constructor(name, price, quantity = 1) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
// メソッド
subtotal() {
return this.price * this.quantity;
}
// getter — プロパティのようにアクセスできる
get taxIncluded() {
return Math.floor(this.subtotal() * 1.1);
}
// setter — 代入で呼ばれる
set qty(value) {
if (value < 1) throw new Error('数量は1以上');
this.quantity = value;
}
}
const item = new CartItem('コーヒー', 350, 2);
console.log(item.subtotal()); // 700
console.log(item.taxIncluded); // 770
item.qty = 3; // setter経由
継承(extends / super)
class DiscountedItem extends CartItem {
constructor(name, price, quantity, discountRate) {
super(name, price, quantity); // 親のconstructorを呼ぶ
this.discountRate = discountRate;
}
// メソッドのオーバーライド
subtotal() {
const base = super.subtotal(); // 親のメソッドを呼ぶ
return Math.floor(base * (1 - this.discountRate));
}
}
const sale = new DiscountedItem('弁当', 500, 1, 0.2);
console.log(sale.subtotal()); // 400(20%引き)
console.log(sale instanceof CartItem); // true
プライベートフィールド(#)
class PaymentProcessor {
// # で始まるフィールドはクラス外からアクセス不可
#apiKey;
#endpoint;
constructor(apiKey, endpoint) {
this.#apiKey = apiKey;
this.#endpoint = endpoint;
}
async charge(amount) {
const res = await fetch(this.#endpoint, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.#apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount }),
});
return res.json();
}
}
const payment = new PaymentProcessor('sk_xxx', '/api/pay');
// payment.#apiKey → SyntaxError(外部からアクセス不可)
静的メソッド(static)
class CartItem {
constructor(name, price, quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
// インスタンスを作らずに呼べるファクトリメソッド
static fromBarcode(barcode, catalog) {
const product = catalog.find(p => p.barcode === barcode);
if (!product) throw new Error(`商品が見つかりません: ${barcode}`);
return new CartItem(product.name, product.price, 1);
}
// 複数アイテムの合計を計算するユーティリティ
static totalOf(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
}
// 使い方
const item = CartItem.fromBarcode('4901234567890', catalog);
const total = CartItem.totalOf(cartItems);