焦燥的なプログラマーのためのJavaScript (ES2022版)
この本をサポートしてください: 購入 または 寄付
(広告、ブロックしないでください。)

14 非値 undefinednull



多くのプログラミング言語には、null という1つの「非値」があります。これは、変数が現在オブジェクトを指していないことを示します。たとえば、まだ初期化されていない場合などです。

対照的に、JavaScriptには2つあります。undefinednull です。

14.1 undefined vs. null

両方の値は非常に似ており、しばしば互換的に使用されます。したがって、それらの違いは微妙です。言語自体は、次の区別をしています。

プログラマーは次の区別をするかもしれません。

14.2 undefinednull の出現

次のサブセクションでは、言語内で undefinednull がどこに出現するかについて説明します。この本で後ほど詳しく説明するいくつかのメカニズムに出会うことになります。

14.2.1 undefined の出現

未初期化の変数 myVar

let myVar;
assert.equal(myVar, undefined);

パラメータ x が指定されていない

function func(x) {
  return x;
}
assert.equal(func(), undefined);

プロパティ .unknownProp が見つからない

const obj = {};
assert.equal(obj.unknownProp, undefined);

return ステートメントを介して関数の結果を明示的に指定しない場合、JavaScriptは undefined を返します。

function func() {}
assert.equal(func(), undefined);

14.2.2 null の出現

オブジェクトのプロトタイプは、オブジェクトであるか、プロトタイプチェーンの最後では null です。Object.prototype にはプロトタイプがありません。

> Object.getPrototypeOf(Object.prototype)
null

正規表現(/a/など)を文字列('x'など)に対してマッチングする場合、マッチングが成功した場合はマッチングデータを含むオブジェクトが得られるか、マッチングが失敗した場合は null が得られます。

> /a/.exec('x')
null

JSONデータ形式は、undefined をサポートしておらず、null のみサポートしています。

> JSON.stringify({a: undefined, b: null})
'{"b":null}'

14.3 undefined または null のチェック

どちらかのチェック

if (x === null) ···
if (x === undefined) ···

x は値を持っていますか?

if (x !== undefined && x !== null) {
  // ···
}
if (x) { // truthy?
  // x is neither: undefined, null, false, 0, NaN, ''
}

xundefined または null のどちらかですか?

if (x === undefined || x === null) {
  // ···
}
if (!x) { // falsy?
  // x is: undefined, null, false, 0, NaN, ''
}

Truthy は「ブール値に強制変換された場合に true になる」ことを意味します。Falsy は「ブール値に強制変換された場合に false になる」ことを意味します。両方の概念は、§15.2「Falsyとtruthyな値」で適切に説明されています。

14.4 デフォルト値のためのNullish coalescing演算子(??)[ES2020]

値を受け取って、それが null または undefined のどちらでもない場合にのみ使用したい場合があります。それ以外の場合は、フォールバックとしてデフォルト値を使用したいと思います。これは、Nullish coalescing演算子??)を使用して行うことができます。

const valueToUse = receivedValue ?? defaultValue;

次の2つの式は同等です。

a ?? b
a !== undefined && a !== null ? a : b

14.4.1 例:一致回数のカウント

次のコードは、実世界の例を示しています。

function countMatches(regex, str) {
  const matchResult = str.match(regex); // null or Array
  return (matchResult ?? []).length;
}

assert.equal(
  countMatches(/a/g, 'ababa'), 3);
assert.equal(
  countMatches(/b/g, 'ababa'), 2);
assert.equal(
  countMatches(/x/g, 'ababa'), 0);

str 内に regex の一致が1つ以上ある場合、.match() は配列を返します。一致がない場合、残念ながら(空の配列ではなく)null を返します。??演算子を使用して修正します。

オプショナルチェイニングを使用することもできました。

return matchResult?.length ?? 0;

14.4.2 例:プロパティのデフォルト値の指定

function getTitle(fileDesc) {
  return fileDesc.title ?? '(Untitled)';
}

const files = [
  {path: 'index.html', title: 'Home'},
  {path: 'tmp.html'},
];
assert.deepEqual(
  files.map(f => getTitle(f)),
  ['Home', '(Untitled)']);

14.4.3 デフォルト値への分割代入の利用

場合によっては、分割代入をデフォルト値に使用することもできます。たとえば、次のようになります。

function getTitle(fileDesc) {
  const {title = '(Untitled)'} = fileDesc;
  return title;
}

14.4.4 レガシーなアプローチ:デフォルト値への論理OR(||)の利用

ECMAScript 2020以前およびNullish coalescing演算子以前は、論理ORがデフォルト値に使用されていました。これには欠点があります。

||undefined および null に対して期待どおりに動作します。

> undefined || 'default'
'default'
> null || 'default'
'default'

しかし、他のすべてのfalsyな値に対してもデフォルトを返します。たとえば、次のようになります。

> false || 'default'
'default'
> 0 || 'default'
'default'
> 0n || 'default'
'default'
> '' || 'default'
'default'

それを ?? の動作と比較してください。

> undefined ?? 'default'
'default'
> null ?? 'default'
'default'

> false ?? 'default'
false
> 0 ?? 'default'
0
> 0n ?? 'default'
0n
> '' ?? 'default'
''

14.4.5 Nullish coalescing代入演算子(??=)[ES2021]

??=論理代入演算子です。次の2つの式はほぼ同等です。

a ??= b
a ?? (a = b)

つまり、??=ショートサーキットです。代入は、aundefined または null の場合にのみ行われます。

14.4.5.1 例:??= を使用して欠落しているプロパティを追加する
const books = [
  {
    isbn: '123',
  },
  {
    title: 'ECMAScript Language Specification',
    isbn: '456',
  },
];

// Add property .title where it’s missing
for (const book of books) {
  book.title ??= '(Untitled)';
}

assert.deepEqual(
  books,
  [
    {
      isbn: '123',
      title: '(Untitled)',
    },
    {
      title: 'ECMAScript Language Specification',
      isbn: '456',
    },
  ]);

14.5 undefinednull はプロパティを持たない

undefinednull は、プロパティを読み取ろうとした場合に例外が発生する唯一の2つのJavaScriptの値です。この現象を調べるために、プロパティ .foo を読み取って(「取得」して)結果を返す次の関数を使用しましょう。

function getFoo(x) {
  return x.foo;
}

getFoo() をさまざまな値に適用すると、undefinednull の場合にのみ失敗することがわかります。

> getFoo(undefined)
TypeError: Cannot read properties of undefined (reading 'foo')
> getFoo(null)
TypeError: Cannot read properties of null (reading 'foo')

> getFoo(true)
undefined
> getFoo({})
undefined

14.6 undefinednull の歴史

(JavaScriptの多くの側面に影響を与えた)Javaでは、初期化値は変数の静的な型に依存します。

JavaScriptでは、各変数はオブジェクト値とプリミティブ値の両方を保持できます。したがって、null が「オブジェクトではない」ことを意味する場合、JavaScriptには「オブジェクトでもプリミティブ値でもない」ことを意味する初期化値も必要です。その初期化値が undefined です。

  クイズ

クイズアプリを参照してください。