13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[JavaScript]null と undefined を区別したデフォルト値設定の書き方

Last updated at Posted at 2020-05-09

グッドパターン

簡単でした。このように書きましょう。


[null, undefined].includes(value) ? 'default': value

これでnullとundefinedを区別できます。また、空文字や0やfalseのときもデフォルト値にいれたければ簡単に変更できます。

動作確認

var value = null;
console.log([null, undefined].includes(value) ? 'default': value);
// default

var value = undefined;
console.log([null, undefined].includes(value) ? 'default': value);
// default

var value = 0;
console.log([null, undefined].includes(value) ? 'default': value);
// 0

var value = false;
console.log([null, undefined].includes(value) ? 'default': value);
// false

var value = '';
console.log([null, undefined].includes(value) ? 'default': value);
// ''

バッドパターン

OR演算子で接続するパターンはやってはいけません。False属性のもの全てが default値になるので、何の場合にデフォルト値にしたいのかがよくわからないコードになります。

他の人が書いたこういうコードをメンテナンスすることになる場合に、何をねらって書いているのかがはっきりせずぼんやりしたコードになるために、バグの温床になります。

var value = null;
console.log(value || 'default');
// default

var value = undefined;
console.log(value || 'default');
// default

var value = 0;
console.log(value || 'default');
// default

var value = false;
console.log(value || 'default');
// default

var value = '';
console.log(value || 'default');
// default

昔のJSでよく書かれていましたよね。デメリットしかないのでもうやめましょう。
「value の無効値は null か undefined しか入らないという前提があるから」OR演算子を使ってもいい。と考える人もいるかもしれませんが、コード読む側からするとそういう前提がわからないまま処理を追う場合に非常に追いにくいのです。

無効値として nullがはいる可能性しかないなら、nullだけ判断する。無効値として null か undefined だけしか入らないならそれだけ判断する。無効値として null か undefined か 空文字 だけなら、それらだけを判断する、とプログラムは書くべきです。

ノーマルパターン

node.js の v14以降とか最新ブラウザ環境ならnull合体演算子というものが使えます。

Null合体演算子 - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

var value = null;
console.log(value ?? 'default');
// default

var value = undefined;
console.log(value ?? 'default');
// default

var value = 0;
console.log(value ?? 'default');
// 0

var value = false;
console.log(value ?? 'default');
// false

var value = '';
console.log(value ?? 'default');
// ''

こちらは null と undefined が同一視する場合のみ有効です。が、これも実際にはOR演算子と同じで null だけ、undefined だけ、判定するつもりで null合体演算子をつかわれると、コードが曖昧になるために、やはりバグの温床感があります。OR演算子よりちょっとまし、なだけで、厳密なプログラムを書く場合には不必要です。

また、短く書けるのですが、

0の場合もデフォルト値に含めたいとか、空文字の場合もデフォルト値に含めたい、とか、undefined のときだけデフォルト値にしたい、という要望に即時に対応することも難しいので、先頭に書いた、includesのパターンのほうが可読性も高く汎用的なのでより優れています。

よりよい解決策。関数で共通化する。

or演算子でも、null合体演算子でもですが、それを使う動機というのは、単に value(あるいは valueXxxXxxXxxXxx)という変数を2回書きたくないというショートハンド的な打鍵数を減らすというメリットがあるから多用されているわけです。

でも、打鍵数が減っても将来的な汎用的な機能拡張ができないのなら本末転倒です。

valueを2回書きたくないなら、関数化しておくのが普通のやり方です。


const initialValue = (
  value, defaultValue, compare = [null, undefined]
) => {
  return compare.includes(value) ? defaultValue : value
};

///////
var value = null;
console.log(initialValue(value, 'default'));
// default

var value = undefined;
console.log(initialValue(value, 'default'));
// default

var value = 0;
console.log(initialValue(value, 'default'));
// 0

var value = false;
console.log(initialValue(value, 'default'));
// false

var value = '';
console.log(initialValue(value, 'default'));
// ''

これで、null合体演算子と同じ機能を持ちますし、また、第3引数を指定すると0とか空文字とか、自分で決めた特定の値が入ったときもデフォルト値を返すという場面にすぐに対応できます。(これが汎用的)

将来、簡単に対応するために、楽をする(怠惰でいる)ために、現在のコードをしっかりと作り込むのが、プログラマの美徳か何かだったと思いますので、null合体演算子とか不要な記号を安易に使わずに、initialValueみたいな関数を自分で作っておくと便利でよいですよ。

共通関数を作らないプロジェクトなんてない(あったらやばい)わけですから。

参考になった記事

2022/02/03(Thu) 追記
いいねをちらほらもらいますので記載しておきます。

参考になる記事を知りました。

undefinedとnullの違い | TypeScript入門『サバイバルTypeScript』
https://typescriptbook.jp/reference/values-types-variables/undefined-vs-null

「nullは使わずundefinedに統一しよう」はシンプルなルールです。これなら共通認識として持つことがしやすく、チームワークもしやすくなります。実際にTypeScriptの開発チームでは「nullは使わない」というたった1行のシンプルなガイドラインを示し、数多くの開発者が参加しやすくなるようにしています。

undefinedに統一するほうが簡単
「値がない」ことを意味するものがundefinedとnullの2種類あることが混乱の元なので、どちらか一方を使うようにするほうがコーディング上の意思決定を減らせます。なので、nullに寄せていく方法も考えられます。しかし、それはお勧めしません。undefinedはいたるところで自然に発生してくるので、それらをすべてnullにしようとすると、記述量がどんどん増えていくからです。

これはとてもそのとおりに思います。nullを使わない。という選択はとても常識的なよい判断です。何か初期値として「値がない」状態の変数を作るときに nullではなく undefined を代入する、ということ。

配列の.find なども値が見つからなければ、undefined を返しますのでそういうのとも共通的な言語仕様です。

ただ、記事中にある下記のところは同意はできなかったです。

(nullとundefinedを)使い分け意識を育てる労力は、それに見合うメリットが少ない

使い分けずにミスして不具合引き起こして、バグが見つからないとか不具合がなぜか直らない。数時間あるいは一日、あるいは多重に問題が重なって数日、バグ取りだけに費やさなくてはいけないというような状況を幾度も体験しています。

特に困難なプロジェクトは、この使い分け意識がない開発者が多数参加していてコード全体がそのような事になっていて不具合があふれている状態になったりします。

そういう経験があると、この使い分け意識を育てる労力がメリットがない、というのは全く思いません。

null と undefined の概念はたしかに少し混乱をもたらしますが知ってしまえばどうでもいいレベルの話です。知らない初心者が混乱を起こしていて知らない初心者が書くコードが他の人にも混乱を広げてしまうだけなので、みんながこの使い分けを知っていれば問題ありません。

その程度の使い分け意識を育てる労力は省いてはいけない、と思います。

null と undefined の区別がわからない状態なのと、知った上で null は使わないというのとは、大きく違います。

PR

GitHubで公開しているライブラリ Parts.js では、matchSomeValue関数や initialValue関数を実装しています。関数を指定できたりしてもっと汎用的な多機能にしています。
適当にご参考ください。

partsjs/match.js at master · standard-software/partsjs
https://github.com/standard-software/partsjs/blob/v5.3.1/source_code/compare/match.js

13
8
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?