6時だョ!!全員集合!!

Railsを中心にアウトプットします。

2021年10月14日 JavaScript (JavaScript Primer) 演算子

前回の記事の続きです。

ビット演算子

ビット演算子では、オペランドである数値を符号付き32ビット整数(0と1からなる32個のビットの集合)として扱います。
たとえば、1という数値は符号付き32ビット整数のビットでは、00000000000000000000000000000001 として表現されます。 わかりやすく4ビットごとに区切ると 0000_0000_0000_0000_0000_0000_0000_0001 のような32ビットの集合となります。 符号付き32ビット整数では、先頭の最上位ビット(一番左のビット)は符号を表し、0の場合は正の値、1の場合は負の値であることを示しています。

2の補数の求め方

JavaScript Primerの2の補数の説明を引用します。

符号付き32ビット整数では負の数値は、2の補数形式という形式で表現されます。2の補数とは、それぞれのビットを反転して1ビットを足した値となります。

2の補数の求め方の手順
1. 各ビットを反転させる
2. 1ビットプラスにする

ビット論理積

ビット論理積演算子(&)はビットごとのAND演算した結果を返します。 AND演算では、オペランドの各ビットがどちらも1の場合は1となり、それ以外の場合は0となります。 次のコードでは、10進数の15と9をAND演算しています。 15は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_1111となります。 9は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_1001となります。 これらをAND演算した結果は0000_0000_0000_0000_0000_0000_0000_1001となり、10進数の値である9を返します。

console.log(15 & 9); // => 9
// 同じ位の各ビット同士をAND演算する(上位の`0`は省略)
// 1111
// 1001
// ----
// 1001
console.log(0b1111 & 0b1001); // => 0b1001

AND演算子(&&)

AND演算子(&&)は、左辺の値の評価結果がtrueならば、右辺の評価結果を返します。 一方で、左辺の値の評価結果がfalseならば、そのまま左辺の値を返します。

Nullish coalescing演算子(??)

Nullish coalescing演算子(??)は、左辺の値がnullishであるならば、右辺の評価結果を返します。 nullishとは、評価結果がnullまたはundefinedとなる値のことです。

例えば、以下のようなコードの場合

obj.x = obj.x ?? 42;
  • obj.xがnullまたは、undefindの場合のみ、右辺の値が代入されます。
  • obj.xがnullまたは、undefind以外の場合、左辺の値が代入されます。

OR演算子( || )やAND演算子( && )がtrueかfalseを判断するのに対して、Nullish coalescing演算子(??)はnullまたはundefinedかを判断しています。そのため、OR演算子( || )やAND演算子( && )を使うとnullまたはundefinedかを判断しないので、注意が必要です。

|| (OR演算子)との違いは、OR演算子は左辺が false として判定される場合は右辺が返るのに対し、 ?? では null または undefined に限定されること。

// 左辺がnullishであるため、右辺の値の評価結果を返す
console.log(null ?? "右辺の値"); // => "右辺の値"
console.log(undefined ?? "右辺の値"); // => "右辺の値"
// 左辺がnullishではないため、左辺の値の評価結果を返す
console.log(true ?? "右辺の値"); // => true
console.log(false ?? "右辺の値"); // => false
console.log(0 ?? "右辺の値"); // => 0
console.log("文字列" ?? "右辺の値"); // => "文字列"

条件演算子三項演算子

三項をとる演算子であることから、三項演算子とも呼ばれます。
一つ目の式の処理結果に応じて実行する式が決まります。

条件式 ? Trueのとき処理する式 : Falseのとき処理する式;

if文との違いは、条件演算子は式として書くことができるため値を返します。

const valueA = true ? "A" : "B";
console.log(valueA); // => "A"

暗黙的な型変換とは

暗黙的な型変換とは次のことを言います。
ある処理において、その処理過程で行われる明示的ではない型変換のこと。

暗黙的な型変換では、ある処理の過程でオペランドの型によって、自動型変換が行われます。そのため、思ってもみない型に変換されてしまう危険性があります。
以下に例を記載します。

1 + "2"; // => "12"
// 演算過程で次のように暗黙的な型変換が行われる
// この場合+演算子を使うと文字列結合を優先する仕様となっているため、数値の1を文字列の1に変換してから計算をする
"1" + "2"; // => "12"

この場合、数値の1を文字列型に変換するつもりがなくても自動で型変換されてしまうのが暗黙的な型変換なのです。
なるべく明示的な型変換を使うように心がけましょう。

等価演算子と暗黙的な型変換の関係

等価演算子では、必ず暗黙的な型変換をしてから、右辺と左辺を比較します。暗黙的な型変換をするので、予期せぬ挙動が発生する可能性があります。そのため、等価演算子ではなく、厳密等価演算子を使うべきです。

( 等価演算子による予想できない型変換の種類は比較する値と型の組み合わせの数だけあるため、予測が困難です。)

3つ以上の値と暗黙的な型変換の関係

3つ以上の値を取り扱う場合、演算結果の型を予測するのが困難になります。以下のコードの場合、オペランドの順番によって、演算結果が変わります。

const x = 1, y = "2", z = 3;

// +演算子は文字列結合を優先する。
console.log(x + y + z); // => "123"
console.log(y + x + z); // => "213"
console.log(x + z + y); // => "42"

// 文字列に対する-演算子の定義はない。
// x + yは文字列だが、数値への型変換が実行される。
console.log(x + y - z); // => 9
console.log(typeof(x + y - z) ); // => number

暗黙的な型変換では、結果の値の型がオペランドの型に依存しているため、
意図せぬ挙動に繋がったり、思わぬバグが生じる原因となります。
その対策として、次回は明示的な型変換についてまとめていきたいと思います。

参考

JavaScript Primer