🦏

JavaScriptで関数に独自プロパティを追加する方法とその活用例

2023/11/05に公開

概要

JavaScript の値はすべて、論理型・数値型・文字列型など何らかの型を持ちます[1]
もちろん関数も型を持ち、JavaScript の関数はオブジェクト型とされています。

関数がオブジェクト型であるということは、関数がプロパティをもつことができるということです。今回の記事は JavaScript の関数に独自プロパティを定義する方法について解説させていただきます。

この記事を読んでわかること

  • JavaScript の関数に独自プロパティを定義する方法
  • 関数に独自プロパティを定義することでできること

対象読者

  • JavaScript 初心者
  • JavaScript にある程度馴染みのある開発者

前提知識・環境

  • JavaScript の文法の基本的な知識

JavaScript の関数に独自プロパティを定義する方法

JavaScript のオブジェクトのプロパティ

本題に入る前に、JavaScript のオブジェクトのプロパティについて簡単に確認しておきます。

オブジェクトのプロパティ
const person = {
  name: 'Mike',
  age: 30,
  hobbies: ['movie', 'baseball', 'reading']
};

console.log(person.name);   // Mike
console.log(person['age']); // 30

オブジェクトのプロパティというと最初に思いつくのはこのようなケースですね。
person オブジェクトは name, age, hobbies など様々な型のプロパティを持ち、person.name または person['age'] のような記述方法によってその値を取得することができます。

また、下記のようにあとからプロパティを追加することも可能です。

オブジェクトのプロパティの追加
person.occupation = 'engineer';
person['nationality'] = 'japan';
console.log(person.occupation);  // engineer
console.log(person.nationality); // japan

では関数のプロパティはどのように表現されるのでしょうか?

JavaScript の関数のプロパティ

関数も先程の例と同様に関数名に . をつけてプロパティの値を取得できます。

関数のプロパティ
const uniqueInteger = () => uniqueInteger.counter++;
uniqueInteger.counter = 0;

console.log(uniqueInteger.counter); // 0
console.log(uniqueInteger());       // 0

console.log(uniqueInteger.counter); // 1
console.log(uniqueInteger());       // 1

console.log(uniqueInteger.counter); // 2
console.log(uniqueInteger());       // 2

uniqueInteger 関数に counter プロパティを設けることで uniqueInteger.countercounter プロパティの値を取得できます。

また、uniqueInteger 関数自体は counter プロパティ自体をインクリメントして返すので実行するたびに counter プロパティの値が更新されます。

関数のプロパティの使用例

機能として関数にプロパティをもたせることができるということがわかりました。
ではどのように利用できるのでしょうか?

例えば下記のような階乗の計算を行う factorial 関数について見てみましょう。

階乗計算の関数
const factorial = (n) => {
  if (Number.isInteger(n) && n > 0) {
    if (!(n in factorial)) {
      factorial[n] = n * factorial(n - 1);
    }
    return factorial[n];
  } else {
    return NaN;
  }
};

factorial[1] = 1;
console.log(factorial(6)); // 720
console.log(factorial[5]); // 120

factorial 関数は引数に任意の整数を受け取り、n の階乗を計算します。
n 番目のプロパティが保存されていなかった場合は factorial[n] に計算結果を保存します。

したがって、factorial(6) を実行済みの状況であれば factorial[5](5の階乗の計算結果)や factorial[3](3の階乗の計算結果)はプロパティとして保存された値からすぐに呼び出す事ができます。

このように JavaScript の関数のプロパティの使用例として実行結果をキャッシュとして保存しておくことが挙げられます。


JavaScript の関数のプロパティに関する個人的意見

今回紹介した中で uniqueInteger 関数を使って実行回数のカウントを行いました。
uniqueInteger 関数でプロパティを使用するメリット・デメリットそれぞれ考えて行きたいと思います。

uniqueInteger.counter で関数の実行回数を管理するメリット

uniqueInteger.counter で実行回数を管理するメリットは、counteruniqueInteger 関数の実行回数である とはっきりわかることだと思います。

例えば下記のように uniqueInteger 関数のスコープ外で変数宣言を行う場合、counter は何に対して回数を数えているのか示されていません。

counterを関数のスコープ外で宣言する場合
let counter = 0;
const uniqueInteger = () => counter++;

console.log(uniqueInteger()); // 0
console.log(uniqueInteger()); // 1

uniqueInteger 関数の処理を確認することで、その実行回数を数えていることを察することができますが、誤って counter 変数の値を関係のない箇所で更新してしまうかもしれません。

uniqueInteger.counter として管理することで、どの関数の実行回数であるかがわかりやすくなります。

uniqueInteger.counter で関数の実行回数を管理するデメリット

サンプルコードで示したように uniqueInteger.counter として関数の実行回数を管理する場合、プロパティの初期値を関数のスコープ外で定める必要があります。

プロパティの初期値の設定
const uniqueInteger = () => uniqueInteger.counter++;
uniqueInteger.counter = 0; // 初期値の宣言

もし、初期値の宣言を忘れたり、コードを管理する過程でこの部分に余計な変更が加えられると予期せぬバグの原因となりそうです。

メリット・デメリットの両方について考えたうえで、今回のケースではクロージャを使った記述方法が筆者の好みです。クロージャを使った方法についてはまた別の記事で解説させていただきます。


最後まで読んでくださりありがとうございました🙇‍♂️
JavaScript のプロパティと考えると最初に示したオブジェクトのプロパティと想像される方が多いと思います。
関数でもプロパティを使えることを知っておくだけでコードを読むときの手助けになるはずです。

脚注
  1. mdn web docs JavaScript のデータ型とデータ構造 ↩︎

Discussion