0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

HTML 内の JavaScript で関数を定義する方法がいろいろあるけど使い分けがよくわからないので調べてみる

Last updated at Posted at 2021-07-09

HTML 内の JavaScript で関数を定義するには次のような方法があると思います。

  • function なんちゃらと書く方法 (関数の定義)
function hello1() {
    console.log('hello1');
}
  • 無名関数を変数に突っ込む方法 (関数式)
const hello2 = function () {
    console.log('hello2');
};

どっちも意味同じじゃん!とか思ってしまったので、以下の実験をしてみました。
参考になれば、と思います。

iframe からアクセスしてみる

2 つの関数を <script> タグに記述し、同一ページおよび iframe 内からアクセスしてみます。
また、 window を先頭に付ける場合と付けない場合のパターンもあわせて検証してみます。

手順

まずは 2 つのページを作成します。

page1.html
<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>
page2.html
<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_before_def.js
hello1();
function hello1() {
    console.log('hello1');
}
hello2_before_def.js
hello2();
const hello2 = function () {
    console.log('hello2');
};

定義後

hello1_after_def.js
function hello1() {
    console.log('hello1');
}
hello1();
hello2_after_def.js
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

ブロックスコープに入れる

以下のようなスクリプトを作成し、実行します。

hello1.js
{
    function hello1() {
        console.log('hello1');
    }
}
hello1();
hello2.js
{
    const hello2 = function () {
        console.log('hello2');
    };
}
hello2();

結果

hello2 ではスコープ制限できたが、 hello1 はスコープが制限できなかった。

script output
hello1 hello1
hello2 Uncaught ReferenceError: hello2 is not defined

即時実行関数の中に入れる

以下のようなスクリプトを作成し、実行します。

hello1.js
(function () {
    function hello1() {
        console.log('hello1');
    }
})();
hello1();
hello2.js
(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

再帰関数を作ってみる

以下のようなスクリプトを作成し、実行します。

hello1.js
function hello1(times) {
    if (times === 1) {
        return 'hello';
    }
    return 'hello' + hello1(times - 1);
}
console.log(hello1(3));
hello2.js
const hello2 = function (times) {
    if (times === 1) {
        return 'hello';
    }
    return 'hello' + hello2(times - 1);
}
console.log(hello2(3));

結果

どちらも正常に表示できた。

script output
hello1 hellohellohello
hello2 hellohellohello

速度は?

以下のようなスクリプトを作成し、実行します。

hello1.js
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`);
hello2.js
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

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?