「JavaScript」変数・スコープ・吊り上げ


このチュートリアルでは、var, let, const の違いや、それらを宣言する方法、名前を付けて定義する方法を調べていきます。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

変数とは何か?

簡単に言えば、変数とは値を格納する容器のことです。例えば、何度か参照する情報を変数に保存して、後でアクセスしたり、変更したりすることができます。数字、文字列、オブジェクト、またはその他のデータ型を JavaScript の変数コンテナに格納することができます。現代のJavaScriptはECMAScript 2015(ES6)の言語仕様に基づいており、当初は唯一利用可能だったvarキーワードから変数宣言が拡張されました。そのため、古い学習リソースではvarしか見当たらないでしょう。このチュートリアルでは、以下のvar、let、constキーワードの違いを探るためにセクション全体に注力しています。ここでは、変数の概念を宣言し、それに値を代入することで、変数の概念を定義するために、1つの型だけを使用します。

// Assign the string value Sam to the username identifier
var username = "sam_shark";

当社のステートメントは、いくつかの条件で構成されています。
1、var キーワードは変数の宣言に使用されています。
2、ユーザ名は、変数の識別子または変数名です。
3、構文は、ステートメントの代入操作を表します。
4、この場合の値は "sam_shark "です。

これで、JavaScriptのコードにユーザー名を実装することができるようになりました。

// Check if variable is equal to value
if (username === "sam_shark") {
  console.log(true);
}
Output
true

すでに述べたように、変数を使ってすべてのJavaScriptのデータ型を表現することができます。次の変数宣言では、文字列、数値、オブジェクト、ブール値、ヌル値を使って実験してみましょう。

// Assignment of various variables
var name = "Sam";
var warriors = 300;
var kingdoms = [ "mammals", "birds", "amphibian" ];
var poem = { roses: "red", bananas: "green" }; 
var success = true;
var nothing = null;

console.log.logを使用して、各変数にどのような値が含まれているかを調べることができます。

// Send warriors variable to the console
console.log(warriors);
Output
300

変数に含まれるデータはメモリに保存されているので、後でアクセスして修正することができます。また、変数に新しい値を代入することもできます。以下の新しい例では、変数にパスワードが格納されています。パスワードを変更する方法を見てみましょう。

// Assign value to password variable
var password = "pass1";

// Reassign variable value with a new value
password = "pass2";

console.log(password);
Output
'pass2'

パスワードはむしろ安全なデータベースに保存されるので、上記の方法で保存することは考えにくいでしょう。しかし、この例では変数に含まれる値を更新する方法を示しています。最初、パスワードに割り当てられた値は pass1 でしたが、後に pass2 に更新しました。その後、JavaScript は更新後の新しい値を認識するようになりました。

変数の名前の付け方

JavaScriptでは、変数名は識別子とも呼ばれます。しかし、変数名を付ける際には、以下のルールを頭に入れておく必要があります。

1、変数名の構成は、文字 (a-z)、数字 (0-9)、ドル記号 ($)、アンダースコア (_) のみとすることができます。
2、変数名には、空白文字(タブやスペース)を含めることができます。
3、変数名を数字で始めることはできません。
4、予約されたキーワードを変数名として使用することはできません。
5、変数名は大文字と小文字を区別します。

JavaScript の変数や関数が var や let で宣言されている場合、それらの名前はキャメルケースで付けるのが一般的です (時々 camelCase として様式化されています)。説明すると、この慣習は最初の単語を小文字にして、その後に続くすべての単語の最初の文字をスペーシングせずに大文字にすることを含みます。実際には、これはほとんどすべての非定数変数で、わずかな例外を除いて守られている慣習です。定数は const キーワードで宣言され、その名前は通常大文字で書かれます。もしあなたが始めたばかりであれば、これらの規則は学ぶことが非常に多いと感じるかもしれませんが、心配しないでください、すぐに第二の自然になるでしょう。

var、let、const の区別

前述したように、この3つのキーワードはJavaScriptの変数を定義する唯一の方法です。それらのユースケースは、以下のrableで説明したように、スコープ、ホイスト、再割り当ての3つです。

自分でプログラムを書くときに、これらの定数のうちどれを使うべきか悩んでいるならば、これを知っておくべきです: 一般的には、できる限りconstを使うのが一般的です。ループや再割り当ての場合には、ほとんどの場合 let が好まれます。ほとんどの場合、レガシーなコードを扱うときだけ var を使うことになります。

スコープを理解する

コードの現在のコンテキストを定義する際には、スコープを指標として使用します。これはJavaScriptにおける変数のアクセシビリティの指標です。スコープにはローカルとグローバルの2つのタイプがあります。

1、ブロックの外にある変数がGlobalである場合、スコープをGlobalと定義します。
2、変数がブロック内にある場合、スコープはLocalになります。
まず、以下の例でグローバル変数を作成してみましょう。

// Initialize a global variable
var creature = "fox";

変数の再割り当てが可能であることは先に見てきました。ローカルスコープを使って外部スコープに変数という名前の新しい変数を作成することも可能で、初期値を変更したり、再設定したりする必要はありません。

したがって、下の例ではグローバルな種の変数を作成しています。これをコンソールに送信してみると、変数の値がスコープに依存していることがわかります。元の値は変わりません。

// Initialize a global variable
var species = "man";

function transform() {
  // Initialize a local, function-scoped variable
  var species = "cat";
  console.log(species);
}

// Log the global and local variable
console.log(species);
transform();
console.log(species);
Output
man
cat
man

この例では、ローカル変数として関数スコープ付きの変数を見てきました。var キーワードで変数を宣言すると、関数スコープ付きの変数になる傾向があります。言い換えれば、認識する関数のスコープが異なるということです。しかし、let や const で宣言された変数はブロックスコープになる傾向があります。したがって、if 文や for ループ、while ループを使用するたびに、すべてのブロックタイプの新しいローカルスコープが作成されますが、これには関数ブロックも含まれます。

let を使用して if ブロックで関数型変数とブロックスコープ付き変数を区別してみましょう。

var fullMoon = true;

// Initialize a global variable
let species = "man";

if (fullMoon) {
  // Initialize a block-scoped variable
  let species = "cat";
  console.log(`It is a full moon. Presley is currently a ${species}.`);
}

console.log(`It is not a full moon. Presley is currently a ${species}.`);
Output
It is a full moon. Presley is currently a cat.
It is not a full moon. Presley is currently a man.

上の例では、species 変数がグローバル値 (man) とローカル値 (cat) の 2 つの値を持つことを示しました。しかし、var を使うと、以下のように異なる結果が得られます。

// Use var to initialize a variable
var species = "man";

if (fullMoon) {
  // Attempt to create a new variable in a block
  var species = "cat";
  console.log(`It is a full moon. Presley is currently a ${species}.`);
}

console.log(`It is not a full moon. Presley is currently a ${species}.`);
Output
It is a full moon. Presley is currently a cat.
It is not a full moon. Presley is currently a cat.

出力されるのは、グローバル変数とブロックスコープされた変数であるcatの両方で同じ値になります。この現象の理由は、var は新しいローカル変数を作成するのではなく、同じスコープ内で再割り当てするからです。さらに、var キーワードは if を新しいスコープとして認識しません。変数値をオーバーライドしないコードを作成するには、ブロックスコープ変数を宣言することをお勧めします。

ホイスティング

上記の例では、var を使って変数を宣言し、それを初期化するために値を使用しました。その後、宣言して初期化した変数にさらにアクセスしたり、再割り当てしたりすることができます。宣言して初期化する前に変数を使おうとすると、未定義の値が返されます。

// Attempt to use a variable before its declaration
console.log(x);

// Variable task
var x = 300;
Output
undefined

varキーワードを省略すると、変数の宣言がなくなり、初期化のみを試されます。これを使おうとすると、ReferenceErrorが発生し、スクリプトの実行に失敗します。

// Attempt to use a variable before it is declared
console.log(x);

// Variable task without var
x = 300;
Output
ReferenceError: x is not defined

これは、ホイストと呼ばれるJavaScriptの動作のために発生しました。これが何を意味するかというと、変数や関数の宣言がスコープの先頭に移動するということです。最初の例では、初期化ではなく宣言がホイストされていたので、未定義を返しています。以下、私たちが書いたコードとJavaScriptからの解釈をさらに解剖してみます。

// The code we used
console.log(x);
var x = 300;

// JavaScript interpretation
var x;
console.log(x);
x = 300;

x はスクリプトが実行される前に JavaScript によってメモリに保存されました。x は最初に定義されずに呼び出されたままなので、出力は未定義であり、300ではありません。この結果、ReferenceErrorが発生してスクリプトが停止してしまいます。これは、varキーワードがvarの場所を変更しなかったにもかかわらず、巻き上げの原理を非常によく示しています。これは、コードを書くプログラマの間で問題となる動作です。ホイストはまた、以下の例にあるように、予測不可能な結果をもたらします。

// Initialize x in the global scope
var x = 300;

function hoist() {
  // this condition should not affect the outcome of our code
  if (false) {
    var x = 400;
  }
  console.log(x);
}

hoist();
Output
undefined

当初、この例では、x はグローバルに 300 として宣言されていました。しかし、if文が適用された場合、xは200に変更される可能性があります。条件が false であったため、x の値は影響を受けないままにしておくべきでした。しかし、それが hoist() 関数の先頭に持っていかれてしまい、結果的に値として undefined になってしまいました。これが、挙動が予測できないためにプログラムにバグが発生する原因の一つです。ブロックスコープされた let と const の別のセットを使用することもできますが、これは結果として hoisting にはなりません。以下の例を参照してください。

// We initiate x in the global scope
let x = true;

function hoist() {
  // Initiate x in the function scope
  if (3 === 4) {
    let x = false;
  }
  console.log(x);
}

hoist();
Output
true

変数をletとconstで2回宣言するとエラーになりますが、varでは結果が出ます。

// Try to overwrite a variable that is declared with var
var x = 2;
var x = 3;

console.log(x);
Output
3
// Attempt to overwrite a variable declared with let
let y = 2;
let y = 3;

console.log(y);
Output
Uncaught SyntaxError: Identifier 'y' has already been declared
In summary, when we introduce variables with var, they are exposed to hoisting since JavaScript may end up saving them in the memory. The result for the behavior may be code with some undefined variables. With let and const variables, however, we can avoid the problem since it will not allow declaration of a variable twice. They also do not allow using variables before declaring them.

JavaScript の定数

ほぼすべてのプログラミング言語で定数に遭遇することがあります。定数は変更も修正もできません。定数のJavaScript表現はconst識別子で表現されます。constに代入された値を再割り当てすることはできません。一般的に、const識別子は他の変数と区別するために大文字で書かれています。変数SPECIESをconstで定数として初期化することで、この概念を探ってみましょう。

// Assign the value to const
const SPECIES = "man"; 

// Try to reassign value
SPECIES = "cat";

console.log(SPECIES);
Output
Uncaught TypeError: Assignment to constant variable.

const値の宣言と初期化を同時に行う必要があります。

// Declare the constant but do not initialize it
const DO;

console.log(DO);
Output
Uncaught SyntaxError: Missing initializer in const declaration

値が変更できない場合、それは不変と呼ばれ、変更可能なものは変異可能です。定数は、constで宣言されたオブジェクトのプロパティを変更することができるという点で、変異的です。

// Create a VEHICLE object with two properties
const VEHICLE = {
    color: "red",
    price: 25000
}

// Modify a property of VEHICLE
CAR.price = 30000;

console.log(CAR);
Output
{ color: 'red', price: 30000 }

定数を使って、プログラムのどの部分を再割り当てしてはいけないかを定義します。逆に、将来変更される可能性のある変数を宣言するには let を使用します。

最後に

このチュートリアルでは、変数の概念、変数の命名規則、変数内の値の再代入方法を探ってきました。また、このアプローチが他のプログラミング言語でどのように機能するかを比較することもできます。

Alibaba Cloudのアカウントをお持ちでない方は、アカウントを登録してください。アカウントにサインアップして、最大1200ドル相当の40以上の製品を無料でお試しください。Alibaba Cloudの利用から開始して、詳細を確認してください。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ