Lodash と標準 JavaScript の使い分けガイド

JavaScript の開発において、Lodash と標準 JavaScript のどちらを選ぶべきかは、多くの開発者が直面する重要な判断です。この記事では、両者の特徴を詳しく比較し、実際のプロジェクトでどのように使い分けるべきかを解説します。
配列操作の比較
map, filter, reduce の実装比較
配列操作は JavaScript 開発の基本であり、Lodash と標準 JavaScript で大きな違いがあります。
標準 JavaScript での配列操作
javascript// 標準 JavaScript での map 操作
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 標準 JavaScript での filter 操作
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
// 標準 JavaScript での reduce 操作
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15
Lodash での配列操作
javascript// Lodash での map 操作
const _ = require('lodash');
const doubledLodash = _.map(numbers, (num) => num * 2);
console.log(doubledLodash); // [2, 4, 6, 8, 10]
// Lodash での filter 操作
const evenNumbersLodash = _.filter(
numbers,
(num) => num % 2 === 0
);
console.log(evenNumbersLodash); // [2, 4]
// Lodash での reduce 操作
const sumLodash = _.reduce(
numbers,
(acc, num) => acc + num,
0
);
console.log(sumLodash); // 15
パフォーマンスの違い
実際のパフォーマンステストを行ってみましょう。
javascript// パフォーマンステスト用の大きな配列
const largeArray = Array.from(
{ length: 1000000 },
(_, i) => i
);
// 標準 JavaScript のパフォーマンステスト
console.time('Standard JS Map');
const standardResult = largeArray.map((num) => num * 2);
console.timeEnd('Standard JS Map');
// Lodash のパフォーマンステスト
console.time('Lodash Map');
const lodashResult = _.map(largeArray, (num) => num * 2);
console.timeEnd('Lodash Map');
パフォーマンス結果の例
- 標準 JavaScript: ~15ms
- Lodash: ~25ms
標準 JavaScript の方が高速ですが、Lodash はより堅牢なエラーハンドリングを提供します。
可読性の観点
複雑な配列操作での比較
javascript// 標準 JavaScript での複雑な操作
const users = [
{ id: 1, name: 'Alice', age: 25, active: true },
{ id: 2, name: 'Bob', age: 30, active: false },
{ id: 3, name: 'Charlie', age: 35, active: true },
];
// 標準 JavaScript での複数条件での絞り込み
const activeUsersOver25 = users
.filter((user) => user.active && user.age > 25)
.map((user) => ({ name: user.name, age: user.age }))
.sort((a, b) => a.age - b.age);
Lodash での複雑な操作
javascript// Lodash での複数条件での絞り込み
const activeUsersOver25Lodash = _.chain(users)
.filter((user) => user.active && user.age > 25)
.map((user) => ({ name: user.name, age: user.age }))
.sortBy('age')
.value();
Lodash のチェーン構文は、複雑な操作をより読みやすく表現できます。
オブジェクト操作の比較
オブジェクトのマージ・クローン
オブジェクトの操作では、Lodash の優位性が明確に現れます。
標準 JavaScript でのオブジェクトマージ
javascript// 標準 JavaScript での浅いマージ
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
// スプレッド演算子を使用
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 3, c: 4 }
// Object.assign を使用
const mergedAssign = Object.assign({}, obj1, obj2);
console.log(mergedAssign); // { a: 1, b: 3, c: 4 }
Lodash でのオブジェクトマージ
javascript// Lodash での浅いマージ
const mergedLodash = _.merge({}, obj1, obj2);
console.log(mergedLodash); // { a: 1, b: 3, c: 4 }
// Lodash での深いマージ
const deepObj1 = { a: { b: 1, c: 2 } };
const deepObj2 = { a: { c: 3, d: 4 } };
const deepMerged = _.merge({}, deepObj1, deepObj2);
console.log(deepMerged); // { a: { b: 1, c: 3, d: 4 } }
プロパティの取得・設定
標準 JavaScript でのプロパティ操作
javascript// 標準 JavaScript でのプロパティ取得
const user = { name: 'Alice', age: 25 };
const name = user.name;
const age = user.age;
// 安全なプロパティ取得(オプショナルチェーン)
const address = user?.address?.street;
console.log(address); // undefined
// プロパティの設定
user.email = 'alice@example.com';
Lodash でのプロパティ操作
javascript// Lodash での安全なプロパティ取得
const nameLodash = _.get(user, 'name');
const addressLodash = _.get(
user,
'address.street',
'Default Street'
);
// Lodash でのプロパティ設定
_.set(user, 'profile.avatar', 'avatar.jpg');
console.log(user.profile.avatar); // 'avatar.jpg'
深いネストの処理
標準 JavaScript での深いネスト処理
javascript// 標準 JavaScript での深いネスト処理
const nestedData = {
users: [
{ profile: { settings: { theme: 'dark' } } },
{ profile: { settings: { theme: 'light' } } },
],
};
// 安全でない処理(エラーが発生する可能性)
try {
const themes = nestedData.users.map(
(user) => user.profile.settings.theme
);
console.log(themes);
} catch (error) {
console.error('Error:', error.message);
}
Lodash での深いネスト処理
javascript// Lodash での安全な深いネスト処理
const themesLodash = _.map(nestedData.users, (user) =>
_.get(user, 'profile.settings.theme', 'default')
);
console.log(themesLodash); // ['dark', 'light']
// より安全な処理
const safeThemes = _.map(nestedData.users, (user) =>
_.get(user, 'profile.settings.theme', 'default')
);
文字列操作の比較
文字列の変換・整形
標準 JavaScript での文字列操作
javascript// 標準 JavaScript での文字列変換
const text = 'hello world';
// 大文字変換
const upperCase = text.toUpperCase();
console.log(upperCase); // 'HELLO WORLD'
// キャメルケース変換
const camelCase = text.replace(
/\s+(\w)/g,
(match, letter) => letter.toUpperCase()
);
console.log(camelCase); // 'helloWorld'
// 文字列の分割と結合
const words = text.split(' ');
const reversed = words.reverse().join(' ');
console.log(reversed); // 'world hello'
Lodash での文字列操作
javascript// Lodash での文字列変換
const upperCaseLodash = _.toUpper(text);
console.log(upperCaseLodash); // 'HELLO WORLD'
// Lodash でのキャメルケース変換
const camelCaseLodash = _.camelCase(text);
console.log(camelCaseLodash); // 'helloWorld'
// Lodash での文字列分割
const wordsLodash = _.words(text);
console.log(wordsLodash); // ['hello', 'world']
検索・置換処理
標準 JavaScript での検索・置換
javascript// 標準 JavaScript での検索
const searchText =
'JavaScript is awesome, JavaScript is powerful';
const searchTerm = 'JavaScript';
// 検索
const includes = searchText.includes(searchTerm);
const index = searchText.indexOf(searchTerm);
const lastIndex = searchText.lastIndexOf(searchTerm);
console.log(includes); // true
console.log(index); // 0
console.log(lastIndex); // 25
// 置換
const replaced = searchText.replace(
/JavaScript/g,
'TypeScript'
);
console.log(replaced); // 'TypeScript is awesome, TypeScript is powerful'
Lodash での検索・置換
javascript// Lodash での検索
const includesLodash = _.includes(searchText, searchTerm);
console.log(includesLodash); // true
// Lodash での置換
const replacedLodash = _.replace(
searchText,
/JavaScript/g,
'TypeScript'
);
console.log(replacedLodash); // 'TypeScript is awesome, TypeScript is powerful'
正規表現との組み合わせ
標準 JavaScript での正規表現処理
javascript// 標準 JavaScript での正規表現処理
const email = 'user@example.com';
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValidEmail = emailRegex.test(email);
console.log(isValidEmail); // true
// 複数の正規表現パターン
const patterns = [
/^[^\s@]+@[^\s@]+\.[^\s@]+$/, // メールアドレス
/^\d{3}-\d{4}-\d{4}$/, // 電話番号
/^[A-Z]{2}\d{2}[A-Z0-9]{10}$/, // IBAN
];
const testString = 'user@example.com';
const matches = patterns.some((pattern) =>
pattern.test(testString)
);
console.log(matches); // true
Lodash での正規表現処理
javascript// Lodash での正規表現処理
const isValidEmailLodash = _.test(email, emailRegex);
console.log(isValidEmailLodash); // true
// Lodash での複数パターンマッチング
const matchesLodash = _.some(patterns, (pattern) =>
_.test(testString, pattern)
);
console.log(matchesLodash); // true
ユーティリティ関数の比較
型チェック関数
標準 JavaScript での型チェック
javascript// 標準 JavaScript での型チェック
const values = [
42,
'hello',
true,
null,
undefined,
[],
{},
() => {},
new Date(),
];
// 型チェック関数
const typeChecks = values.map((value) => ({
value,
isNumber: typeof value === 'number',
isString: typeof value === 'string',
isBoolean: typeof value === 'boolean',
isNull: value === null,
isUndefined: value === undefined,
isArray: Array.isArray(value),
isObject: typeof value === 'object' && value !== null,
isFunction: typeof value === 'function',
}));
console.log(typeChecks);
Lodash での型チェック
javascript// Lodash での型チェック
const lodashTypeChecks = values.map((value) => ({
value,
isNumber: _.isNumber(value),
isString: _.isString(value),
isBoolean: _.isBoolean(value),
isNull: _.isNull(value),
isUndefined: _.isUndefined(value),
isArray: _.isArray(value),
isObject: _.isObject(value),
isFunction: _.isFunction(value),
isDate: _.isDate(value),
}));
console.log(lodashTypeChecks);
数値・日付の処理
標準 JavaScript での数値・日付処理
javascript// 標準 JavaScript での数値処理
const numbers = [1.23456, 2.34567, 3.45678];
// 四捨五入
const rounded = numbers.map((num) => Math.round(num));
console.log(rounded); // [1, 2, 3]
// 小数点以下2桁
const fixed = numbers.map((num) => Number(num.toFixed(2)));
console.log(fixed); // [1.23, 2.35, 3.46]
// 日付処理
const now = new Date();
const tomorrow = new Date(
now.getTime() + 24 * 60 * 60 * 1000
);
const yesterday = new Date(
now.getTime() - 24 * 60 * 60 * 1000
);
console.log(tomorrow);
console.log(yesterday);
Lodash での数値・日付処理
javascript// Lodash での数値処理
const roundedLodash = _.map(numbers, (num) => _.round(num));
console.log(roundedLodash); // [1, 2, 3]
// Lodash での範囲生成
const range = _.range(1, 11);
console.log(range); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Lodash でのランダム数値
const random = _.random(1, 100);
console.log(random); // 1-100のランダムな数値
配列・オブジェクトの判定
標準 JavaScript での判定
javascript// 標準 JavaScript での空判定
const emptyArray = [];
const emptyObject = {};
const nullValue = null;
const undefinedValue = undefined;
console.log(emptyArray.length === 0); // true
console.log(Object.keys(emptyObject).length === 0); // true
console.log(nullValue === null); // true
console.log(undefinedValue === undefined); // true
Lodash での判定
javascript// Lodash での空判定
console.log(_.isEmpty(emptyArray)); // true
console.log(_.isEmpty(emptyObject)); // true
console.log(_.isNull(nullValue)); // true
console.log(_.isUndefined(undefinedValue)); // true
// Lodash での詳細な判定
console.log(_.isArrayLike(emptyArray)); // true
console.log(_.isPlainObject(emptyObject)); // true
パフォーマンスとバンドルサイズ
実行速度の比較
実際のパフォーマンステストを行ってみましょう。
javascript// パフォーマンステスト用の関数
function performanceTest() {
const testArray = Array.from(
{ length: 100000 },
(_, i) => i
);
const testObject = {};
for (let i = 0; i < 10000; i++) {
testObject[`key${i}`] = `value${i}`;
}
// 配列操作のパフォーマンステスト
console.time('Standard JS Array Operations');
const standardResult = testArray
.filter((num) => num % 2 === 0)
.map((num) => num * 2)
.reduce((acc, num) => acc + num, 0);
console.timeEnd('Standard JS Array Operations');
console.time('Lodash Array Operations');
const lodashResult = _.chain(testArray)
.filter((num) => num % 2 === 0)
.map((num) => num * 2)
.reduce((acc, num) => acc + num, 0)
.value();
console.timeEnd('Lodash Array Operations');
// オブジェクト操作のパフォーマンステスト
console.time('Standard JS Object Operations');
const standardObj = { ...testObject };
console.timeEnd('Standard JS Object Operations');
console.time('Lodash Object Operations');
const lodashObj = _.cloneDeep(testObject);
console.timeEnd('Lodash Object Operations');
}
performanceTest();
ファイルサイズへの影響
バンドルサイズの比較
javascript// package.json での依存関係
// 標準 JavaScript のみ
{
"dependencies": {
// 追加の依存関係なし
}
}
// Lodash を使用
{
"dependencies": {
"lodash": "^4.17.21"
}
}
バンドルサイズの測定
javascript// webpack-bundle-analyzer での測定例
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [new BundleAnalyzerPlugin()],
};
最適化のポイント
Lodash の部分インポート
javascript// 全体的なインポート(非推奨)
const _ = require('lodash');
// 部分的なインポート(推奨)
const map = require('lodash/map');
const filter = require('lodash/filter');
const reduce = require('lodash/reduce');
// ES6 モジュールでの部分インポート
import { map, filter, reduce } from 'lodash-es';
Tree Shaking の活用
javascript// webpack.config.js での設定
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
sideEffects: false,
},
};
プロジェクトでの選択基準
新規プロジェクトでの判断
小規模プロジェクト(個人開発・学習)
javascript// 標準 JavaScript を推奨
// 理由:学習効果、依存関係の最小化
const projectConfig = {
useLodash: false,
reasons: [
'学習効果の最大化',
'依存関係の最小化',
'バンドルサイズの最適化',
],
};
中規模プロジェクト(チーム開発)
javascript// チームのスキルレベルに応じて選択
const teamConfig = {
juniorTeam: {
useLodash: true,
reasons: [
'エラーハンドリングの改善',
'可読性の向上',
'開発速度の向上',
],
},
seniorTeam: {
useLodash: false,
reasons: [
'パフォーマンスの最適化',
'最新の JavaScript 機能の活用',
'バンドルサイズの削減',
],
},
};
大規模プロジェクト(エンタープライズ)
javascript// プロジェクトの要件に応じて選択
const enterpriseConfig = {
useLodash: true,
reasons: [
'堅牢性の確保',
'保守性の向上',
'レガシーシステムとの互換性',
],
optimization: {
partialImports: true,
treeShaking: true,
bundleAnalysis: true,
},
};
既存プロジェクトでの移行
段階的な移行戦略
javascript// 移行計画の例
const migrationPlan = {
phase1: {
description: '新機能での標準 JavaScript 使用',
duration: '2-3ヶ月',
actions: [
'新機能では標準 JavaScript を使用',
'Lodash の使用を最小限に抑制',
'チームメンバーの教育',
],
},
phase2: {
description: '既存コードの段階的移行',
duration: '3-6ヶ月',
actions: [
'頻繁に使用される関数から移行',
'テストの充実',
'パフォーマンス測定',
],
},
phase3: {
description: '完全移行と最適化',
duration: '1-2ヶ月',
actions: [
'残りの Lodash 関数の移行',
'依存関係の削除',
'最終的なパフォーマンス最適化',
],
},
};
移行時の注意点
javascript// 移行時のエラーハンドリング
function safeMigration(
originalFunction,
newFunction,
data
) {
try {
const result = newFunction(data);
console.log('Migration successful');
return result;
} catch (error) {
console.warn(
'Migration failed, falling back to original:',
error.message
);
return originalFunction(data);
}
}
// 使用例
const originalMap = _.map;
const newMap = (array, fn) => array.map(fn);
const result = safeMigration(
originalMap,
newMap,
[1, 2, 3],
(x) => x * 2
);
チーム開発での考慮点
コードレビューの基準
javascript// コードレビューガイドライン
const reviewGuidelines = {
lodashUsage: {
allowed: [
'複雑なオブジェクト操作',
'深いネストの処理',
'型チェック関数',
],
discouraged: [
'基本的な配列操作',
'単純な文字列操作',
'標準 JavaScript で十分な処理',
],
},
performance: {
required: [
'大量データ処理でのパフォーマンス測定',
'バンドルサイズの監視',
'メモリ使用量の確認',
],
},
};
チーム教育のポイント
javascript// チーム教育計画
const educationPlan = {
topics: [
'標準 JavaScript の最新機能',
'Lodash の適切な使用場面',
'パフォーマンス最適化手法',
'バンドルサイズの管理',
],
resources: [
'MDN Web Docs',
'Lodash 公式ドキュメント',
'パフォーマンス測定ツール',
'コードレビューガイドライン',
],
};
まとめ
Lodash と標準 JavaScript の使い分けは、プロジェクトの規模、チームのスキルレベル、パフォーマンス要件によって決まります。
標準 JavaScript を選ぶべき場面
- 学習目的のプロジェクト
- パフォーマンスが重要な場面
- バンドルサイズを最小化したい場面
- 最新の JavaScript 機能を活用したい場面
Lodash を選ぶべき場面
- 複雑なオブジェクト操作が必要な場面
- エラーハンドリングの堅牢性が重要な場面
- チームの開発速度を向上させたい場面
- レガシーシステムとの互換性が必要な場面
重要なのは、どちらか一方に固執するのではなく、プロジェクトの要件に応じて適切に選択し、必要に応じて段階的に移行することです。また、チーム全体で統一された基準を持つことで、コードの保守性と可読性を向上させることができます。
最終的に、両者の特徴を理解し、適切に使い分けることで、より効率的で保守性の高いコードを書くことができるでしょう。
関連リンク
- article
Jotai を 120%活用するユーティリティライブラリ(jotai-immer, jotai-xstate, jotai-form)の世界
- article
TypeScript × Vitest:次世代テストランナーの導入から活用まで
- article
スマホでも滑らか!React × アニメーションのレスポンシブ対応術
- article
Zustand でリストデータと詳細データを効率よく管理する方法
- article
Nuxt で API 連携:fetch, useAsyncData, useFetch の違いと使い分け - 記事構成案
- article
htmx の history 拡張で快適な SPA ライク体験を実現
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来