日本語版
最新ニュース
科学&テクノロジー

JavaScript における undefined のさまざまな側面

JavaScript には、なぜ未知の値を表す方法がこんなにたくさんあるのでしょうか。私が見つけた方法のうち、必要なのは 2、3 だけです。残りの未知の値は混乱を招き、コードに欠陥を生じさせます。これは残念な状況ですが、JavaScript でコードを書く場合は理解しておく価値があります。以下では、JavaScript で欠損データ、不明値、未定義値を表現する方法をまとめます。この記事では、それぞれについて詳しく説明します。 シンプルなケース null Null は典型的な null 値です。これは通常、値がオプションであるが、提供されていないことを意味するために使用されます。これが言語内の唯一の未定義値であれば、多くのプログラマーは満足するでしょう。実際、他の多くの言語では、1 つだけで済みます。しかし、JavaScript は違います。これはまだ始まったばかりです。 undefined 行方不明として の概念 undefined 一見すると意味が通っているように思えます。 null 実際の値を指定する undefined 値は実際には存在しないことを示します。TypeScript 表記を使用してこの構造を考慮すると、これは理にかなっています。 interface Arguments { nullable_value: number | null optional_value?: number | null } 私たちはこう言っています nullable_value…

JavaScript における undefined のさまざまな側面

1721237113
2024-07-17 13:12:55

JavaScript には、なぜ未知の値を表す方法がこんなにたくさんあるのでしょうか。私が見つけた方法のうち、必要なのは 2、3 だけです。残りの未知の値は混乱を招き、コードに欠陥を生じさせます。これは残念な状況ですが、JavaScript でコードを書く場合は理解しておく価値があります。

以下では、JavaScript で欠損データ、不明値、未定義値を表現する方法をまとめます。この記事では、それぞれについて詳しく説明します。

シンプルなケース

null

Null は典型的な null 値です。これは通常、値がオプションであるが、提供されていないことを意味するために使用されます。これが言語内の唯一の未定義値であれば、多くのプログラマーは満足するでしょう。実際、他の多くの言語では、1 つだけで済みます。しかし、JavaScript は違います。これはまだ始まったばかりです。

undefined 行方不明として

の概念 undefined 一見すると意味が通っているように思えます。 null 実際の値を指定する undefined 値は実際には存在しないことを示します。TypeScript 表記を使用してこの構造を考慮すると、これは理にかなっています。

interface Arguments {
	nullable_value: number | null
	optional_value?: number | null
}

私たちはこう言っています nullable_value はこの構造体の必須プロパティです。省略することはできません。ただし、null 値にすることはできます。 optional_value も null 可能ですが、存在する必要はありません。 undefined 特定の null 価値があり、価値がまったく存在しない。

const a = {
	nullable_value: null,
	optional_value: null,
}

const b = {
	nullable_value: null,
}

これらの変数により、 a.optional_value === null。 で b しかし、 b.optional_value !== null しかし b.optional_value === undefined、 以来 optional_value 定義されていない b まったく。普段使いでは、これは構造体から欠落している値を報告または検出するための適切な方法のように感じられます。

残念ながら、非可逆比較は混乱を招きます。私は === null 厳密にチェックする null 値。式 b.optional_value == null 未定義の値は大まかに比較すると次の値と等しいため、真です。 null等式は引数を入れ替えても同じなので、 b.nullable_value == undefined も真実です。

他のほとんどの言語には、この undefined の概念はありません。 C++の分野ではundefined は特定の値ではありませんが、値が未定義であることを意味します。つまり、確定した状態を持たず、何でもあり得ます。

undefined 価値として

比較すると undefined 必要となる undefined は言語のシンボルです。そして、構文の一貫性を保つために、それは言語の実数値です。これはJavaScriptが実装の単純さのために選択したもので、 undefined 特別なシンボル。

ここで最初の奇妙な点が現れます。 undefined それ自体はあなたにとってまだ奇妙ではありませんでした。次の定数を指定したらどうなるでしょうか:

const c = {
	nullable_value: null,
	optional_value: undefined,
}

私ははっきりと言った optional_valueundefinedでは、この構造は実際に optional_value 財産?

標準JSONモジュールを使用してJSONにシリアル化して戻すと、明示的な undefined 何も指定しなかったかのように、削除される。これは良いことかもしれない。さらに、 c.optional_value === undefined は正しいです。それは良いことです。値をまったく指定しないのと同じように思えますが…

…ちょっと待って? Object.hasOwn( c, ‘optional_value’ ) 本当です。だから coptional_value 財産です。確かに、私が放出すれば c コンソールに次のように表示されます optional_value: undefinedこれはオブジェクトとは対照的です b 前に定義したように、 Object.hasOwn( b, ‘optional_value’ ) は誤りです。

混乱を招く慣習

これにより、 undefined プロパティ値は、存在しないプロパティとは区別されます。この不一致は、コード内の未定義値関連の欠陥の一般的な原因です。

これはまた、 undefined値が存在しないという意味ではなく、単に null慣習的に、私たちは undefined 値は欠損値と同等になります。また、すべてのライブラリがこの規則に準拠しているわけではなく、標準の JavaScript ライブラリでも同様です。

この慣習は私が 言語間シリアル化フォーマットを作成したPython側の処理を楽にするために、私は最終的にすべての型を次のように扱いました。 null | undefined JavaScriptでは、 undefinedしかし、私はさらに複雑な MissingSentryType 真の未知数と一致する。

解釈上の事例

ここで、より難解な未定義値をいくつか見てみましょう。これらは、明らかに未定義値を導入する言語セマンティクスですが、間接的な方法で導入されます。

偽物

考慮する必要がある 偽物 この議論では、値について説明します。多くのコードは、以下のような条件文で null をチェックします。

if( someObject ) {
   someObject.call()
}

このif式は偽です someObject は null または undefined であり、これがここで求められていたものです。ブール値の false の場合も false です。さらに、値 0 と空の文字列の場合も false です。この 2 つが、null をチェックしていると思われるコードで欠陥を引き起こすのを見たことがあります。

この誤ったパターンはC言語など他の言語でも見られます。しかし、C言語ではJavaScriptと同じ欠陥を引き起こすことはありません。ポインタは異なる型であり、明示的な逆参照が必要です。ゼロの整数を誤ってヌルポインタとして解釈することはできません。 int* someObject C言語では、上記のJavaScriptのif文は次のようになります。 if( someObject && *someObject )

参照エラー

ブラウザコンソールに未知の記号を入力すると、 ReferenceError 例外。

> nothing_here

Uncaught ReferenceError: nothing_here is not defined

しかし、私が入力すると window.nothing_here 私は undefined 価値。 window はグローバルデータ(NodeJSでは「global」)のビューであるはずです。なぜ nothing_here アクセス方法に応じて、不足していることを報告する方法が異なりますか?

この不一致は関数でも発生します。

function missing() {
    console.log( this.nothing_here )
    console.log( nothing_here )
}

この関数を呼び出すと、 undefined 続いて ReferenceError矛盾につながる違いは簡単に理解できます。しかし、なぜこの矛盾が求められるのかを理解するのは難しいです。

JavaScript の配列はスパースになる可能性があり、これにより欠損値を作成する別の方法が導入されます。

arr1 = []
arr1[0] = "Zero"
arr1[2] = "Two"

もし私が放出したら arr1 コンソールに、私は Array(3) [ “Zero”, , “Two” ] 少なくとも Firefox では、短い出力として表示されます。ただし、コンソール出力を展開すると、以下のようになります。

0: "Zero"
2: "Two"
length: 3

2つの要素しか表示されていないのは当然ですが、長さは3のままです。 arr[1] 結果的に undefined

しかし、明示的な undefined 価値?

arr2 = ["Zero", undefined, "Two"]

放出 arr2 コンソールに生成する Array(3) [ “Zero”, undefined, “Two” ]。したがって、空のスロットは、配列内の未定義の値と同じではありません。たとえ、それらが同じ長さで、インデックス1の値が undefined どちらの場合も、電話すれば違いが分かります .flat() それらの上に。 arr1.flat() == [“Zero”, “Two”] 一方 arr2.flat() == [“Zero”, undefined, “Two”]コア言語は、 undefined 欠損値として。

既存の配列に空きスロットを取得したい場合は、 delete オペレーター。 delete arr2[1] を削除します undefined 値を空のスロットに置き換えます。

-1 欠損値として

両方を持っているにもかかわらず undefined そして null 価値観、 Array.indexOf 戻り値 -1 配列内の要素が見つからない場合は Array.find 戻ってきて嬉しい undefined 要素が見つからない場合。

しかし、JavaScriptでは配列は厳密には配列ではありません。配列のように振る舞うオブジェクトです。また、前に見たように、疎なオブジェクトにすることもできます。これにより、 indexOf 関数。

arr = ["Zero", "One"]
arr[-1] = "Minus"
arr.indexOf("Minus") // returns -1

indexOf ここで戻る -1、そのインデックスで見つかったからではなく、適切なインデックスのみをチェックして見つからなかったからです。戻り値をチェックし忘れると、奇妙な欠陥が発生する可能性があります。

const someArray = [];
const at = someArray.indexOf("Source");
someArray[at] = "Target";
someArray[someArray.indexOf("Target")] == "Target"

何かを「見つからない」インデックスに永久的に固定しておいたので、それを探すと見つからないが、同じように見つかることもあります。

未定義の置換

undefined は、少なくとも関数内では、JavaScript の予約シンボルではありません。これにより、undefined のカスタム値を作成できます。

undefined = 123
console.log(undefined) // prints "undefined"

function stupidCode() {
   const undefined = 123
   console.log( undefined ) // prints "123"
}

奇妙なことに、私が書こうとすると const undefined = 123 グローバルスコープでは構文エラーが発生しますが undefined = 123 問題ありませんが、何も起こりません。

TypeScript のケース

関数の引数

TypeScript は、関数シグネチャにオプションの引数を追加することで、さらに工夫を凝らしています。次の関数を考えてみましょう。

function missingValue(a: number, b?: number) {
    console.log(arguments)
    console.log(a, b)
}

missingValue(1)
missingValue(2, undefined)

b? はオプションの引数です。関数本体では、型は number | undefinedですが、これは厳密には正しくありません。 number または、次のように評価される欠損値がある undefinedこれを印刷すると、 arguments 値。最初の呼び出しでは要素が 1 つだけであり、2 番目の呼び出しでは 2 つの要素があり、2 番目の位置には明示的な undefined があります。

したがって、この関数は次のように呼び出されると動作が異なります。 missingValue(1)missingValue(1, undefined)

空所

TypeScript では、未知の形式の 1 つとして void が導入されています。おそらく、戻り値を定義しない関数を宣言するときに、これを最もよく認識するでしょう。

function noReturn() {
}

type CallWithoutReturn = () => void
const proc : CallWithoutReturn = noReturn

しかし、 void はTypeScriptの単なる型です。厳密なnullチェックが有効になっている場合は、次のようになります。 undefinedそれ以外の場合は、 undefined | nullつまり、まったく役に立たないように見えます。すべてのステートメントと式が有効である次のコードを考えてみましょう。

function noReturn() {
	return undefined
}

function undefinedReturn() : undefined {
	// no explicit return
}

function impliedMismatch() : void {
  const a = undefined
  return a
}

const a : void = undefined
const b: void = noReturn()
const c : undefined = noReturn()
const d : void = undefinedReturn()

全部交換できたのに voidundefinedvoid 言語に新しい意味論を導入するものではありません。

未定義の結論

つまり、 undefined 「未定義」です。

これは多くの場所では問題にならないかもしれませんが、いくつかの一般的な使用例では悪影響があります。シリアル化と辞書です。シリアル化では、すべての欠落値が同じか、明示的な未定義が特別に扱われるか、またはnullとは区別されるかを決定する必要があります。JavaScriptオブジェクトを辞書として使用する場合(これはよく行われますが)、落とし穴もあります。欠落値と明示的な未定義値では反復処理が異なります。イントロスペクション関数の呼び出し、たとえば hasPropertyも慎重に使用する必要があります。

これらの不一致は、元の未指定の JavaScript の奇妙さを考慮しようとした言語の進化によって生じたものと思われます。TypeScript もこれをすべてそのままにしていたため、コードで最もよく使用される値に大幅な安全性を追加できませんでした。これは残念なことです。Web を通じてコーディングを始めた人は、一般的なプログラミング言語の中で最も混乱した欠損値システムにさらされることになるからです。

#JavaScript #における #undefined #のさまざまな側面

執筆者について: nipponese

Nipponese News編集部は、国内外のニュースを日本語で分かりやすくお届けします。