【JavaScript】varとfunction"文"は使わずにletとconstを使って欲しい(切実)

  • 11
    Like
  • 2
    Comment

この記事で言いたいこと

タイトルの通りです。

varとfunction文は使わずにletとconstを使って欲しい。

※多少自分の好みが混ざっています。宗教戦争にならないことを祈ります。
※2017年になって今更というツッコミあるかもしれませんが、未だに横行しています。

なぜなのか

varとfunction"文"の挙動が変だからです。

余談:文と式の違い

式(Expression)と文(Statement) - よねKENのプログラミング研究

文はそれ単独で完結する言語要素です。式はそれ単独では基本的に完結せず、文または式の一部として使用される言語要素です。また、式の最大の特徴として、値を返すという点が挙げられます

実例を見ると下記の通りで、プログラムは文がいくつも並んでいて、文の中に文もしくは式が入るという構成になっています。
JavaScriptにおいては、functionは代入文の中で右辺に式として持ってくることもできるし、単体でfunction文として宣言することが出来ます。どちらで宣言しても同じように利用することができます。

// function 式
// "="より後の部分を"式"といいます
// この一行全体のことを代入文といいます
const shikiFunc = function (){};

// function 文
function bunFunc() {
// この中に代入文やif文を書き、その中に式が含まれる
}

function文を使ってほしくないということは、基本的に関数を使うときは代入文の右辺に持ってくる前者の形で使ってほしいということです。

実例

では、varとかfunction文を使ってほしくない理由を説明していきます。
下記のコードを見て下さい。

console.log(variable);
console.log(func);

var variable = '';
// let variable = '';
function func() {};
// let func = function() {};

まず、これを実行するとどうなるのが自然かというと、変数variableも関数funcも宣言される前にconsole.logしちゃっていますね。
自然に考えてエラーになるはずですが、結果は。。。

undefined
ƒ func() {}

となりました。
variableは"undefined"、つまり定義されていないですよっていう意味の値を出力します。
undefinedとはいえ値は値です。なぜかvariableには値が代入されているのです。
これは仕事でコーディングする時に非常に恐ろしいことになります。エラーが出ていないからとリリースしたら、重要な値が実はundefinedのままだった・・・恐ろしいことです。

後者のfuncに至っては平気な顔して関数の中身をちゃんと出力していますね。タイムスリップでもしているのでしょうか。。。
関数が宣言前に使えるというのは、場合によってはコードが見やすくなるというメリットもあるかもしれませんが、個人的にはやはり上から下にコードを読んでいくのが人間なので、わかりにくいなぁとは思います。

というのも、JavaScriptにおいてはオブジェクトという値と関数をまとめて管理する方法があるので、ベタでfunction文を使って宣言する場面というのは、上から下に実行していきたい場合に限られる気がするのです。(これは個人的な見解ですが)

let object = {
  func: function() {
    let longer = function(string) {
      return string + string;
    }
    console.log(longer(this.variable));
  },
  variable: 'こんにちはー!',
};
object.func(); // こんにちはー!こんにちはー!と出力される

上記のコードで言うところのobjectがオブジェクトと呼ばれる値です。内部にfuncという関数とvariableという値を格納しています。

オブジェクトの中ではvariableがfuncの後に書かれていてもfuncの中で使うことができます。オブジェクトの中身はあくまで値と関数を格納しているだけなので、順番は無関係なのです。

しかし、実際の処理を書くfuncの中で、longerのような関数を使う場合は、コードを読む人は上から下に実行されていることを期待しています。
なのにvarとかfunction文は宣言前に使ってもエラーを吐かないので困ることがあるかもしれないのです。
例えば以下の様な場合。

let object = {
  func: function() {
    // ここで、「ん?longerって何?」ってなる
    // variableにはthis.を付けるのでobjectに格納されている値なんだなって分かるけど。
    console.log(longer(this.variable));

    // ここにいろいろと30行くらい処理が書いてあったりする

    // そしてここにあるんかいー!!っていうね。下は見に行かないよね。上見に行っちゃうよね
    function longer(string) {
      return string + string;
    }
  },
  variable: 'こんにちはー!',
};
object.func(); // こんにちはー!こんにちはー!と出力される

さて、冒頭のコードに戻ってvarとfunction文を使わないようにしましょう。

console.log(variable);
console.log(func);

// var variable = '';
let variable = '';
// function func() {};
let func = function() {};

実行すると・・・?

Uncaught ReferenceError: variable is not defined
    at window.onload ((index):46)

ちゃんとエラーになりましたね!!同様にfuncもエラーになります。

ちなみに

ざっくり仕組みを説明しますと、JavaScriptは実行する時に、最初に使われそうな変数をコードを眺めて初期化してしまうという挙動をしています。

なので、さっきのコードだとこんな感じの順序で動いています。

  1. よっしゃー今から実行するでー。まずは準備からや
  2. 下の方にvar variableとfunc()がおるやん。おけおけ
  3. func()に関しては関数の中身も代入して初期化しておこう
  4. variableはとりあえずundefined入れとこ
  5. 準備もできたし、じゃあ中身実行するかー
  6. console.logでvariable使っているけど、現時点だとundefinedやな
  7. console.logでfunc読んでるから、さっき初期化した関数渡してあげよ
  8. 以下略

こちらの記事が熱弁を振るっていて、当時めちゃめちゃ勉強になったので、もし気合のある方は読んでみて下さい。
http://maeharin.hatenablog.com/entry/20130313/javascript_scopechain

しかし、letで宣言した場合はこのように最初にundefinedを入れたりしないので、宣言前に変数を使おうとした場合はエラーになるように動きます。

const

constもletと同じように宣言前に使うとエラーになることに加えて、constで宣言した値は中身を変更できません。
不便なように見えますが、これも後からコードを読む人が、「この値はいつ変更されるかわからないから丁寧にコードを読もう」ではなく「constなら値が変更しないな、ずっと初期値のままなのね」という感じで負担軽く読めるので、変更の予定がない値に関してはconstで初期化するようにしましょう。

まとめ

varとfunction文は使わずにletとconstを使って欲しい。

function文を使わずに代入文に統一したほうが、変数の代入と同じ書き方になるので見やすいというメリットもあります。

※このような挙動はJavaScriptに限った話なので気をつけて下さい。

ちなみに、functionを書くときは現代ではアロー関数式を使うことがおすすめです。
【JavaScript】アロー関数式を学ぶついでにthisも復習する話※自薦乙

33contribution

var宣言やfunction式は名前の巻き上げが厄介だ、という動機ならまずはstrictモード(厳格モード)にするのが先です。
明示的にstrictモードにしないままだとletやconstが正しく解釈されないブラウザがあります。
あのIEでも10以降ならこのモードに対応してるので、宣言より前に使おうとするとちゃんとエラーを投げてくれます。

72contribution

コメントありがとうございます!
そうですね、strictモードにした上で巻き上げに気をつけるのが正しい順番だと思います(まあどちらにせよvarとかfunction文は基本的には避けて欲しいと思っているのですが)。

>明示的にstrictモードにしないままだとletやconstが正しく解釈されないブラウザがあります。
これは知りませんでした!ありがとうございますm(__)m