HTML 内の JavaScript で関数を定義するには次のような方法があると思います。
- function なんちゃらと書く方法 (関数の定義)
function hello1() {
console.log('hello1');
}
- 無名関数を変数に突っ込む方法 (関数式)
const hello2 = function () {
console.log('hello2');
};
どっちも意味同じじゃん!とか思ってしまったので、以下の実験をしてみました。
参考になれば、と思います。
iframe からアクセスしてみる
2 つの関数を <script>
タグに記述し、同一ページおよび iframe 内からアクセスしてみます。
また、 window
を先頭に付ける場合と付けない場合のパターンもあわせて検証してみます。
手順
まずは 2 つのページを作成します。
<script>
function hello1() {
console.log('hello1');
}
const hello2 = function () {
console.log('hello2');
};
</script>
<h1>parent</h1>
<button onclick="console.log(hello1);">hello1</button>
<button onclick="console.log(hello2);">hello2</button>
<br>
<button onclick="console.log(window.hello1);">window.hello1</button>
<button onclick="console.log(window.hello2);">window.hello2</button>
<h1>iframe</h1>
<iframe src="page2.html"></iframe>
<button onclick="console.log(parent.hello1);">hello1</button>
<button onclick="console.log(parent.hello2);">hello2</button>
<br>
<button onclick="console.log(parent.window.hello1);">window.hello1</button>
<button onclick="console.log(parent.window.hello2);">window.hello2</button>
ウェブサーバを立ち上げ、各ボタンをクリックします。(ファイルを直接ブラウザで開いただけでは iframe からのアクセスができません)
開発者ツールで出力を確認します。
結果
const は同一ページからでないと見えない。
parent
直アクセスでは hello1, hello2 ともに見えるが、 window 経由では hello2 が見えない。
button | output |
---|---|
hello1 | ƒ hello1() { console.log('hello1'); } |
hello2 | ƒ () { console.log('hello2'); } |
window.hello1 | ƒ hello1() { console.log('hello1'); } |
window.hello2 | undefined |
iframe
hello1 は見えるが hello2 が見えない。
button | output |
---|---|
parent.hello1 | ƒ hello1() { console.log('hello1'); } |
parent.hello2 | undefined |
parent.window.hello1 | ƒ hello1() { console.log('hello1'); } |
parent.window.hello2 | undefined |
関数の定義前後で呼び出してみる
HTML 内に以下のようなスクリプトを記述して検証します。
定義前
hello1();
function hello1() {
console.log('hello1');
}
hello2();
const hello2 = function () {
console.log('hello2');
};
定義後
function hello1() {
console.log('hello1');
}
hello1();
const hello2 = function () {
console.log('hello2');
};
hello2();
結果
function は定義場所があまり関係ない。
const は定義する場所が重要。
定義前
hello2 でエラーが発生した。定義前にアクセスできない。当然かもしれない。
script | output |
---|---|
hello1_before_def | hello1 |
hello2_before_def | Uncaught ReferenceError: Cannot access 'hello2' before initialization |
定義後
hello1, hello2 ともに正常に表示された。
script | output |
---|---|
hello1_after_def | hello1 |
hello2_after_def | hello2 |
ブロックスコープに入れる
以下のようなスクリプトを作成し、実行します。
{
function hello1() {
console.log('hello1');
}
}
hello1();
{
const hello2 = function () {
console.log('hello2');
};
}
hello2();
結果
hello2 ではスコープ制限できたが、 hello1 はスコープが制限できなかった。
script | output |
---|---|
hello1 | hello1 |
hello2 | Uncaught ReferenceError: hello2 is not defined |
即時実行関数の中に入れる
以下のようなスクリプトを作成し、実行します。
(function () {
function hello1() {
console.log('hello1');
}
})();
hello1();
(function () {
const hello2 = function () {
console.log('hello2');
};
})();
hello2();
結果
hello1, hello2 ともにスコープが制限された。
script | output |
---|---|
hello1 | Uncaught ReferenceError: hello1 is not defined |
hello2 | Uncaught ReferenceError: hello2 is not defined |
再帰関数を作ってみる
以下のようなスクリプトを作成し、実行します。
function hello1(times) {
if (times === 1) {
return 'hello';
}
return 'hello' + hello1(times - 1);
}
console.log(hello1(3));
const hello2 = function (times) {
if (times === 1) {
return 'hello';
}
return 'hello' + hello2(times - 1);
}
console.log(hello2(3));
結果
どちらも正常に表示できた。
script | output |
---|---|
hello1 | hellohellohello |
hello2 | hellohellohello |
速度は?
以下のようなスクリプトを作成し、実行します。
function hello1() {
return 1;
}
let sum = 0;
const start = performance.now();
for (let i = 0; i < 10000000; ++i) {
sum += hello1();
}
console.log(sum === 10000000);
console.log(`Elapsed: ${performance.now() - start} ms`);
const hello2 = function () {
return 1;
}
let sum = 0;
const start = performance.now();
for (let i = 0; i < 10000000; ++i) {
sum += hello2();
}
console.log(sum === 10000000);
console.log(`Elapsed: ${performance.now() - start} ms`);
結果
hello2 の方が速いように見えるが、ページを何回か更新して時間を見ているとさほど変わらなかった。
function | result | time |
---|---|---|
hello1 | true | 29 ms |
hello2 | true | 26.5 ms |
まとめ
実験 | function | const |
---|---|---|
iframe からアクセス | o | x |
定義部分の上側でアクセス | o | x |
ブロックスコープで制限 | x | o |
即時実行関数でスコープを制限 | o | o |
再帰関数 | o | o |
実行速度 | そんなに | かわらない |
function と const では使えるスコープに差が見られましたが、それ以外の違いはあまりわかりませんでした。
読みやすさやスコープを意識して書き分けるのが重要なのかなと思いました。
参考にしたページ
https://stackoverflow.com/questions/13323237/anonymous-function-vs-normal-function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function