JavaScriptを本当に初歩から学んでみようと思います。
概念的に現段階で理解が難しそうな内容は、多少間違っている表現でもこじつけて理解しやすいように、強引な改悪翻訳をしています。その都度注釈を入れて補足しますが、間違いは温かい目で見守りつつ、コメントお願いします。
目次
そもそも「プログラム」とは?
強引に単純化すると、「データを処理するもの」ということができます。なにかしらの入力(input)に対して、必要な処理を施して出力(output)する。それがプログラムの行っていることです。そしてそれを書くのが、コーディングあるいはプログラミングと言えます。
データ「型」とは?
扱うデータには、数値や文字列の他にも、さまざまなものがありますが、その種類によって施される処理は異なります。この様々なデータの種類を 型 として区別することにより、プログラミングでは、データに必要な処理を行えるようにしています。
> "1.41421356"[2] // これは文字列型。文字の並びなので3文字目が取り出せる。
"4"
> 1.41421356 // これは数値型。全体で一つの値を示すので3文字目だけを取り出せない
undefined
// こういったコードの例にある「>」は、記述したコードであることを表す
プログラミング言語によって扱うことができるデータ型は異なります。JavaScriptで扱うことができるデータ型は大きく分けて8種類です。(厳密には他にもあります。)
データ型 | 説明 |
---|---|
Null型 | 「存在しない」オブジェクトを表す。オブジェクトが存在しないことを明示的に表すために使用する。 |
Undefined型 | 「未定義」の値を表す。初期化されていないデータはUndefinedの値を持つ。 |
真偽値型 | 条件が成り立つか、成り立たないかを表す。真偽値の値はtrueとfalseのみ。 |
数値型 | 有限の小数を表す。表現できる値は限られる。ほとんどの場合はこの型で数値を表す。 |
長整数型 | 上限値のない整数値を表す。 |
文字列型 | 文字の並びを表す。 |
シンボル型 | 同じ値を複数生成できない一意の値を表す。 |
オブジェクト型 | 下部構造を持つデータを表す。データやそのデータに対する処理を内部的に持つことができる。 |
学習の最初に使用するのは、ほとんど文字列型か数字型か真偽値型だと思いますので、別のタイミングでもう少し掘り下げます。長整数型やシンボル型は使用頻度が少ないかもしれませんが、オブジェクト型に関する理解は特に重要です。
リテラルとは?
データ型の具体的な説明に入る前に「 リテラル 」について触れておきましょう。
コンピュータはソースコード(プログラムしたコードのこと)を直接扱っているわけではありません。文字列として読み込まれたソースコードを、コンピュータは解釈して内部的な表現に変換し、その内部的な表現に従って処理を実行します。なので、あくまでソースコードは、人間がコンピュータに指示を出すための表現といえるでしょう。
コンピュータだって、人間のして欲しい事を全て汲み取って理解できるわけではありません。
強引に言えば、リテラル、あるいはリテラル値による記述とは、コンピュータに対して「この書き方した時は、こういう意味で解釈してね。」というお約束と言えるでしょう。
例) ""←これで囲んだのは、文字列として解釈してね!
null // Nullリテラル
true, false // 真偽値リテラル
1, 1n 0x1 // 数値リテラル 長整数リテラル
"React", "12" // 文字列リテラル
これ以外の値は、JavaScriptではソースコード上で値を直接的に表現できません。そのため、他のリテラルを利用して間接的に表現することになります。ただし、 [ 0, 1, 2]
のように、「[ ]」で値を囲むことで配列を作成するものを配列リテラルとも言ったりするので、他にもそういった表現(リテラル)があると認識しておいてください。
面倒かもしれませんが、実際、JavaScriptはかなり柔軟な部類です。これについては「型付け」について説明することがあれば、触れてみたいと思います。
こういった表現を書き分けることで、プログラムは人間が思ったように処理を行うことができます。
> let zipcodeString = "550-0002"; // 文字列リテラルとして解釈し、変数に代入する。
> console.log(zipcodeString);
"550-0002" // 文字列として解釈された値が出力される。
> let zipcodeNumber = 550-0002; // 数値リテラルとして解釈し、変数に代入する。
> console.log(zipcodeNumber);
448 // 550引く2とい数値計算として解釈され、その演算結果が出力される。
変数と定数
変数は、値が変わっても平気で、定数は値が変わると怒られる。ざっくり言えばそんな違いの両者ですが、もう少し踏み込んで、考えてみましょう。
定数
まずは定数から説明します。
例として、円の面積計算の公式で考えてみましょう。概算でよいとして、円周率を3にしてみましょう。
先ほど説明したリテラルを覚えれば、ひとまずプログラムに値を記述して計算できそうです。
const 定数名 = 値;
とすると定数を宣言し、値を代入できます。
> const circleArea = 3 * 5 * 5; // 円周率x半径5x半径5の計算結果を代入しています。
> console.log(circleArea);
75
良さそうです。ですが、やはり円周率を「3.14」に変更しましょう。
計算しているのが1か所なら、リテラルの部分を書き換えれば求める処理に変更できそうです。
ですが、仮にコードの中で100か所で円の面積を計算していたら、100か所の「3」を訂正する必要があるでしょう。
コードの中に「3」が、円周率以外にもあったとして、その「3」が円周率の「3」であるのか、「3」分の「3」なのか、一つ一つ見るのは非効率で大変です。バグの原因にもなりかねません。
このような時に使用するのが、 定数 です。
定数を利用すると、値に名前(識別子)を付けて、その名前で値を利用できるようになります。
> const pi = 3.14; // 定数piを宣言し、数字型の3.14を代入。
> const circleArea = pi * 5 * 5; // このpiには、数値型の3.14が代入されています。
このように、定数として値に名前を付けると良いことがあります。
- ソースコードで、piという名前で値を使いまわすことができる。
- もしも、piの値を変更したいときでも、
const pi = 3
と書き換えれば、他を訂正せずに済む。 - コードを見て「これは円の面積の計算である」ということが明確になる。
- 円周率piが他の値に上書きされたり、変更されなくなり安全になる。
といった利点があります。
特にプログラミングにおいては、ソースコード中にリテラルを直接書き込んで演算するよりも、定数や変数同士で演算することの方が圧倒的に多く、また変更などにも強い良い書き方とされています。
ちなみにソースコード中に直接リテラルとして書かれた値をマジックナンバーといいます。
変数
次に変数について説明します。
ざっくり言えば、「定数のように値に名前を付けれて使いまわせる。それでいて値を上書きできるもの。」です。
定数は一度宣言すると値が上書きできませんが、変数は何度も(実際に同じ名前の変数を何度も上書きして使いまwすことはしませんが)代入して値を上書きすることができます。
> let hensu = 'これは'; // 変数memoryを宣言し、文字列を代入。
> hensu = '上書きできる'; // 変数へ再代入。
じゃあ全部変数にした方が便利じゃん。間違って上書きしても怒られないし。
そう思っていた時期が私にもありました。
実際に、JavaScriptの仕様では変数のほうが先に存在し、定数は後から追加されました。上書きができない一見不便なものが後から追加されたのには、理由があります。
それは、 「値を書き換えることができない」 という性質が、変数にない利点だからです。
例えば、定数の説明の際に宣言した円周率piが、別の場所で意図せず勝手に書き換えられたらどうでしょうか。コードの中のpiを利用したすべての計算が本来意図しない計算結果を出すようになってしまいます。ですが、定数としてそれを宣言しておくと、意図しない変更や上書きはされずに安全に取り扱うことができます。
両者の違いについて、改めて整理すると、
- 変数は
let
識別子により宣言し、定数はconst
識別子により宣言します。 - どちらも同じスコープで、再宣言するとエラーになります。
- 変数は値を何度も代入することができ、定数は一度代入するとその値は変更できません。
こういった違いを理解すると、両者のメリットに応じた使い分けができるようになります。
具体的に説明しましょう。例えば、円の面積の公式では、
$円の面積 = 円周率 \times (半径)^2$
それぞれ数値が入るべきところに「円周率」と「半径」という名前がついていますが、それぞれには明確な違いがあります。
円周率は固定の値であり、どんな計算になってもその値が変わることがありません。いわば、 具体的な値に名前がついているもの と言えます。
一方、半径は演算するその時々により5cmだったり、6378.1kmだったりと、値は変わることがありますが、その値が果す役割は変わりません。いわば 役割に名前がついているもの なのです。
(より専門的に言えば、抽象度が違うということになります。)
もう少し例を出してみます。
> const TAX_RATE = 0.1; // 消費税はその値自体、(上がる一方ですが)固定的なものです。
> let egg_price = 284; // タマゴの値段も高くなりました。けどまた戻るかもしれません。
> console.log( egg_price + egg_price * TAX_RATE );
312.4
> egg_price = 250; // 値下がりしました。
> console.log( egg_price + egg_price * TAX_RATE);
275
上のように、値自体が意味を持つものは定数。役割、あるいは今はその値だけで変更が予想されるものは変数といったように使い分けることで安全で、修正しやすいプログラミングを行うことができます。また、基本的には定数を使用し、変数である必要があるときに変数を使用する。というのも最初はおすすめです。
実際には、これに スコープも関係してきますが、始めはそんな認識で十分かと思います。
代入、定義、初期化、識別子
ここまで、あえてそのまま使用してきたこれらの言葉の意味を整理し、この記事をまとめます。
識別子
識別子とは、今まで説明してきた変数や定数、また関数やクラスなどに対して名前を付けるための文字列のことです。これは開発者の自由につけれるものですが、使用できる識別子には規則があります。
- 識別子の1文字目はUnicode文字、「$」、「_」である必要があります。ただし、「_」などには別の意味が含まれることもあるので、ひとまず文字列を使えば問題ないです。
- 2文字目以降は、1文字目に使用できたものに加え、数字も利用できます。
- 予約語 は使用できません。(予約語については後述します。)
以上の規則を守れば、自由につけれることもできます。(これ以外にも利用シーンによってはルールや規則があります。)
> const hensu1 = '変数1';
undefined
> const case = 'ケース'; // caseは、予約語のため識別子として使用できない。
Uncaught SyntaxError: Unexpected token 'case' // エラー
// トークン"case"が予期せぬ場所に登場しました。 というエラー
トークンとは、プログラミング言語にとって意味のある単語のことで、先ほど説明に出た予約語と同じ意味です。現在トークンとして使用している単語や将来使用される可能性がある単語は識別子として使用することができません。let
や const
なんかもトークンです。
宣言、代入、初期化
宣言と代入とは、上の説明で、変数の利用した時のように、宣言によって役割を用意し、代入によってその役割を果す具体的な値を設定することです。変数も定数も宣言は一度しか行えませんが、変数なら代入は何度もでき、定数は一度しか代入することができません。
初期化とは、初期化子を用いて、宣言時に値を代入することです。
> let hoge = "初期化子"; //=の後にあるのが、初期化子。
//上のコードは、「変数hogeを宣言し、”初期化子”という値を代入し初期化している」という意味になります。
> hoge;
"初期化子"
> let hogehoge; // 変数宣言時の初期化子は省略できる。定数では省略できません。
undefined
> hogehoge = "ここで代入"; //後から代入することもできる。
ちなみに「=」のことを、 代入演算子 といいます。
プログラミング言語には他に○○演算子というものがいくつかありますが、最初は「意味を持った記号」くらいの認識でOKです。
まとめ
かなり適当な感じで説明をしてきましたが、実際にすべてを理解しながら進めることは難しいので、最初はこのくらいの認識でコードを見た時に理解ができれば良いかと思います。用語も使いながら覚えて行ければ良いです。
プログラミング=記憶ではありませんが、基本的で応用の効く用語や仕様についてしっかり覚えておくことで、今後の学習もしやすいかと思います。
次回は「プリミティブとオブジェクト」についての記事にする予定です。
もう少しイラストなんかも使用できればと思いますので、良ければご覧ください。