JavaScript再入門 #3
2020/09/26
変数について
- let(ES6~)
- 再宣言 : ✗
- 再代入 : ○
- スコープ範囲 : ブロック
- 初期化 : ✗
- const(ES6~)
- 再宣言 : ✗
- 再代入 : ✗
- スコープ範囲 : ブロック
- 初期化 : ✗
- var(非推奨)
- 再宣言 : ○
- 再代入 : ○
- スコープ範囲 : 関数
- 初期化 : undefined
varの場合はホスティング(巻き上げ)が発生するため利用しないようにする
データ型
型 / 英名 - 利用例
- 真偽値 / Boolean - true / false
- 数値 / Number - 12
- 文字列 / String - “Hello”
- undefined / Undefined - undefined
- null / Null - null
- シンボル / Symbol - 一意の値
- Biglnt / Bigint - 12n
- オブジェクト / Object - {a: ‘value’}
暗黙的な型変換
変数が呼ばれた状況によって、変数の型が自動的に変換されること
- JS自体は動的型付け言語のため、変数を仕様する状況によって変数の型が変更される。
- また、TypeScriptやJavaの場合は静的型付け言語のため宣言時の型制限が維持される。
// 型の種類と値を表示する
function printTypeAndValue(val) {
console.log(typeof val, val);
}
let a = 0;
printTypeAndValue(a); // Number, 0
let b = '1' + a;
printTypeAndValue(b); // String, 10 => '1'のの文字列が優先され、0の数字が `1`+'0'となる
let c = 15 - b;
printTypeAndValue(c); // number, 5 => -の演算子は数値にしか利用されないため、 15から 5が引かれる
let d = c - null;
printTypeAndValue(d); // number, 5 => nullが数値の0に暗黙的に変換される
let e = d - true;
printTypeAndValue(e); // number, 4 => trueが数値の1に暗黙的に変換される
// 明示的な型変換を行う場合
let f = parseInt('10') + 10;
printTypeAndValue(f); // number, 20 => parseIntで10の文字列を数字に型変換している
厳格な等価性と抽象的な等価性
厳格な等価性の場合は**「型の比較を行う」が、抽象的な等価性の場合は「型の比較は行わない」**
- 基本的に抽象的な等価性はバグを生む原因となってしまうため、厳格な等価性を利用すること
let a = '1';
let b = 1;
console.log(a === b); // false - 数値と文字列のため型が異なる
console.log(a == b); // true - 数値と文字列だが、1で等しく判断される
let c = true;
console.log(b === c); // false - 数字の1とBoolean: trueの比較のため型が異なる
// MEMO: Boolean: 数値が変換された場合trueになり、0だとfalseで判定される
console.log(b == c); // true - Boolean(b):true === true で判断される
let e = ''; // 空文字のString
let f = 0; // 0のInt
console.log(e === f); // false - 空文字と数値の0の比較のため型が異なる
// MEMO: 空文字は数値の0と等価になるので Boolean('')とBoolean(0)で比較される
console.log(e == f); // true - Boolean(e):false === Boolean(f):false で判断される
let g = null;
let h; // undefined
console.log(g === h); // false - null型とundefined(未定義)型のため型が異なる
// MEMO: 抽象的な等価性の場合、nullとundefinedは等しく判断される
console.log(g == h); // true
FalsyとTruthy
- Falsyな値とは Booleanで真偽値に変換した場合にfalseになる値のこと
// Falsyな値
- false
- null
- 0
- 0n
- undefined
- NaN
- ""
// truthyな値
- 上記以外
// falsyな値の場合に実行したい処理がある場合
let a = "";
if (!a) {
// 注意点として0でもfalsyと判断されるため、0を含んでも問題がないかを検討すること
console.log('Hello');
}
AND条件とOR条件
- 条件文、ANDとORの動作について
// && - AND条件
const a = 1;
const b = 2;
const c = 3;
const d = 0;
// 左からtrutsyな値かどうかを確かめて、
// 1. falsyな値があった場合は、falsyな値を返却
// 2. trutsyな値の場合は、最後の値を返却する
console.log(a && b); // 2
console.log(a && b && c); // 3
console.log(a && d); // 0
// || - OR条件
const a = 1;
const b = 0;
const c = 3;
const d = null;
// 左からtrutsyな値かどうかを確かめて、
// 1. 全てfalsyな値の場合、最後の値を返却する
// 2. trutsyな値があった場合、trutsy値を返却する
console.log(a || b); // 1
console.log(a || b || c); // 1
// AND条件とOR条件を混合したものを各場合はグループ化すること
const a = 1;
const b = 2;
const c = 0;
const d = 3;
const e = 4;
console.log((a && b) || (c || d) || e);
応用した利用方法として
function hello(name) {
// 値が渡ってこなかった初期値を指定
// if(!name) {
// name = 'Tom';
// }
// 上記のコードをOR条件を利用した場合の記載方法
name = name || 'Tom';
console.log('Hello'+ name); // "Hello Tom"
}
hello();
// ES6以降は default関数が存在しているため下記の形で記載可能
function hello(name = 'Tom'){
// name = name || 'Tom'; // 記載不要
console.log('Hello' + name); // "Hello Tom"
}
// 下記のように記載すると、
let name;
name && hello(name); // nameがtrutsyな値の場合、&&以降の処理を実行する
プリミティブ型とオブジェクト
プリミティブ型
- 変数に値が格納される
- 一度作成すると、その値を変更することはできない - immutable(不変)
- letなどの変数の値を変更した場合、メモリ空間上の参照が異なる値に変わっているだけ。
オブジェクト(型)
- 変数には参照が格納される
- 値を変更する事ができる - mutable(可変)
- 参照を名前(プロパティー)付きで管理している
参照とコピー
- プリミティブ型のコピーした場合は、参照先の値がコピーされる
- オブジェクトのコピーした場合、オブジェクトへの参照がコピーされる
let a = 'hello';
let b = a; // aとbで別々の値が複製される
b = 'bye';
console.log(a, b); // hello bye
let c = {
propr: 'hello'
}
let d = c; // dからの参照がcのpropに
d.prop = 'bye';
console.log(c,d); // {prop : 'bye'} {prop : 'bye'}
参照とconst
constを利用しているとき、オブジェクトのkey: valueは変更できる
// プリミティブ型のconstについて
const a = 'Hello'; // a -> 'Hello' の参照がロックされる
a = 'bye'; // constによってロックされているため、Errorとなる
// オブジェクトのconstについて
const b = { // b -> {} への参照がconstでロックされる
prop: 'hello'; // メモリ空間上では、{} -> prop -> hello で参照される
}
b = {}; // constによってロックされているため、Errorとなる
b.prop = 'bye'; // {} -> prop -> 'hello' の参照部分はロックされていないため変更可
console.log(b); // {prop: 'bye'}
参照と引数
// プリミティブ型
let a = 0;
function fn1(arg1) {
arg1 = 1;
console.log(a, arg1); // 0, 1
}
fn1(a);
// オブジェクト(型)
let b = {
prop: 0;
}
function fn2(arg2) {
arg.prop = 1;
console.log(b, arg2); // {prop: 1}, {prop: 1}
}
fn2(b);
// オブジェクト(型)
let b = {
prop: 0;
}
function fn3(arg2) {
arg2 = {};
console.log(b, arg2); // {prop: 0}, {}
}
fn3(b);
参照と分割代入
オブジェクトから特定のプロパティを抽出して宣言する - let {a, b} = object;
- プロパティが持っている参照先がコピーされて、新しい変数が定義される
const a = {
prop: 0
}
let { prop } = a;
// TIPS: let {prop: b} = a; とすれば名称を変更できる
//
prop = 1;
console.log(a, prop); // {prop: 0}, 1
function fn(obj) {
let { prop } = obj;
prop = 1;
console.log(obj, prop): // { prop: 0}, 1
}
// 分割代入で特定の変数のみ利用するパターンの場合は下記の省略した形でもOK
function fn({ prop }) {
prop = 1;
console.log(a, prop) // { prop: 0}, 1
}
const c = {
prop1: {
prop2: 0
}
}
let { prop1 } = c;
prop1.prop2 = 1;
console.log(c, prop1) // {prop1: { prop2: 1 } }, {prop2: 1}
参照の比較と値の比較
プリミティブ型では値の比較で、オブジェクトは参照の比較 となる。
// 内部の値が同じオブジェクトを作成
const a = {
prop: 0;
}
const b = {
prop: 0;
}
console.log(a === b); // false(オブジェクト同士の比較になるため)
console.log(a.prop === b.prop); // true(オブジェクト内の数値が等しいため)
const c = a;
console.log(a === c); // true (cの参照先はaのため)
補足: 静的解析でコードを読む方法
- 動的解析 ⇒ コードを実行して解析する
- 静的解析 ⇒ コードを実行せずに読んで解析する
let obj =
{
prop1: 10,
prop2: {
prop3: 1
}
};
function fn({ prop2 }) {
let prop = prop2
prop.prop3 = 2;
prop = { prop3: 3 }; // ※3
return { prop2: prop }; // ※2
}
obj = fn(obj); // ※1
// 下記のコードでprop3に入る値を調べたい時は、
// console.log(obj.prop2.prop3);
// ※1 で fn(obj)でfnが実行されていることがわかるので…
// ※2 を見ると return で返却されるものが固定されている prop これを見る。
// ※3 prop は prop = {prop3 : 3}; で定義されているのでここの値が正しい