論理演算子
GASの入門書などを見ると論理演算子はifの中に使用することでその値を判定させるという書き方をされているのをよく目にする気がします。
ですが、バイブコーディングを行っている中でifを使わず(ifの外側)に論理演算子を使用しているのを見て、ifを使うのはあくまで例であって、論理演算子の本当の挙動はどういうものなのか?を確認しましたのでまだまだGAS入門者の私用に備忘録代わりに記事を作成します。
誤っておりましたら優しく教えてもらえたら・・・助かります^^;
使用可能な論理演算子
2026年1月現在でGAS V8ランタイムで使用可能な論理演算子は下記となります。
| 演算子 | 名称 |
|---|---|
| && | 論理積演算子(Logical AND Operator) |
| II | 論理和演算子(Logical OR Operator) |
| ?? | ヌル値合体演算子(Nullish Coalescing Assignment Operator) |
| ! | 論理否定演算子(Logical NOT Operator) |
また、論理演算子の左右をオペランド(被演算子)と表現するそうです。
論理和演算子はwindowsであればShiftを押しながらBack spaceの左側キーを押すと入力されます。
&& 論理積演算子(Logical AND Operator)
ifを使った場合の説明では全てのオペランドがtrueの場合に・・・と説明がされますが、論理積演算子本来の動作は
左から順に真偽判定を行い、どこかで左側オペランドが偽(false)になるとその値を返し、左側全てのオペランドが真(true)の場合のみ右端オペランドを返す。
そうです。
全てがtrueならばtrueを返すという意味合いとしては同じようですが、真偽判定をするのではなく右端オペランドを返すというのが私の心にヒットしました。
左から右に向かってfalseが出るまで進み、falseが出たとこで止まりその値が返されますが、出なければ右端の値が返される
と言い換えられるかもしれません。
具体的なコードとその結果を見たいと思います。
aには3を代入し、bには-1を代入します。
aとbが0より大きいのか?小さいのか?を判定させる簡単なコードです。
function test() {
const a = 3;
const b = -1;
console.log("1.&& 全てtrue判定:",a > 0 && b < 0); //true
console.log("2.&& a:false判定:",a < 0 && b < 0); //false
console.log("3.&& b:false判定:",a > 0 && b > 0); //false
console.log("4.&& 全てfalse判定:",a < 0 && b > 0); //false
console.log("5.&& b:false判定:",a > 0 && b > 0 && 'default'); //false
console.log("6.&& true判定:",a > 0 && b < 0 && 'default'); //default
console.log("7.&& null判定:",c && 'default'); //null
console.log("8.&& undefined判定:",d && 'default'); //undefined
}
こちらを実行させた結果です。
9:42:20 お知らせ 実行開始
9:42:21 情報 1.&& 全てtrue判定: true
9:42:21 情報 2.&& a:false判定: false
9:42:21 情報 3.&& b:false判定: false
9:42:21 情報 4.&& 全てfalse判定: false
9:42:21 情報 5.&& b:false判定: false
9:42:21 情報 6.&& true判定: default
9:42:21 情報 7.&& null判定: null
9:42:21 情報 8.&& undefined判定: undefined
9:42:21 お知らせ 実行完了
1~4について説明します。
- a > 0:trueですので右オペランドに進み、b < 0:trueとなりましたので、右端オペランドのtrueが返されています。
- a < 0:falseですので、falseが返されています。
- a > 0:trueですので右オペランドに進み、b > 0:falseとなりましたので、falseが返されています。
- a < 0:falseですので、falseが返されています。
ここで、2.と4.を見ると同じ判定条件でfalseが返されていることに気づくと思いますが、論理積演算子本来の動作を見返してもらうと理解いただけるかと思います。
本来の動作の左側からというのがポイントで、3.を見ると左側オペランドがtrueでしたので、右側オペランドに進んでいることがわかります。
続いて、5、6について説明します。
5. a > 0:trueですので右オペランドに進み、b > 0:falseとなりましたので、falseが返されています。
次の6.がポイントで、
6. a > 0:trueですので右オペランドに進み、b < 0:trueですのでさらに右に進んだところ、右端の'default'が返されていることがわかります。
左から順に真偽判定を行い、どこかで左側オペランドが偽(false)になるとその値を返し、左側全てのオペランドが真(true)の場合のみ右端オペランドを返す。
こちらの動作を6.で確認できたかと思います。
つまり、真偽判定で使用することも出来るのですが、全ての真偽判定が真であった場合に、真ではなく別の値を返すという動作を行うことも出来ます。
ifと論理演算子を使った説明では真偽判定しかわからないのですが、論理演算子本来を動作を考えると色々な使い方が出来そうですね。
因みに、7.と8.で例を上げたnullとundefinedの場合はfalseですので、それぞれの値が返されています。
|| 論理和演算子(Logical OR Operator)
論理和演算子はwindowsであればShiftを押しながらBack spaceの左側キーを押すと入力されます。
ifを使った場合の説明ではいずれかのオペランドがtrueの場合に・・・と説明がされますが、|| 論理和演算子(Logical OR Operator)本来の動作は、
左から順に真偽判定を行い、真(true)になるとその値を返し、偽(false)になるとその右側のオペランドを返す。
そうです。
いずれかがtrueならばtrueを返すという意味ですが、論理積演算子と同様に右端のオペランドを返す事も出来ます。
左から右に向かってtrueが出るまで進み、trueが出たとこで止まりその値が返されますが、出なければ右端の値が返される
と言い換えられるかもしれません。
function test() {
const a = 3;
const b = -1;
const c = null;
const d = undefined;
const e = ""
const f = 0;
const g = -0;
console.log("1.|| true判定:",a > 0 || b < 0); //true
console.log("2.|| a:false判定:",a < 0 || b < 0); //true
console.log("3.|| b:false判定:",a > 0 || b > 0); //true
console.log("4.|| a,b:false判定:",a < 0 || b > 0); //false
console.log("5.|| c:null,b:true判定:",c || b < 0); //true
console.log("6.|| c:null,b:false判定:",c || b > 0 || 'default'); //default
console.log("7.|| d:undefined,b:true判定:",d || b < 0); //true
console.log("8.|| d:undefined,b:false判定:",d || b > 0 || 'default'); //default
console.log("9.|| e:空文字,b:false判定:",e || b > 0 || 'blank'); //blank
console.log("10.|| f:0,b:false判定:",f || b > 0 || 'zero'); //zero
console.log("11.|| g:-0,b:false判定:",g || b > 0 || '-zero'); //-zero
}
こちらを実行させた結果です。
12:04:04 お知らせ 実行開始
12:04:05 情報 1.|| true判定: true
12:04:05 情報 2.|| a:false判定: true
12:04:05 情報 3.|| b:false判定: true
12:04:05 情報 4.|| a,b:false判定: false
12:04:05 情報 5.|| c:null,b:true判定: true
12:04:05 情報 6.|| c:null,b:false判定: default
12:04:05 情報 7.|| d:undefined,b:true判定: true
12:04:05 情報 8.|| d:undefined,b:false判定: default
12:04:05 情報 9.|| e:空文字,b:false判定: blank
12:04:05 情報 10.|| f:0,b:false判定: zero
12:04:05 情報 11.|| g:-0,b:false判定: -zero
12:04:05 お知らせ 実行完了
1~4について説明します。
- a > 0:trueですのでtrueが返されています。
- a < 0:falseですので右オペランドに進み、b < 0:trueですのでtrueが返されています。
- a > 0:trueですのでtrueが返されています。
- a < 0:falseですので右オペランドに進み、b > 0:falseですのでfalseが返されています。
1.と3.は同じ判定条件でtrueが返されていることに気づくと思いますが、論理和演算子本来の動作を見返してもらうと理解いただけるかと思います。
本来の動作の左側からというのがポイントで、2.を見ると左側オペランドがfalseでしたので、右側オペランドに進んでいることがわかります。
ここまでがif内で使用した場合の論理和演算子の動作説明ですが、先ほどと同様にif外で使用することが出来ます。
なお、論理演算子は以下の条件でfalseと判定(falsy)します。
- false
- null
- undefined
- "":空文字
- 0
- -0
- NaN
5.と6.を見ていきます。
変数cはnullと返された場合を想定して定義しておりますのでfalseと判定されます。
5. c:falseですので右のオペランドに進み、b < 0:trueですのでこの値が返されています。
6. c:falseですので右のオペランドに進み、b > 0:falseですので更に右のオペランドに進み、'default'が返されています。
7.と8.についてもundefinedはfalseと判定されますので同様な動作となります。
9.と10.と11.についてもいずれもfalseと判定されますので同様な動作となります。
5.~10.を見るとif内で使った場合とかなり異なる動きをしていると感じられるかと思います。
論理和演算子を使用することのメリットですが、コードの短縮化が挙げられるようです。
例えば・・・実行ログを保存するシート名をスクリプトプロパティに保存する場合を考えます。
プロパティ名:LOG_SHEET_NAME
シート名:保存したいシート
としてスクリプトプロパティに保存しておくのですが、それがされていなかった場合でもデフォルト値を設定することが出来ます。
const logSheetName = properties.getProperty('LOG_SHEET_NAME') || '実行ログ';
論理和演算子を使用しておりますので、properties.getProperty('LOG_SHEET_NAME')で得られるシート名が定義されていない場合、undefinedとなりますのでfalse判定となり右のオペランドの値が採用されます。
このため、スクリプトプロパティで指定していない場合でも'実行ログ'というシートをログシート名のデフォルト値に指定することが出来ます。
これをif関数を使って行おうとしたらコードが長くなってしまうのですが、論理和演算子を使うことでかなりの短縮を行うことが出来ます。
?? ヌル値合体演算子
演算子はifの外側で使用することも出来てコードの短縮化が行えます。
一方、そのfalse判定条件(falsy)に0等が含まれていることで意図しない動作を防ぐ対策を行う必要があります。
そこでGAS V8ランタイムから使用可能になったのがヌル値合体演算子です。
こちらのfalsyは
- null
- undefined
です。
このことから0はtrueと判定されることになります。
ヌル値合体演算子の動作につきましては、
左側オペランドがfalsyの場合は右側オペランドを返します。そうでない場合は左側オペランドを返します。
function test5() {
const a = 3;
const b = -1;
const c = null;
const d = undefined;
const e = ""
const f = 0;
const g = -0;
console.log("1.?? true判定:",a > 0 ?? b < 0); //true
console.log("2.?? a:false判定:",a < 0 ?? b < 0); //false
console.log("3.?? b:false判定:",a > 0 ?? b > 0); //true
console.log("4.?? a,b:false判定:",a < 0 ?? b > 0); //false
console.log("5.?? c:null,b:true判定:",c ?? b < 0); //true
console.log("6.?? c:null,b:false判定:",c ?? b > 0 ?? 'default'); //false
console.log("7.?? d:undefined,b:true判定:",d ?? b < 0); //true
console.log("8.?? d:undefined,b:false判定:",d ?? b > 0 ?? 'default'); //false
console.log("9.?? e:空文字,b:false判定:",e ?? b > 0 ?? 'blank'); //空文字
console.log("10.?? f:0,b:false判定:",f ?? b > 0 ?? 'zero'); //0
console.log("11.?? g:-0,b:false判定:",g ?? b > 0 ?? '-zero'); //0
}
こちらを実行させた結果です。
14:49:02 お知らせ 実行開始
14:49:03 情報 1.?? true判定: true
14:49:03 情報 2.?? a:false判定: false
14:49:03 情報 3.?? b:false判定: true
14:49:03 情報 4.?? a,b:false判定: false
14:49:03 情報 5.?? c:null,b:true判定: true
14:49:03 情報 6.?? c:null,b:false判定: false
14:49:03 情報 7.?? d:undefined,b:true判定: true
14:49:03 情報 8.?? d:undefined,b:false判定: false
14:49:03 情報 9.?? e:空文字,b:false判定:
14:49:03 情報 10.?? f:0,b:false判定: 0
14:49:03 情報 11.?? g:-0,b:false判定: 0
14:49:02 お知らせ 実行完了
1.~4.の結果を見るとヌル値合体演算子が判定できるのは左側オペランドの判定だけですので、a > 0:trueの1.と3.でtrueが返されています。
つまりこのような使い方はしない演算子ということになります。
5.と6.の結果を見ていきます。
5.は左側オペランドがnullですのでfalse判定となり右側のオペランドが返されています。このとき、b < 0:trueですのでtrueが返されています。
6.は5.と同様に右側のオペランドが返されていますが、b > 0:falseですのでfalseが返されていまして、さらにその右側につきましては無視されています。
このように連結はできない演算子ということになります。
7.と8.につきましても5.と6.と同様な結果となります。
9.につきましては空文字はtrueですので左側オペランドが返されることから空文字が返されています。
10.と11.につきましても0はtrueですので左側オペランドが返されることから0が返されています。
なお、0の前のマイナスは返されない仕様のようですね。
いずれも連結させても最初のヌル値合体演算子左右オペランドについてしか処理されない演算子となっております。
一例ですが、下記のコードの使い方が出来るかと思います。
function test() {
let user = null;
let name = user ?? 'ゲスト'; //ゲスト
console.log(name);
let user2 = 'Alice';
let name2 = user2 ?? 'ゲスト'; //Alice
console.log(name2);
let user3 = 0;
let name3 = user3 ?? 'ゲスト'; //0
console.log(name3);
}
こちらを実行させた結果です。
15:23:46 お知らせ 実行開始
15:23:46 情報 ゲスト
15:23:46 情報 Alice
15:23:46 情報 0
15:23:46 お知らせ 実行完了
userという関数に値が入っていれば顧客の名前が表示され、入っていなければゲストとして表示されるという使い方も可能です。
また、論理和演算子と異なり0はtrueとなりますので、0という顧客はその名前が正しく表示されるようになります。
! 論理否定演算子
論理否定演算子はその判定を否定(覆す)演算子となります。
つまり、trueならばfalse、falseならばtrueという結果を返します。
function test() {
const a = 3;
const b = -1;
console.log("1.&& 全てtrue判定:",a > 0 && b < 0); //true
console.log("2.&& 全てtrueを論理否定判定:",!(a > 0 && b < 0)); //false
console.log("3.&& 全てfalse判定:",a < 0 && b > 0); //false
console.log("4.&& 全てfalseを論理否定判定:",!(a < 0 && b > 0)); //true
}
こちらを実行させた結果です。
15:33:09 お知らせ 実行開始
15:33:10 情報 1.&& 全てtrue判定: true
15:33:10 情報 2.&& 全てtrueを論理否定判定: false
15:33:10 情報 3.&& 全てfalse判定: false
15:33:10 情報 4.&& 全てfalseを論理否定判定: true
15:33:09 お知らせ 実行完了
1.は全てtrue判定なのですが、その結果を論理否定演算子で囲うことでfalse判定となります。
同様に3.は全てfalse判定なのですが、その結果を論理否定演算子で囲うことでtrue判定となります。
なお、例として全てを囲いましたが、
console.log("2.&& 全てtrueを論理否定判定:",!(a > 0) && b < 0);
一部分だけを囲うことも可能です。