React や Vue などのフレームワークを使わず、ブラウザ標準のJavaScriptだけで書くとき(いわゆるバニラJS)に毎回調べがちな構文をまとめました。非同期処理・配列操作・オブジェクトコピー・DOM操作・ストレージなど、実務でよく使うパターンを用途別に整理しています。Node.js でも動く構文は多いですが、主にブラウザ環境を想定しています。

ストレージ — sessionStorage / localStorage

sessionStorage.setItem('myData', '保存したい内容'); // 保存
const savedData = sessionStorage.getItem('myData'); // 読み出す
sessionStorage.removeItem('myData'); // 削除
sessionStorage.clear(); // 全て削除

sessionStorage は同一タブ内でのみ保持され、タブを閉じると破棄されます。最大容量はブラウザによりますがおおむね5MB。保存形式は文字列のみなので、オブジェクトを保存したい場合は JSON.stringify() で変換が必要です。

sessionStoragelocalStorage に書き換えるとデータを永続化できます。

オブジェクトの凍結 — Object.freeze

const Trial = {
  ok: true,
  ng: false
};

Object.freeze(Trial);   // オブジェクトを凍結
Trial.ok = false;       // 非 strict mode ではエラーにならないが値は変わらない(strict mode では TypeError)
console.log(Trial.ok);  // true

const ではプロパティの書き換えを防げません。Object.freeze() はトップレベルのプロパティ変更を防ぎますが、浅い凍結なので入れ子のオブジェクトは変更できます。JS で Enum を表現したいときに便利です。

非同期処理 — fetch / async / await

async function fetchUser(userId) {
  const response = await fetch(`/api/user/${userId}`);
  if (!response.ok) throw new Error("通信エラー");
  const data = await response.json();
  return data;
}

fetchUser(1).then(user => {
  console.log(user.name);
}).catch(err => {
  console.error(err);
});

fetch() はブラウザ組み込みのHTTP通信APIです。await で非同期処理の完了を待てるため、Promise のチェーンよりも読みやすくなります。await を使う関数は async で宣言する必要があります。

非同期処理 — Promise

function wait(ms) {
  return new Promise(resolve => {
    setTimeout(() => resolve(`${ms}ms 待ちました`), ms);
  });
}

wait(1000).then(msg => console.log(msg));

Promise は非同期処理の結果を表すオブジェクトです。resolve で成功、reject で失敗を返します。then を数珠繋ぎにすることで順次処理を書けます。

配列操作 — filter / map

const nums = [1, 2, 3, 4, 5];
const even = nums.filter(n => n % 2 === 0); // [2, 4]
const squared = nums.map(n => n * n);       // [1, 4, 9, 16, 25]

console.log(even, squared);

filtermap は配列を加工するメソッドです。for文を使わずワンライナーで処理を書けるのが魅力です。

配列の条件チェック — every / some

// every: 全ての要素が条件を満たすか?
const allPositive = [1, 2, 3].every(n => n > 0); // true

// some: 一つでも条件を満たせばOK
const hasNegative = [1, -2, 3].some(n => n < 0); // true

filter と違い、真偽値だけを返すメソッドです。バリデーション処理などに使えます。

展開 — スプレッド構文 ...

const a = [1, 2];
const b = [3, 4];
const merged = [...a, ...b]; // [1, 2, 3, 4]

... のスプレッド構文で配列やオブジェクトを展開できます。Object.assign と同様に浅いコピーを作るのにも使えます。

オブジェクト結合・クローン — Object.assign

// マージ
const a = { x: 1 }, b = { y: 2 };
Object.assign(a, b);           // a は { x:1, y:2 } に上書きされる

// デフォルト適用(後ろが優先)
const opts = Object.assign({ timeout: 5000, retry: 0 }, userOpts);

// 浅いクローン(target を {} に)
const clone = Object.assign({}, source);

Object.assign(target, ...sources) は列挙可能なプロパティを左から右へ浅くコピーし、target を破壊的に更新して返します。浅いマージ・浅いクローン用途ではスプレッド構文でも似た書き方ができます(ただし Object.assign は setter を呼ぶなど挙動が完全一致するわけではありません)。

const merged = { ...a, ...b };
const clone2 = { ...source };

浅いコピーでは、入れ子のオブジェクトは参照が共有される点に注意してください。

深いコピー — structuredClone

const src = { a: 1, d: new Date(), m: new Map([['k', 1]]) };
const dst = structuredClone(src);

ブラウザ/Node 組み込みの深いコピーAPIです。入れ子のオブジェクトまで独立したコピーを作ります。ただし関数・DOM ノード・WeakMap など clone できない値を含む場合は DataCloneError になります。

// 浅いコピー: 入れ子の参照が共有される
const src1 = { nest: { x: 1 } };
const shallow = { ...src1 };
shallow.nest.x = 9;
console.log(src1.nest.x); // 9 ← 参照が共有されるため元も変わる

// 深いコピー: 完全に独立
const src2 = { nest: { x: 1 } };
const deep = structuredClone(src2);
deep.nest.x = 7;
console.log(src2.nest.x); // 1 ← 独立しているため元は変わらない

マージや浅い複製には Object.assign / スプレッド、完全な複製には structuredClone を使い分けましょう。

引数の挙動 — 値渡しと参照のコピー

JSは常に値渡しですが、オブジェクトの場合は参照のコピーが渡されるため、関数内でプロパティを変更すると外側に反映されます。

// プロパティ変更は外側に反映される
function modifyObject(obj) {
  obj.value = 'fuga';
}
const myObject = { value: 'hoge' };
modifyObject(myObject);
console.log(myObject.value); // "fuga"

// 再代入は外側に影響しない
function reassign(obj) {
  obj = { value: 'bar' };
}
const o = { value: 'hoge' };
reassign(o);
console.log(o.value); // "hoge"

// プリミティブは常に値のコピー
function modifyPrimitive(v) { v = 10; }
let x = 5;
modifyPrimitive(x);
console.log(x); // 5

クラス — class

class User {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hello, ${this.name}`;
  }
}

const u = new User("Alice");
console.log(u.greet());

ES6 から使えるクラス構文です。constructor でインスタンス生成時の初期化を行います。

デバッグ — スタックトレース

function debugTrace() {
  console.log(new Error("trace").stack);
}
Error: trace
    at debugTrace (article_form.js:26:15)
    at initViews (article_form.js:33:3)
    at HTMLDocument.<anonymous> (article_form.js:21:3)

new Error().stack で呼び出し元をトレースできます。どこから呼ばれているか分からなくなったときに便利です。

DOM操作 — data属性へのアクセス

<div id="user" data-id="123" data-role="admin"></div>
const el = document.getElementById("user");

console.log(el.dataset.id);   // "123"
console.log(el.dataset.role); // "admin"

el.dataset.role = "editor";   // 設定
delete el.dataset.id;          // 削除

HTML側では data-xxx、JS側では camelCasedata-user-namedataset.userName)でアクセスします。値は文字列なので数値・真偽値として使う場合は変換が必要です。

JSON変換 — JSON.stringify / JSON.parse

// オブジェクト → JSON文字列
const obj = { name: "Alice", age: 25 };
const json = JSON.stringify(obj);
console.log(json); // {"name":"Alice","age":25}

// JSON文字列 → オブジェクト
const parsed = JSON.parse('{"name":"Alice","age":25}');
console.log(parsed.name); // Alice

localStorage への保存や API リクエストのボディ生成など、日常的に使う組み合わせです。

コレクション — Map / Set

Map はキーと値のペアを保持します。キーにオブジェクトも使えます。

const map = new Map();
map.set("name", "Taro");
console.log(map.get("name")); // "Taro"

const obj = { id: 1 };
map.set(obj, "Hanako");
console.log(map.get(obj)); // "Hanako"

Set は重複しない値のコレクションです。配列の重複除去に便利です。

const set = new Set([1, 2, 2, 3]);
console.log(set); // Set(3) {1, 2, 3}

set.add(4);
console.log(set.has(2)); // true

メモリ管理 — WeakMap / WeakSet

WeakMap はオブジェクトをキーにしたマップで、キーとなるオブジェクトが他で参照されなくなると自動削除(GC対象)されます。キャッシュや一時データに便利ですが列挙はできません。

let obj = { name: "Taro" };
const weakMap = new WeakMap();
weakMap.set(obj, "data");
console.log(weakMap.get(obj)); // "data"
obj = null; // 参照がなくなると weakMap 内からも消える

WeakSet はオブジェクトだけを格納する Set です。要素が他で参照されなくなれば自動削除されます。

let obj = { id: 1 };
const weakSet = new WeakSet();
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
obj = null;

GC のタイミングは保証されません。

DOM監視 — MutationObserver

MutationObserver は DOM の変化を監視するブラウザAPIです。子要素の追加・削除、属性・テキストの変更などを検知してコールバックを実行します。

const callback = (mutationsList) => {
  mutationsList.forEach(mutation => {
    console.log(mutation.type);
  });
};

const observer = new MutationObserver(callback);
const targetNode = document.getElementById("nuContents");

observer.observe(targetNode, {
  childList: true,      // 子要素の追加・削除
  attributes: true,     // 属性の変更
  characterData: true,  // テキスト内容の変更
  subtree: true         // 子孫要素も監視
});

jQuery拡張としての実装例

この仕組みを応用して、hidden input の値変化を監視する jQuery プラグインです。

注意: MutationObserver が検知するのは setAttribute("value", ...) のような DOM属性の変更 のみです。element.value = ... や jQuery の .val(...) はプロパティ変更なので発火しません。このプラグインは setAttribute で値を書き込むコードと組み合わせて使う前提です。

(function($) {
  $.fn.observeValue = function(callback) {
    return this.each(function() {
      if (this.tagName.toLowerCase() !== 'input' || this.type !== 'hidden') {
        throw new Error('observeValue(): 対象は input[type="hidden"] のみです');
      }
      var observer = new MutationObserver(function(mutations, obs) {
        callback.call(this, mutations, obs);
      }.bind(this));
      observer.observe(this, { attributes: true, attributeFilter: ["value"] });
    });
  };
})(jQuery);
// 先に observer を登録してから setAttribute で値をセットすると発火する
$("#myHidden").observeValue(function() {
  console.log("hiddenの値が変更されました:", this.getAttribute("value"));
});

document.getElementById("myHidden").setAttribute("value", "new-value");

関連記事

関連アイテム