最近 Reddit でJavaScriptのスレッドを見つけました。「これ、意外とみんな使ってないけど便利だな」とか「昔はわざわざライブラリを入れてたのに、今はこんなに楽なんだ!」と共感するポイントがたくさんあったので、共有させていただきたいです。
1. Set:高速検索できる、o(1) の計算量でfilterより早い
即時重複排除
配列から重複を取り除いたり、特定のアイテムが存在するかを高速にチェックしたりする際に最適です。
const idPool = [101, 102, 102, 103, 103, 103];
const dedupedIds = [...new Set(idPool)];
console.log(dedupedIds); // [101, 102, 103]
イベントの二重登録を防ぐ
const wiredEvents = new Set();
function bindOnce(evtName, handler) {
if (wiredEvents.has(evtName)) return;
window.addEventListener(evtName, handler);
wiredEvents.add(evtName);
}
bindOnce('scroll', () => console.log('scrolling'));
bindOnce('scroll', () => console.log('scrolling')); // 2回目は登録されない
2. Object.entries() + Object.fromEntries():オブジェクト変換
オブジェクトを一度配列に変換して加工し、再びオブジェクトに戻す処理が驚くほど簡単になります。
null や空文字のプロパティを除外する
const rawProfile = {
fullName: 'Taro Tanaka',
age: 28,
avatar: '',
phone: '09012345678',
};
// 値が空文字でないものだけを抽出して新しいオブジェクトを作成
const compactProfile = Object.fromEntries(
Object.entries(rawProfile).filter(([k, v]) => v !== '')
);
console.log(compactProfile);
// { fullName: 'Taro Tanaka', age: 28, phone: '09012345678' }
URL クエリ文字列をオブジェクトに変換
const queryString = window.location.search; // 例: "?id=123&category=js"
const queryMap = Object.fromEntries(new URLSearchParams(queryString));
console.log(queryMap); // { id: "123", category: "js" }
3. ??(Null 合体演算子)と ??= — || より安全なデフォルト値
|| は 0 や空文字 '' もfalseと判定してしまいますが、?? は null または undefined のときだけfalseを判定します。
const qtyInput = 0;
const wrongQty = qtyInput || 10; // 0が無視されて10になる
const safeQty = qtyInput ?? 10; // 0が有効な値として保持される
console.log(wrongQty, safeQty); // 10, 0
既存プロパティを上書きせずにデフォルト値を設定
const reqOpts = { timeout: 5000 };
reqOpts.retries ??= 3; // retries が未定義の場合のみ代入
console.log(reqOpts); // { timeout: 5000, retries: 3 }
4. Intl:通貨・数値・日付の国際化 API
外部ライブラリ(非推奨となったMoment.js など)を使わずに、ブラウザ標準機能でローカライズが可能です。
価格のフォーマット
const priceValue = 1234.56;
const priceJPY = new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(priceValue);
const priceUSD = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(priceValue);
console.log(priceJPY); // "¥1,235" (円の場合はデフォルトで整数に)
console.log(priceUSD); // "$1,234.56"
日付のローカライズ
//
const now = new Date();
const jaDateNow = new Intl.DateTimeFormat('ja-JP', { dateStyle: 'full' }).format(now);
console.log(jaDateNow); // "2025年X月X日曜日"
//
const date = new Date('2025-12-17T00:00:00');
const jaDate = new Intl.DateTimeFormat('ja-JP', { year: 'numeric', month: 'long', day: 'numeric' }
).format(date);
console.log(jaDate); // 2025年12月17日
5. IntersectionObserver:画像の遅延読み込みと無限スクロール
要素が画面内に入ったかどうかを効率的に検知できます。scroll イベントを監視するよりパフォーマンスが大幅に向上します。
画像の遅延読み込み
const imageWatcher = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const img = entry.target;
img.src = img.dataset.src;
imageWatcher.unobserve(img); // 一度読み込んだら監視解除
}
});
document.querySelectorAll('.js-lazy').forEach((img) => imageWatcher.observe(img));
6. Promise.allSettled():全ての完了を待つバッチ処理
Promise.all() は一つでも失敗すると全体が reject されますが、allSettled() は成否に関わらずすべての結果を返します。
const batchTasks = [
fetch('/api/user/101'),
fetch('/api/orders/101'),
fetch('/api/invalid-path'),
];
const outcomes = await Promise.allSettled(batchTasks);
// 成功したものだけを抽出してパース
const okPayloads = await Promise.all(
outcomes
.filter(r => r.status === 'fulfilled' && r.value.ok)
.map(r => r.value.json())
);
console.log('成功したデータ:', okPayloads);
7. Element.closest():スマートな DOM 取得
従来、クリックされた要素から特定の親要素を探したい場合、element.parentNode.parentNodeのように parentNode を何度も辿る必要がありました。この書き方は DOM 構造に強く依存しており、要素増減でコードが壊れてしまうという問題があります。Element.closest()を使うと、クリックされた子要素から、特定の条件に合う親要素(または自分自身)を簡単に取得できます。
<ul class="people-list">
<li class="people-item">
<button class="btn-delete">削除</button>
</li>
</ul>
document.querySelector('.btn-delete').addEventListener('click', (ev) => {
// ボタンから一番近い li 要素を見つける
const item = ev.target.closest('.people-item');
item?.remove();
});
8. URL クラス:URL の操作を確実に
文字列連結で URL を作ると / の漏れなどのバグが起きやすいですが、URL クラスを使えば安全です。
const link = new URL('https://example.com/page');
// クエリパラメータの追加・更新
link.searchParams.append('page', '3');
link.searchParams.set('sort', 'desc');
console.log(link.href); // "https://example.com/page?page=3&sort=desc"
console.log(link.hostname); // "example.com"
9. for…of:可読性が高く、中断可能なループ
forEach よりも直感的で、break や continue、return が使用可能です。
const catalog = [
{ id: 1, label: 'Phone', price: 59990 },
{ id: 2, label: 'Laptop', price: 99990 },
{ id: 3, label: 'Tablet', price: 39990 },
];
for (const item of catalog) {
if (item.price > 80000) {
console.log('高額商品を発見:', item.label);
break; // 条件を満たしたらループを終了
}
}
10. await:モジュールの非同期初期化
ES モジュール(.mjs または type: "module")内では、関数の外でも await が使えます。
// config.js
const resp = await fetch('/api/config');
export const runtimeConfig = await resp.json();
// main.js
import { runtimeConfig } from './config.js';
console.log('設定の読み込み完了:', runtimeConfig);
まとめ
昔なら外部ライブラリを頼りにしたり、今ではブラウザ機能だけできれいに書けます。JavaScript も進化していますね。日々のコーディングをちょっと楽にして、もっと「いい感じ」のコードを書きたい方の参考になれば嬉しいです!