値を入れて使う箱のようなもの。値を直接書いていくこともできるが、1つの処理しかできなくなる。変数を使うことによって値の変更が可能なプログラムの処理として使いまわせるようになる。
変数宣言と代入
let、const、varの違い
let、constはES6から使用できるようになった変数宣言を行うためのキーワード。
それ以前はvarを使っていた。
宣言による機能の違い
タイプ | 再宣言 | 再代入 | スコープ | 初期化 |
let | × | ○ | ブロック | × |
const | × | × | ブロック | × |
var(非推奨なので使わない) | ○ | ○ | 関数 | undefined |
再宣言とは、もう一度変数として宣言すること。
let a = 0;
let a = 1; //エラー
var b = 2;
var b = 3; //再宣言可能
再代入は一度設定した値を変更できるかどうか。
let a = 0;
a = 1;
const b = 2;
b = 3; //エラー
スコープに関してはスコープの記事を参照。
変数とデータ型
データ型
データ型とは変数が持っているデータの種類のこと。
型 | 英名 | 例 |
真偽値 | Boolean | true / false |
数値 | Number | 12 |
文字列 | String | “Hello” |
undefined | undefined | undefined |
null | null | null |
シンボル | Symbol | 一意の値 |
BigInt | BigInt | 12n(桁が大きい場合) 対応していないブラウザを必要がある |
オブジェクト | Object | {a: ‘value’} |
暗黙的な型変換
変数が呼ばれた状況にいよって変数の方が自動的に変換されること。
例)文字列として扱った変数が状況より数値として取り扱われる。
型を確認するためには、typeofという演算子を利用する。
function printType(val){
console.log(typeof val, val);
}
let a = 0;
printType(a); //number 0
let b = '1' + a;
printType(b); //String 10
let c = 15 - b;
printType(c); //number 5
let d = c - null;
printType(d); //number 5
let e = d - true;
printType(e); //number 4
変数aは数値のため、「number 0」が返ってくる。
変数bは文字列と数値のため文字列に合わせる形となり「String 10」が返ってくる。なお、parseIntという関数を呼ぶことによって、数値に変換することも可能。
変数cはマイナスが数値の計算にしか使われないため、文字列が数値に変換された。
変数dはnull(=空)のため、cから引いても数値の5が返される。
変数eはtrue(=1)のため、数値変換され、5-1になり4が返される。
int a = 0; //整数を宣言
厳格な等価性と抽象的な等価性
厳格な等価性
型の比較まで行う。(型変換は行わない)
let a = '1';
let b = 1;
console.log(a === b) //false
抽象的な等価性
型の比較は行わない。抽象的な等価性は暗黙的に型変換が自動的に行われるため型が違ってもtrueになる。
let a = '1';
let b = 1;
console.log(a == b) //true
falsyとtruthy
falsyとはBooleanで真偽値に変換した場合にfalseになる場合に「falsyな値」と言う。
false、null、0、undefind、On、NaN、空文字がfalsyな値として出力される。
AND条件とOR条件
AND条件
const a = 0;
const b = 1;
console.log(a && b);
AND条件の場合、aがtruthyかどうかを確認。falsyの場合はaの値を条件式の結果として返却。truthyの場合は、bの値を結果として返却する。
この場合、0はfalsyのため、aの値を結果として返却している。
OR条件
const a = 0;
const b = 1;
console.log(a || b);
OR条件の場合、AND条件と同様aがtruethyかどうかを確認。falsyの場合は、bの値を取りに行く。式の間でtruethyが見つかったところで止まる。
ANDとOR条件が混在している場合
ANDとOR条件が混在している場合、どのように処理されているのかわかりにくくなるため、グループ化を行う。
const a = 0;
const b = 1;
const c = 2;
const d = 0;
console.log((a || b) && (c || d));
上記コードのようにAND条件とOR条件が明確になるため、処理がわかりやすくなる。
AND条件とOR条件の応用編
ES6でデフォルト引数が使えるようになったためこの書き方がベストではないが、型として理解しておくと他者の記述で気付きやすい。
OR条件を使った応用
function hello(name){
name = name || 'Tom';
console.log('Hello!' + name);
}
hello('Bob');
関数を作成した際に、nameの値が空の場合、OR条件を使うと「nameが空の場合、Tomを表示する」ということ条件式で初期化を簡略化できる。
ES6からはデフォルト引数というのが引数に設けられるため下記のような書き方も同じ挙動になる。
function hello(name = 'Tom'){
console.log('Hello!' + name);
}
hello('Bob');
AND条件を使った応用
function hello(name){
console.log('Hello!' + name);
}
let name = 'Bob';
if(name){
hello(name);
}
上記のようなコードを簡略化して書く方法として、AND条件を利用する。
もし、nameが空でfalthyな場合値を返さない。
function hello(name){
console.log('Hello!' + name);
}
let name = 'Bob';
name && hello(name);
プリミティブ型とオブジェクト
プリミティブ型
変数には値(データ型)が格納される
一度作成するとその値を変更することはできない(immutable)
ただし、letで再代入できるが厳密には読み込む値が切り替わるだけで元の値を上書きしているわけではない。
オブジェクト
変数には参照が格納される。
値を変更することができる(mutable)
参照とコピー
プリミティブ型のコピーとオブジェクトのコピーの挙動に関して全く別の形となるので注意が必要。
プリミティブ型のコピー
let a = 'Hello';
let b = a;
b = 'Bye';
変数aを変数bに代入すると、メモリ空間にコピーされる。
更に変数bにbyeを再代入するとbの参照先が「Hello」から「Bye」に変更される。
変数bは変数aの値自体もコピーされる。そのため、変数bの値を再代入したとしても、変数aに影響はない。(独立している)
オブジェクトのコピー
let c = {
prop: 'Hello'
}
let d = c;
b.prop = 'Bye';
console.log(c); //Bye
console.log(d); //Bye
変数cを変数dに代入するとメモリ空間に、オブジェクトがコピーされるが、実態に関してはaを参照することになる(この場合はprop: ‘Hello’)。
そのため、参照元のオブジェクト(この場合は変数c)にも影響があり、コピーしたdの中のpropというプロパティにByeを代入すると、参照元はaのため、cもdと同様「Bye」と出力される。
では、変数dに対して新しいオブジェクトを定義するとどうなるか・・・
let c = {
prop: 'Hello'
}
let d = c;
b.prop = 'Bye';
d = {};
console.log(c); //Bye
console.log(d); //{}
cに定義されたオブジェクトはそのまま(bye)でdはオブジェクトが新たに定義されるようになる。コピーしただけだと、コピー元の中を参照するがオブジェクトの中を変更すると、別のオブジェクトへの参照となる。
参照とconst
プリミティブ値の場合
const a = 'Hello';
a = 'bye'; //エラー
この場合は、値がロックされるため再代入しようとするとエラーが返される。
オブジェクトの再代入の場合
const b = {
prop: 'Hello'
}
b = {} //エラー
b.prop = 'Bye'
console.log(b); //{prop: 'Bye'}
オブジェクト自体を再代入するとロックされているため、エラーが出る。
bのプロパティに変更を加えるとエラーを発生せずにプロパティが変更されたオブジェクトが生成される。
constをオブジェクトで使った場合には、オブジェクトの再代入はできないがオブジェクト内のプロパティは変更できる。
参照と分割代入
分割代入
let{a, b} = object;
配列の要素やオブジェクトのプロパティの値を抽出して宣言を行うことを分割代入という。配列リテラルやオブジェクトリテラルに似た書き方を左辺に書くことで右辺の配列やオブジェクトから対応する値を取り出せる。
const arr = [10, 20, 30, 40, 50];
const[a, b, c] = arr;
console.log(a, b, c); //10,20,30
左辺に「[]」を書くことで右辺の配列から変数に代入できる。
要素を飛ばしたい時は、変数を書かずに「,」を書く。残りの変数の値を全て配列で得たいときは、末尾に「…」と書いた後に変数を書く
const arr = [10, 20, 30, 40, 50];
const[a, , b, ...c] = arr;
console.log(a, b, c); //10, 30, [40, 50]
配列の要素がなく、undefinedが返ってきた場合規定値を、「=」で設定することも可能。下記のようにaの値が入っている場合は初期値が優先される。
const arr = [10];
const[a = 20, b = 30, c = 40] = arr;
console.log(a, b, c); //10,30,40
参照と分割代入
const a = {A: 10, B: 20, C: 30};
const {A, B, C} = a;
console.log(A, B, C); //10,20,30
基本の型は上記だが、下記のように変数名を変えて受け取ることも可能
const a = {A: 10, B: 20, C: 30};
const {A:num1, B:num2, C:num3} = a;
console.log(num1, num2, num3); //10,20,30
配列と同様、残りのプロパティを全て取得する場合は末尾に「…」と書いた後に変数を書く。
const a = {A: 10, B: 20, C: 30};
const {A, ...num} = a;
console.log(A, num); //10,{B: 20, C: 30}
配列と同様、プロパティがなく、undefinedが返ってきたときの規定値を「=」で設定することも可能。
const a = {A: 10, B: 20, C: 30};
const {A = 100, D = 400} = a;
console.log(A, D); //10, 400
コメント