TypeScriptに入門したいのでJavaScriptに入門してみる(演算子)

続: JavaScript入門

教材は

https://jsprimer.net/

演算子

+ での文字列結合

const value = "hoge" + "moge";
console.log(value); // -> hogemoge

数値計算

JavaScriptでは数値は内部的には IEEE 754方式の浮動小数点数として表現される。よって整数と浮動小数点数の加減乗除も + ,- , * , / , % で実行可能。

[ES2016] ** べき乗演算子

ES2016から使えるべき乗を求める演算子 **

なお、旧来は他の言語と同じく Math.pow といったメソッドを利用していた様子。

console.log(2**4); // -> 16
console.log(Math.pow(2, 4)); // -> 16

単項 + または - 演算子

単項演算子の + はオペランドを数値に変換する(マイナスも同様) つまり文字列の "1" を 数値の 1 に変換してくれる。

let t = +"1";
console.log(t); // -> 1
console.log(typeof(t)); // -> number

数値に変換できない文字列などを単項 + 演算子で変換しようとした場合は NaN (= Not-a-Number) が返される。

console.log(+"hoge"); // -> NaN

NaN はNaN自身も含めどの値とも一致しない特性があり、 Number.isNaN メソッドで NaN の判定を行える。

console.log(NaN === NaN); // -> false
console.log(typeof NaN); // -> number
console.log(Number.isNaN(NaN)); // -> true

ただし 単項 + 演算子は文字列から数値の変換へ使うべきではない。明示的ではないため。 明示的な方法としては Number コンストラクタ関数や parseInt 関数などの方法がある。

インクリメント/デクリメント演算子

++ 演算子はCと同じく ++a もしくは a++ で利用できる。 C言語の場合は前に置くか後に置くかでその行の処理前にインクリメントするの後にインクリメントするのかが変わるが、JavaScriptも同じである。 デクリメント演算子も同様である。

比較演算子

厳密等価演算子 === / 厳密不等価演算子 !==
console.log( 1 === 1 ); // -> true
console.log( 1 === "1" ); // -> false

オブジェクト同士の比較もできる。これはオブジェクトの参照が同じ場合に true が返る。

const objA = {};
const objB = {};
console.log(objA === objB); // -> false
console.log(objA === objA); // -> true

console.log(objA !== objB); // -> true
console.log(objA !== objA); // -> false
等価演算子 == と 不等価演算子 !=

等価演算子 == や不等価演算子 != は2つのオペランドを比較する。厳密(不)等価演算子との違いは、オペランド同士が異なる型の値であった場合に同じ型となるように 暗黙的な型変換をしてから比較する 。 (なんじゃそりゃ…)

つまり、以下のように意図しない挙動になる可能性がある。

console.log(1 == "1"); // -> true, 文字列を数値に変換してから比較する
console.log(1 == "01"); // -> true, 文字列を数値に変換してから比較する
console.log(0 == false); // -> true, falseを数値に変換してから比較する
console.log(0 == null); // -> false, nullの比較はfalseを返す
console.log(null == undefined); // -> true, nullとundefinedの比較は常にtrueを返す

よって、 等価演算子、不等価演算子は基本的に使うべきではない が、例外的にに等価演算子が使われるケースとしては nullundefined の比較がある。

// 厳密等価演算子を使うと2回の比較が必要となるケース
const value = undefined;
if (value === null || value === undefined ) {
    console.log("valueがnullかundefinedの場合の処理");
}

// 等価演算子ではnullと比較するだけでよい(なぜなら nullとundefinedの比較がtrueであるため)
if (value == null) {
    console.log("valueがnullかundefinedの場合の処理");

}

ビット演算子

ビット演算子はオペランドを符号付き32bit整数に変換してから演算する。ビット演算子による演算結果は10進数の数値を返す。

// 整数の9を表す2進数
console.log(0b0000000000000000000000000001001); // -> 9
// Number#toStringメソッドでにすうすう表記の文字列を取得できる。
console.log((9).toString(2)); // -> "1001"

負の数はビッグエンディアンの2の補数形式で表現される。

// -9
console.log(0b11111111111111111111111111110111); // -> 4294967287
// ゼロ桁埋め右シフトをしてからNumber#toStringで2進数表記を取得できる
console.log((-9 >>> 0).toString(2)); // -> 11111111111111111111111111110111

論理積 & 、論理和 | 、排他的論理和 ^ 、否定 ~ は他の言語と同じ使い方ができるようなので省略。

JavaScriptは 0 も if文ではfalseとして扱われるために、否定 ~ だけ以下のような使われ方をすることがあった。

const str = "aaabaaa"
// indexOfは見つからなかった
if (str.indexOf("b") !== -1) {
    console.log("found.");
}
// 否定演算子で同じ動作を実装
// indexOfの結果、見つからなかった場合は-1、その否定なので0、0はfalseとして扱われるためif文に入らない
if (~str.indexOf("b") ) {
    console.log("found.");
}

なおES2015 からは文字列に includes メソッドが実装されたので、素直に includesメソッドを使った方がわかりやすい。

const str = "aaabaaa";
if (str.includes("b")) {
    console.log("found");
}

>> ・左 << ・ゼロ埋め右シフト演算子 >>>

右、左は他の言語と同じく numbit 文だけ右または左にシフトする。右シフトは左端のビットで埋められる。

num >> bit;

ゼロ埋め右シフト演算子は右シフトした時に左を0で埋める。

[ES2015] 分割代入

分割代入を使うことで配列やオブジェクトの値を複数の変数へ同時に代入できる。分割代入は短縮記法の一つでES2015から導入された。

const array = [1, 2];
const [a, b] = array;
console.log(a); // -> 1
console.log(b); // -> 2

オブジェクトの分割代入も可能。

const obj = {
    "key" : "value"
};
// const key = obj.key; と同義
const { key } = obj;
console.log(key)

三項演算子、論理演算子、グループ化演算子

別の言語と比べて新しい内容はなさそうなのでスキップ

カンマ演算子

const a = 1, b = 2, c = a+b;
console.log(c); // -> 3

左から順に評価して最後の式の評価結果を返す。

const a = 1, b = 2;
if(key=( ab = a === b, !ab ) ) {
    console.log(ab); // -> false
}
all_c(key)

aとbは等しくないので abにはfalseが入る。 abはfalseと等しい、かつ最後の評価結果なのでkeyにはtrueが入る。 ということになる。