DESIGNMAP

  1. TOP
  2. 公開講座
  3. ゼロから始めるJavaScript入門
  4. 変数のスコープ、varletの違い、巻き上げ、定数 ー ゼロから始めるJavaScript入門(ECMAScript 2015)【Vol.10】

変数のスコープ、varletの違い、巻き上げ、定数 ー ゼロから始めるJavaScript入門(ECMAScript 2015)【Vol.10】

今回は変数のスコープと定数を学びます。
ローカル変数を宣言するときに、varletが使えますが、違いをじっくり学んでいきます。

変数のスコープ(varlet

変数には有効範囲があります。この有効範囲をスコープといいます。

JavaScriptにはグローバル変数、ローカル変数(関数スコープ)、ローカル変数(ブロックスコープ)の3つの範囲があります。
範囲の大きさは「グローバル変数 > ローカル変数(関数スコープ)> ローカル変数(ブロックスコープ)」です。

JavaScriptは長らくブロックスコープは存在しませんでした。そのため今まではトリッキーなコードを書いて開発者は凌いできました。JavaScript開発者は、機能がないないりに工夫してきました。ECMAScript2015でようやくブロックスコープが仕様に追加されました。

まず大原則で覚えておきたいのは、変数はなるべく狭いスコープで利用することです。つまりローカル変数(ブロックスコープ)をなるべく使う、グローバル変数は使わない、が基本原則です。JavaScriptではブロック内でletで宣言すると、ブロックスコープになります。

下記のコードをご覧ください。コードは書かなくて大丈夫です。

var name = "田中太郎";

function hello(){
  var name = "田中二郎";
  
  if (true){
    let name = "田中三郎";
    console.log(name); //->田中三郎
  }

  console.log(name); //->田中二郎
}

hello();

console.log(name); //->田中太郎

同じ名前の変数が3ヶ所で使われていますが、ぞれぞれスコープが違うため、別な変数として扱われます。
図でスコープを説明します。

//->は出力される値です。実際にはこのような同じ名前の変数を多用したコードは読みにくく、誤解されやすいので書きません。あくまでスコープの説明用のコードです。

関数の外側で宣言された変数がグローバル変数です。
関数の中で定義された変数はローカル変数といいます。

関数の中に、if文があり、ブロック({ })があります。このブロック内でletで定義された変数はブロック内だけに限定されますブロックスコープの変数は、ブロックを抜けると、破棄され使えなくなります。

ブロックスコープが一番狭いスコープです。

if文の中のconsole.log(name);は、田中三郎が出力されますが、if文のブロックを抜けた後の、console.log(name);は、関数スコープに変わり、田中二郎出力されます。

関数をぬけると、グローバルスコープに変わり、一番下のconsole.log(name);田中二郎が出力されます。

内側のスコープから外側のスコープを取得する

グローバル変数についてもう少し考えてみましょう。
以下のコードを見てください。田中二郎が出力されます。

var name = "田中太郎";

function hello(){

  if (true){  
    console.log(name); //->田中太郎
  }

}

hello();

ブロック内部のconsole.log(name);name変数はまず、ブロック内の同じ名前の変数を探します。ない場合は、ひとつ上のスコープである関数内のスコープを探します。それでもない場合は、関数の外側のグローバルスコープを探します。

このようにスコープの内側から、スコープの外側の変数を取得することができます。
逆にスコープの外側から、内側のスコープの変数にアクセスできません。

letvarの違い

letvarの違いをもう少しじっくり学びます。

letはブロックスコープなので、関数スコープのvarより狭い範囲に変数を封じ込めることができます。
ブロックとは{}の部分です。

if (true) {
  let i = "Hello";
}
console.log(i); //Uncaught ReferenceError: i is not defined.

iはletで変数宣言をしているので、ブロックを抜けると変数は使えなくなります。
JavaScriptコンソールで試すと「Uncaught ReferenceError: i is not defined.」というエラーが表示されます。変数iは未定義ですというメッセージです。

if (true) {
  var i = "Hello";
}
console.log(i);//Hello

変数をvarで宣言した場合、ブロックをぬけても変数iは生き続けます。

ブロックは入れ子になることもあります。
その場合、ブロックが違えば、変数に同じ名前をつかっても別な変数として扱われます。

 function hello (condition) {
    let name = "Taro";
    
    if (condition) {
        let name = "Hanako";
        console.log(name); //Hanako
    }
    
    console.log(name); //Taro
}

hello(true);

varの場合は関数スコープなので、同一の変数になってしまいます。

function hello (condition) {
    var name = "Taro";
    
    if (condition) {
        var name = "Hanako";
        console.log(name);// Hanako
    }
    
    console.log(name); //Hanako
}

hello(true);

しかも今回の場合、varは関数スコープなので、同じスコープ内に同じ名前の変数を二つ定義することになってしまいます。varは二重定義をしても、値を黙って上書きしてエラーになりません。

letの二重定義はシンタックスエラーになる

letは二重定義をするとエラーになります。

let name = "太郎";
let name = "次郎";

これを実行すると「Uncaught SyntaxError: Identifier 'name' has already been declared」というエラーメッセージが表示されます。すでに定義済みというメッセージです。

varは巻き上げをして、letは巻き上げをしない

var宣言の特徴に巻き上げがあります。

console.log(i); //undefined
var i = 0;

変数を宣言する前に、変数iを参照しています。コードはエラーにならず「undefined」が表示されます。これは

var i;
console.log(i); //undefined
i = 0;

と変数の宣言がコードの頭に巻き上げされるからです。

同じコードをletで書くととエラーになります。

console.dir(i); //Uncaught ReferenceError: i is not defined
let i = 0;

「Uncaught ReferenceError: i is not defined」というエラーがでます。

定数の宣言 const

constletと同じくブロックスコープですが、定数の宣言につかいます。定数は値の最代入ができなくなります。constで宣言をすると、定数になります。

const name = "太郎";
name = "次郎";

は「Uncaught TypeError: Assignment to constant variable.」というエラーが表示されます。

スポンサーリンク

関連記事

プロフィール

DESIGNMAP
制作ディレクター
ON VISITINGを制作・運営。
お問い合わせ