JavaScript の関数は「呼べる処理」だけでなく その定義(コード)を文字列として取り出せる 特別な性質があります。これは toString() を呼ぶことでできます。以下、仕組み → 具体例 → 注意点 → 練習問題 の順で、初心者でも分かるように丁寧に説明します。
1. まず仕組みをざっくり
- JavaScript の関数は
Functionオブジェクト。 - そのオブジェクトにある
toString()を呼ぶと、その関数の「ソースコード(定義したコード)」を文字列で返します。 - つまり
someFunc.toString()は"function name(...) { ... }"のような文字列を返す、ということ。
2. 簡単な例(コンソールで試せます)
関数宣言(function 宣言)
function add(a, b) {
return a + b;
}
console.log(add.toString());
JavaScript出力(例):
function add(a, b) {
return a + b;
}
CSS関数式(匿名関数を変数に代入)
const sum = function(a, b) {
return a + b;
};
console.log(sum.toString());
JavaScript出力(例):
function (a, b) {
return a + b;
}
CSS(関数名がないため function (a, b)... のように出ます)
アロー関数
const mul = (x, y) => x * y;
console.log(mul.toString());
JavaScript出力(例):
(x, y) => x * y
Function コンストラクタで作った関数
const f = new Function('a', 'b', 'return a - b;');
console.log(f.toString());
JavaScript出力(例):
function anonymous(a,b
) {
return a - b;
}
CSS※ anonymous(無名)など出力が少し異なります。
3. 実用的な例:デバッグやログに使う
function greet(name) {
console.log('hello, ' + name);
}
console.log('greet の定義:\n' + greet.toString());
JavaScript→ 関数の中身がログに出るので、「この関数、今どんなコードになってる?」を確認できます。
4. 注意点(大事なポイント)
- クロージャ(外側の変数を参照する関数)の状態は文字列化しても保存できない
toString()は「コード」を返すだけ。実行時の環境(変数の値や閉じたスコープ)は文字列に含まれません。- つまり関数を文字列にして別の場所で実行しても、元のスコープ情報は復元できません。
- エンジンによって表示が微妙に違う
- Chrome、Firefox、Node.js、古いブラウザで
toString()の出力フォーマットや空白の入り方に差があります。テストやデバッグ以外で依存するのはおすすめしません。
- Chrome、Firefox、Node.js、古いブラウザで
- ネイティブ関数は
[native code]と出る
console.log(Math.max.toString());
// "function max() { [native code] }"
JavaScriptつまりブラウザや環境の組み込み関数の中身は見られません。
- セキュリティ/可搬性の問題
eval()やnew Function(...)で文字列化した関数を実行するときは注意(外部入力を eval すると危険)。- コードの送受信・保存に使うときは、依存関係やクロージャをどう扱うかよく考える必要があります。
5. よくある Q&A(初心者向け)
- Q「文字列にした関数をそのまま復元できる?」
- A
evalやnew Functionを使えばある程度は復元できますが、元のスコープ(閉じた変数)は復元できません。セキュリティリスクもあるので慎重に。
- Q「デバッグ以外で使う場面は?」
- A
非常に限定的。ライブラリが関数を解析して自動ドキュメントを作るときなどに使われることがありますが、日常のアプリ開発ではあまり頻繁には使いません。
6. ブラウザで試せる HTML サンプル
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>関数 toString() デモ</title>
</head>
<body>
<button id="btn1">関数宣言の toString</button>
<button id="btn2">アロー関数の toString</button>
<button id="btn3">ネイティブ関数の toString</button>
<pre id="out"></pre>
<script>
function add(a, b) {
return a + b;
}
const arrow = (x) => x * x;
document.getElementById('btn1').onclick = () => {
document.getElementById('out').textContent = add.toString();
};
document.getElementById('btn2').onclick = () => {
document.getElementById('out').textContent = arrow.toString();
};
document.getElementById('btn3').onclick = () => {
document.getElementById('out').textContent = Math.max.toString();
};
</script>
</body>
</html>
HTML- ボタンを押すと、それぞれの関数の定義が
<pre>に表示されます。挙動の違いを体感してみてください。
7. 練習問題
- 次のコードの
toString()の結果は何になる?(予想してから実行してみる)const f = function hello() { return 'hi'; }; console.log(f.toString()); - クロージャの関数を
toString()して、その文字列をeval()で実行すると外側の変数はどうなる?試してみて説明してみよう。 toString()の出力を使って関数名(もしあれば)を簡易的に取得する正規表現を書いてみよう(※実務だと脆弱なので用途限定で)。
解答例コード+解説
練習問題1:名前付き関数式の toString() 結果は?
問題コード
const f = function hello() { return 'hi'; };
console.log(f.toString());
JavaScript✅ 解答(実行結果の例)
function hello() { return 'hi'; }
💡 解説
function hello() { ... }は「名前付き関数式」と呼ばれます。- 変数
fに代入しているため、実際に呼び出すのはf()ですが、toString()は 定義した関数の内容そのまま を文字列化します。 - そのため、定義時の関数名
helloが残るのがポイントです。
console.log(f()); // "hi"
JavaScript⚠️ 注意:hello() では呼び出せません(スコープ外だから)。
練習問題2:クロージャ関数を toString() → eval() で実行したら?
問題コード
function makeAdder(x) {
return function(y) {
return x + y;
};
}
const add5 = makeAdder(5);
console.log(add5(2)); // => 7
// 関数を文字列化
const code = add5.toString();
console.log(code);
// 文字列から新しい関数を作る
const newFunc = eval('(' + code + ')');
console.log(newFunc(2)); // 結果は?
JavaScript✅ 解答(実行結果)
7
function(y) {
return x + y;
}
Uncaught ReferenceError: x is not defined
💡 解説
add5はクロージャ:x(=5)という外側の変数を覚えています。- でも、
toString()は「コード」しか返さないので、「5 という値」や「x のスコープ情報」は消えます。 evalで復元しても、xがない状態で実行するためエラーになります。
つまり:
「関数を文字列にしても、外側の変数(スコープ)は含まれない!」
練習問題3:toString() の結果から関数名を抜き出してみよう
問題コード
function greet() { return 'hi'; }
const str = greet.toString();
const match = str.match(/function\s+([^(]+)/);
console.log(match ? match[1] : '名前なし');
JavaScript✅ 解答(実行結果)
greet
💡 解説
match()で正規表現/function\s+([^(]+)/を使っています。functionのあとに空白\s+- そのあとに「(」が出るまでの文字をグループ
([^(]+)で取得
- つまり
"function greet() { ... }"の中からgreetを取り出しています。
まとめ(今回の学び)
| 観点 | 内容 |
|---|---|
| 関数の文字列化 | .toString() で定義内容をそのまま文字列にできる |
| クロージャとの違い | 外側の変数スコープは文字列化されない |
| 名前付き関数式 | toString() に関数名が残る |
| 正規表現活用 | function の後ろの単語を抽出できる |
