19
17

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.

JavaScriptの酒のつまみになる話

Posted at

はじめに

JavaScriptを触っていると少し直感とは反するような挙動をするようなことがあると思います。
今日は直感と反する内容とその対処方法について説明したいと思います。
具体的には「宣言されていない変数への代入はグローバル変数が作成される」「書き換え不可能なグローバル変数への代入が正常終了する」「関数の引数名の重複がある場合に後の引数が勝つ」の3つになります。

宣言されていない変数への代入はグローバル変数が作成される

このことを試すために簡単なテストをしたいと思います。
下記の通りテスト用のコードを作成します。階層はすべて同じ場所に置きます。

test.html
<html>
  <head></head>
  <body>
    <div id = "print"></div>
    <script type="text/javascript" src="test1.js"></script>
    <script type="text/javascript" src="test2.js"></script>
  </body>
</html>
test1.js
function test() {
   var_test = "vars test" // ポイントはここで定義されていない変数への代入を行っていること
}
test()
test2.js
function print_html(){
  document.getElementById("print").innerHTML = "<p>" + var_test +"</p>" // 別のjsにあるvar_testを使用する
}
print_html()

直感的には上記のとおりなコーディングを行った場合はエラーとなりそうですが、宣言されていない変数への代入はグローバル変数を作成して代入する挙動なのでprint_htmlの中でvar_testを使用することができます。
image.png

もちろん、testの中でvarletを使用して定義していればグローバル変数が作成されるわけではないのでエラーとなります。

test1.js
function test() {
   var var_test = "vars test" // test1.jsの中で変数定義して代入
}
test()

image.png

書き換え不可能なグローバル変数への代入が正常終了する

そもそもグローバル変数だったの??って思う人もいるかもですが、undefined等のグローバル変数は値を書き換えることができません。万が一そういった変数に対して値を代入してしまった場合はエラーが起きるわけではなく値の書き換えはされないままとなります。
その検証のために以下のように先ほどのtest2.jsを下記のように変更してみます。(今回の調査にtest1.jsは関係ないです)

test2.js
var undefined = "undefined test";

function print_html(){
  document.getElementById("print").innerHTML = "<p>" + undefined +"</p>"
}
print_html()

image.png

上記の通りグローバル変数のundefinedに対して値をundefined testを代入していますが、画面に表示されているのはundefinedとなっており、変数は更新されていません。

関数の引数名の重複がある場合に後の引数が勝つ

関数の引数名が重複している場合は最初の引数は隠されて後で宣言されている引数が展開されます。
その検証のために以下のように先ほどのtest2.jsを下記のように変更してみます。(今回の調査にtest1.jsは関係ないです)

test2.js
function print_html(test, test, test2){
  document.getElementById("print").innerHTML = "<p>" + test +"</p>"+"<p>" + test2 +"</p>"
}
print_html('test','update test', 'test2')

image.png

print_htmlにてtestが重複しているのですがこれはエラーになることなく1つめの引数はなかったことになり2つ目の値で展開されます。

解決方法「Strict モード」

ここまでの問題を見た時点で知っている人であればStrictモードでの解決方法なんだろうなと分かると思います。
Strictモードは関数やファイルの先頭に'use strict';を追加することで使用可能となります。

※スコープに関わる話ではあるので本当はもっと詳しく書くべきですが、今回は割愛します。参考にしたページを見ていただければと思います。
Strictモードで実行した場合はここまでの問題はすべてエラーとなって実行できなくなります。

宣言されていない変数への代入はグローバル変数が作成される

Strictモードを使用した場合はtest1.jsの時点でvar_testが未定義の変数への代入のためエラーとなります。
image.png

書き換え不可能なグローバル変数への代入が正常終了する

Strictモードを使用した場合はundefinedは書き換え不可能なグローバル変数なのでエラーになります。
image.png

関数の引数名の重複がある場合に後の引数が勝つ

Strictモードを使用すると関数の重複がある時点でエラーとなります。
image.png

終わりに

Strictモードの説明についてStrictモードありきでの説明が多く問題の解決策として説明する記事があまりないなと思って今回の記事を書きました。
Strictモードで解決される問題はここに紹介しただけではありませんし、Strictモードを使うとする場合にも気にするべきポイントはたくさんあります。Strictモードを使うにしても使わないにしてもこういったようなJavaScriptを使用する上で気にしたほうが良い問題点を抑えておくことは重要だと思います。
この記事を機にStrictモードやこれらのJavaScriptの挙動についても目を向けていただければなと思います。

参考にしたページ

19
17
0

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
19
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?