es-toolkitの便利な関数を見てみよう!


こんにちは。フロントエンドエンジニアの辻です。
Gaji-LaboではNext.jsやReactをはじめとして、その時々のモダンなフロントエンド技術を積極的に採用しています。

今回は私の好きなライブラリでもあるes-toolkitの便利な関数を見ていきます!
es-toolkitは2024年に登場した新進気鋭のライブラリで、2024年後半に爆発的に人気になりました。
es-toolkitがどんなものかの紹介はこちらの記事で解説してします。

それでは個人選定になりますが、es-toolkitに用意された便利な関数を見ていきましょう!

es-toolkitの便利な関数

toMerged

toMergedはオブジェクト型の引数を2つ取り、第一引数に第二引数をマージしたオブジェクトを生成します。入れ子構造もうまくマージしてくれます。
以下のサンプルコードにおいて、xオブジェクトとyオブジェクトのbプロパティは、最終的にすべてがマージされ、b: { c: 20, d: 30, e: 50, f: 60 }となります。

import { toMerged } from "es-toolkit";

const x = { a: 10, b: { c: 20, d: 30 } };
const y = { b: { e: 50, f: 60 }, g: 40, h: { i: 50, j: 60 } };
console.log(toMerged(x, y)); // { a: 10, b: { c: 20, d: 30, e: 50, f: 60 }, g: 40, h: { i: 50, j: 60 } }

似たような関数としてmergeがありますが、mergeは第一引数のオブジェクトをマージ後のオブジェクトで上書きします。対してtoMergedは引数に変更を加えることはありません。
副作用のことを考えると、よく利用するのはtoMergedの方ですね。

toMerged: https://es-toolkit.slash.page/reference/object/toMerged.html
merge: https://es-toolkit.slash.page/reference/object/merge.html

pick

pickは引数を2つ取ります。
第一引数に対象となるオブジェクト。第二引数には、第一引数のオブジェクトから抽出したいプロパティのキー名を配列形式で指定します。
返り値は「第一引数のオブジェクトから、第二引数で指定したキー名に該当するプロパティを抽出したオブジェクト」になります。

import { pick } from "es-toolkit";

const x = { a: 10, b: { c: 20, d: 30 }, e: 40 };
console.log(pick(x, ["a", "b"])); // { a: 10, b: { c: 20, d: 30 } }

また、es-toolkit/compatのpick関数を利用すると、ネストされたプロパティも指定できます。
es-toolkit/compatはLodashとの互換性を確保するために作られたモノです。
Lodashからes-toolkitへ移行する際、すぐにes-toolkitの関数で置き換えるのではなく、はじめにes-toolkit/compatの関数を利用することでスムーズに移行できます。
「どの関数に互換性があるか」は公式サイトのImplementation Statusから確認できます。

import { pick } from "es-toolkit/compat";

const x = { a: 10, b: { c: 20, d: 30 }, e: 40 };
console.log(pick(x, ["a", "b.c"])); // { a: 10, b: { c: 20 } };

ちなみに、pick関数と反対の挙動をするomitも用意されています。

pick: https://es-toolkit.slash.page/reference/object/pick.html
omit: https://es-toolkit.slash.page/reference/object/omit.html
es-toolkit/compat: https://es-toolkit.slash.page/compatibility.html

memoize

memoizeは、引数に指定した関数の返り値をキャッシュさせる関数です。
memoize関数で生成した関数は、引数が変更されない限り再計算されることがなく、直前に計算した値をキャッシュし続けます。

以下のサンプルコードでは、1から100の範囲で乱数を返すprintRandom関数をmemoize関数にセットして、memoized関数として定義しています。
memoized関数を10回ほど呼び出していますが、毎回乱数を返すことはなく、常に同じ返り値となります。

11回目でmemoized関数の引数をfirstからsecondに変更していますね。
このタイミングで引数の変更が入ったため、再計算されて新しい値がキャッシュされました。

import { memoize } from "es-toolkit";

const printRandom = (prefix: string) => `${prefix}: ${Math.floor(Math.random() * 100) + 1}`;
const memoized = memoize(printRandom);

console.log(memoized("first"));  //  1回目  first: 91 ← 以降、返り値がキャッシュされる
console.log(memoized("first"));  //  2回目  first: 91
console.log(memoized("first"));  //  3回目  first: 91
console.log(memoized("first"));  //  4回目  first: 91
console.log(memoized("first"));  //  5回目  first: 91
console.log(memoized("first"));  //  6回目  first: 91
console.log(memoized("first"));  //  7回目  first: 91
console.log(memoized("first"));  //  8回目  first: 91
console.log(memoized("first"));  //  9回目  first: 91
console.log(memoized("first"));  // 10回目  first: 91
console.log(memoized("second")); // 11回目 second: 62 ← 引数を変更すると再計算される
console.log(memoized("second")); // 12回目 second: 62
console.log(memoized("second")); // 13回目 second: 62
console.log(memoized("second")); // 14回目 second: 62
console.log(memoized("second")); // 15回目 second: 62
console.log(memoized("second")); // 16回目 second: 62
console.log(memoized("second")); // 17回目 second: 62
console.log(memoized("second")); // 18回目 second: 62
console.log(memoized("second")); // 19回目 second: 62
console.log(memoized("second")); // 20回目 second: 62

memoize: https://es-toolkit.slash.page/reference/function/memoize.html

debounce

debounceは、第一引数に関数を、第二引数に時間を指定します。
すると、「第一引数の関数が最後に呼び出されたときから、第二引数の時間が経過するまで、第一引数の関数の発火を間引く関数」を定義できます。

以下のサンプルコードでは、debounce関数を使ってdebounced関数を定義して、ブラウザサイズが変更されたときに呼び出せるようにしています。

window.addEventListener("resize", resize)とすると、リサイズイベント発生時にresize関数が毎回呼び出されてしまいます。ほんのちょっとしたリサイズでもresize関数が連続発火します。
この問題を回避するためにdebounce関数の第二引数に0.5 * 1000 = 0.5秒を指定しています。

これにより「resize関数が最後に発火したときから、0.5秒間はresize関数の発火を間引く。0.5秒経過後にresize関数を発火させる」debounced関数が完成し、リサイズイベントの連続発火を抑制できます。

import { debounce } from "es-toolkit";

const resize = () => {
  console.log("window resized");
};

const debounced = debounce(resize, 0.5 * 1000);

// window.addEventListener("resize", resize); // リサイズイベント発生時にresize関数が連続発火してしまう
window.addEventListener("resize", debounced); // resize関数発火後の0.5秒間は、resize関数の再発火を間引く

debounce: https://es-toolkit.slash.page/reference/function/debounce.html

まとめ

以上、(個人選定による)es-toolkitの便利な関数を見てきました!
es-toolkitのようなユーティリティライブラリがあれば、わざわざ車輪の再発明をせずに済みますね。

Gaji-Laboでは、その時々のモダンなフロントエンド技術を積極的に採用しています。
技術的にも積極的にチャレンジできる環境が整っていますので、フロントエンドエンジニア職に興味のある方はお気軽にご応募ください!

Gaji-Labo フロントエンドエンジニア向けご案内資料

Gaji-Labo は新規事業やサービス開発に取り組む、事業会社・スタートアップへの支援を行っています。

弊社では、Next.js を用いた Web アプリケーションのフロントエンド開発をリードするフロントエンドエンジニアを募集しています!さまざまなプロダクトやチームに関わりながら、一緒に成長を体験しませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください!

求人応募してみる!


投稿者 Tsuji Atsuhiro

フロントエンドエンジニア。 DTP・Webデザイナーを経験した後、フロントエンドエンジニアに転向。HTML/CSS/JavaScriptを中心にWeb開発を担当してきました。 UI・UXに興味があり、デザイン・コーディング両面から考えられるデザインエンジニアを目指しています。 普段はマラソンやボクシングなどで体を動かしてます。