JavaScriptで未定義を処理するための7つのヒント

Ruby、Python、Javaなどの最新言語のほとんどには、単一のnull値(nilまたはnull)、これは合理的なアプローチのようです。

しかし、JavaScriptは異なります。

nullだけでなく、undefinedも、JavaScriptの空の値を表します。では、それらの正確な違いは何ですか?

簡単に言うと、JavaScriptインタープリターは、まだ初期化されていない変数またはオブジェクトプロパティにアクセスすると、undefinedを返します。例:

反対側では、nullは欠落しているオブジェクトを表します参照。 JavaScriptは、nullを使用して変数またはオブジェクトプロパティを初期化しません。

String.prototype.match()などの一部のネイティブメソッドは、nullを返して不足しているオブジェクトを示すことができます。サンプルを見てください:

JavaScriptは寛容であるため、開発者は初期化されていない値にアクセスしたいという誘惑に駆られます。私もそのような悪い習慣に罪を犯しています。

このような危険なアクションは、多くの場合、undefined関連のエラーを生成します:

  • TypeError: "undefined" is not a function
  • TypeError: Cannot read property "<prop-name>" of undefined
  • 同様のタイプエラー。

JavaScript開発者はこのジョークの皮肉を理解できます。 :

このようなエラーを減らすには、undefinedが生成されます。 undefinedとそのコードの安全性への影響を調べてみましょう。

1。未定義のもの

JavaScriptには6つのプリミティブ型があります:

そして分離されたオブジェクト型:{name: "Dmitri"}

6つのプリミティブ型からundefinedは、独自の型Undefinedを持つ特別な値です。 ECMAScript仕様によると:

変数に値が割り当てられていない場合、未定義の値プリミティブ値が使用されます。

標準では、初期化されていない変数、存在しないオブジェクトプロパティにアクセスすると、undefinedを受け取ることが明確に定義されています。存在しない配列要素など。

いくつかの例:

上記の例は、以下にアクセスすることを示しています。

  • 初期化されていない変数number
  • 存在しないオブジェクトプロパティmovie.year
  • または存在しない配列要素movies

undefinedに評価されます。

ECMAScript仕様は、undefined値のタイプを定義します。

未定義のタイプは唯一の値がundefined値であるタイプ。

この意味で、typeof演算子はundefined値の"undefined"文字列を返します:

もちろんtypeofは、変数にundefined値が含まれているかどうかを確認するのに適しています。

2。未定義を作成するシナリオ

2.1初期化されていない変数

宣言された変数であるが、まだ値が割り当てられていない(初期化されていない)のは、デフォルトでundefinedです。

わかりやすくシンプル:

myVariableが宣言されており、まだ値が割り当てられていません。変数にアクセスすると、undefinedに評価されます。

初期化されていない変数の問題を解決するための効率的なアプローチは、可能な限り初期値を割り当てることです。初期化されていない状態で存在する変数が少ないほど、優れています。

理想的には、宣言const myVariable = "Initial value"の直後に値を割り当てます。しかし、それが常に可能であるとは限りません。

ヒント1:constを優先するか、letを使用しますが、

私の意見では、ECMAScript 2015の最も優れた機能の1つは、constと。それは大きな前進です。

constletはブロックスコープです(以前の関数スコープvar)であり、宣言行まで一時的なデッドゾーンに存在します。

値が変更されない場合は、const変数をお勧めします。不変のバインディングを作成します。

constの優れた機能の1つは、変数const myVariable = "initial"に初期値を割り当てる必要があることです。変数は初期化されていない状態にさらされておらず、undefinedにアクセスすることはできません。

単語が回文であるかどうかを確認する関数を確認しましょう:

lengthおよびhalf変数には値が1回割り当てられます。これらの変数は変更されないため、constとして宣言するのが妥当と思われます。

値が変更される可能性のある変数には、let宣言を使用します。可能な限り、すぐに初期値を割り当てます。 let index = 0

古い学校のvarはどうですか?私の提案はそれを使うのをやめることです。

var宣言の問題は、関数スコープ内での変数の巻き上げです。関数スコープの最後のどこかでvar変数を宣言できますが、それでも宣言前にアクセスできます。undefined

myVariableはアクセス可能であり、宣言行の前でもundefinedが含まれています:var myVariable = "Initial value"

逆に、constまたはlet変数は、宣言行の前にアクセスできません—変数宣言前の一時的なデッドゾーンにあります。 undefinedにアクセスする機会が少ないので便利です。

上記の例はletで更新されています(代わりにof var)は、一時的な不感帯の変数にアクセスできないため、ReferenceErrorをスローします。

不変のバインディングにconstを使用することを推奨するか、letを使用すると、初期化されていないものの外観を減らすことができます。変数。

ヒント2:凝集度を高める

凝集度は、モジュールの要素(名前空間、クラス、メソッド、コードのブロック)がどの程度一緒に属するかを示します。凝集力は高くても低くてもかまいません。

このようなモジュールの要素は単一のタスクのみに焦点を当てているため、高凝集性モジュールが望ましいです。モジュールを次のようにします。

  • 焦点を絞って理解できる:モジュールの機能を理解しやすくする
  • 保守可能でリファクタリングしやすい:モジュールの変更が影響するモジュールの数が少なくなります
  • 再利用可能:単一のタスクに焦点を合わせているため、モジュールの再利用が容易になります
  • テスト可能:単一のタスクに焦点を合わせたモジュールを簡単にテストできます

疎結合を伴う高い凝集度は、適切に設計されたシステムの特徴です。

コードブロックは小さなモジュールと見なすことができます。高い凝集度の利点を活用するには、変数を使用するコードブロックにできるだけ近づけてください。

たとえば、ブロックスコープのロジックを形成するために変数が単独で存在する場合は、そのブロック内でのみ変数を宣言して有効にします(constまたはlet宣言)。外側のブロックはこの変数を気にする必要がないため、この変数を外側のブロックスコープに公開しないでください。

変数の寿命が不必要に長くなる典型的な例の1つは、関数内のサイクル:

indexitemlength変数は関数本体の先頭で宣言されます。ただし、それらは終わり近くでのみ使用されます。このアプローチの問題は何ですか?

上部の宣言とforステートメントでの使用の間で変数indexitemは初期化されておらず、undefinedに公開されています。それらは、機能範囲全体で不当に長いライフサイクルを持っています。

より良いアプローチは、これらの変数を使用場所にできるだけ近づけることです:

indexおよびitem変数は、forステートメントのブロックスコープにのみ存在します。 for以外では意味がありません。
length変数もその使用元の近くで宣言されています。

変更されたバージョンが最初のバージョンよりも優れているのはなぜですか?見てみましょう:

  • 変数は初期化されていない状態にさらされていないため、undefined
  • にアクセスするリスクはありません。変数を使用場所にできるだけ近づけると、コードの可読性が向上します
  • 必要に応じて、コードのまとまりのあるチャンクをリファクタリングして個別の関数に抽出する方が簡単です

2.2アクセス存在しないプロパティ

存在しないオブジェクトプロパティにアクセスすると、JavaScriptはundefinedを返します。

例でそれを示しましょう:

favoriteMovieは、単一のプロパティtitleを持つオブジェクトです。プロパティアクセサーfavoriteMovie.actorsを使用して存在しないプロパティactorsにアクセスすると、undefinedと評価されます。

存在しないプロパティにアクセスしても、エラーはスローされません。この問題は、最も一般的なundefinedトラップである存在しないプロパティからデータを取得しようとすると発生し、よく知られているエラーメッセージTypeError: Cannot read property <prop> of undefined

前のコードスニペットを少し変更して、TypeErrorのスローを説明しましょう:

favoriteMovieにはプロパティactorsがないため、favoriteMovie.actors undefinedに評価されます。

その結果、式favoriteMovie.actorsを使用してundefined値の最初の項目にアクセスすると、

存在しないプロパティへのアクセスを許可するJavaScriptの寛容な性質は、非決定性の原因です。プロパティが設定されているかどうかは不明です。この問題を回避する良い方法は、オブジェクトが保持するプロパティを常に定義するようにオブジェクトを制限することです。

残念ながら、多くの場合、オブジェクトを制御できません。このようなオブジェクトは、さまざまなシナリオで異なるプロパティのセットを持つ場合があります。したがって、これらすべてのシナリオを手動で処理する必要があります。

新しい要素の配列の最初または最後に追加する関数append(array, toAppend)を実装しましょう。 toAppendパラメーターは、プロパティを持つオブジェクトを受け入れます:

  • first
  • lastarrayの最後に挿入された要素。

この関数は、元の配列を変更せずに、新しい配列インスタンスを返します。

append()の最初のバージョンは、少しナイーブですが、次のようになります。

toAppendオブジェクトはfirstまたはlastプロパティを省略できます。これらのプロパティが。

プロパティが存在しない場合、プロパティアクセサーはundefinedと評価します。 firstまたはlastプロパティが存在するかどうかを確認する最初の誘惑は、undefined。これは、条件付きif(toAppend.first){}およびif(toAppend.last){}

では実行されません。このアプローチには欠点があります。 undefined、およびfalsenull0NaN""は偽の値です。

append()の現在の実装では、関数は偽の要素を挿入できません:

以下のヒントでは、プロパティの存在を正しく確認する方法について説明しています。

ヒント3:プロパティの存在を確認する

幸い、JavaScriptはオブジェクトに特定のプロパティがあるかどうかを判断するさまざまな方法:

  • obj.prop !== undefinedundefined直接
  • typeof obj.prop !== "undefined":プロパティ値のタイプを確認します
  • obj.hasOwnProperty("prop"):オブジェクトに独自のプロパティがある
  • "prop" in obj:オブジェクトに独自のプロパティがあるか継承されているプロパティがあるかを確認する

推奨事項はin演算子を使用します。短くて甘い構文です。 in演算子の存在は、実際のプロパティ値にアクセスせずに、オブジェクトに特定のプロパティがあるかどうかを確認するという明確な意図を示唆しています。

obj.hasOwnProperty("prop")も優れたソリューションです。 in演算子よりもわずかに長く、オブジェクト自体のプロパティでのみ検証されます。

in演算子を使用してappend(array, toAppend)関数を改善しましょう:

"first" in toAppend(および"last" in toAppend)は、対応するプロパティが存在するかどうかにかかわらず、truefalseそれ以外の場合。

in演算子は、偽の要素0およびfalse。ここで、これらの要素をの最初と最後に追加すると、期待される結果が生成されます。

ヒント4:オブジェクトプロパティにアクセスするための破棄

オブジェクトプロパティにアクセスするときに、プロパティが存在しない場合はデフォルト値を設定する必要がある場合があります。

これを実現するには、inを三項演算子と一緒に使用できます。

チェックするプロパティの数が増えると、三項演算子の構文が難しくなります。プロパティごとに、デフォルトを処理するための新しいコード行を作成する必要があり、似たような三項演算子の醜い壁が増えます。

より洗練されたアプローチを使用するために、オブジェクトの破棄と呼ばれるES2015の優れた機能について理解しましょう。

オブジェクトの非構造化により、オブジェクトのプロパティ値を変数に直接インライン抽出し、プロパティが存在しない場合はデフォルト値を設定できます。 undefinedを直接処理しないようにするための便利な構文。

実際、プロパティの抽出は正確になりました。

動作を確認するために、文字列を引用符で囲む便利な関数を定義しましょう。

quote(subject, config)は、最初の引数をラップする文字列として受け入れます。 2番目の引数configは、プロパティを持つオブジェクトです。

オブジェクトの破壊の利点を適用して、quote()

const { char = """, skipIfQuoted = true } = config割り当てを1行で破棄すると、プロパティcharskipIfQuoted from configオブジェクト。
configオブジェクトに一部のプロパティがない場合、破棄割り当てによってデフォルト値が設定されます。 :charの場合は"""skipIfQuotedの場合はfalse

幸いなことに、この機能にはまだ改善の余地があります。

破壊する割り当てをパラメータセクションに移動しましょう。また、configパラメータのデフォルト値(空のオブジェクト{ })を設定して、デフォルト設定で十分な場合に2番目の引数をスキップします。

非構造化割り当ては、関数のシグネチャのconfigパラメータを置き換えます。私はそれが好きです:quote()は1行短くなります。

= {}は、非構造化割り当ての右側にあり、2番目の引数がまったく指定されていない場合、空のオブジェクトが使用されるようにしますquote("Sunny day")

オブジェクトの破棄は、オブジェクトからのプロパティの抽出を効率的に処理する強力な機能です。アクセスしたプロパティが存在しない場合に返されるデフォルト値を指定できるのが好きです。その結果、undefinedとその周りの煩わしさを回避できます。

ヒント5:オブジェクトにデフォルトのプロパティを入力する

破壊の割り当てのように、すべてのプロパティに変数を作成する必要がない場合は、一部のプロパティが欠落しているオブジェクトを入力できます。デフォルト値で。

ES2015 Object.assign(target, source1, source2, ...)は、列挙可能なすべての独自プロパティの値を1つ以上のソースオブジェクトからターゲットオブジェクトにコピーします。この関数はターゲットオブジェクトを返します。

たとえば、unsafeOptionsオブジェクトのプロパティにアクセスする必要がありますが、プロパティの完全なセットが常に含まれているとは限りません。

unsafeOptionsから存在しないプロパティにアクセスするときにundefinedを回避するには、いくつかの調整を行います。

  • デフォルトのプロパティ値を保持するオブジェクトdefaultsを定義します
  • Object.assign({ }, defaults, unsafeOptions)を呼び出してビルドします新しいオブジェクトoptions。新しいオブジェクトはunsafeOptionsからすべてのプロパティを受け取りますが、欠落しているプロパティはdefaultsから取得されます。

unsafeOptionsにはfontSizeプロパティのみが含まれます。 defaultsオブジェクトは、プロパティfontSizeおよびcolorのデフォルト値を定義します。

Object.assign()は、最初の引数をターゲットオブジェクト{}として受け取ります。ターゲットオブジェクトは、unsafeOptionsソースオブジェクトからfontSizeプロパティの値を受け取ります。また、unsafeOptionsには含まれていないため、defaultsソースオブジェクトのcolorプロパティの値color

ソースオブジェクトが列挙される順序は重要です。後のソースオブジェクトのプロパティは、前のプロパティを上書きします。

options.colorを含め、optionsオブジェクトのプロパティに安全にアクセスできるようになりました。 div id = “5cc93a0102”> 最初は。

幸い、オブジェクトをデフォルトのプロパティで埋める簡単な方法があります。オブジェクト初期化子でspreadプロパティを使用することをお勧めします。

Object.assign()の呼び出しの代わりに、オブジェクト拡散構文を使用して、ソースオブジェクトからすべての独自の列挙可能なプロパティをターゲットオブジェクトにコピーします。

オブジェクト初期化子は、defaultsおよびunsafeOptionsソースオブジェクトからプロパティを拡散します。ソースオブジェクトを指定する順序は重要です。後のソースオブジェクトのプロパティが前のプロパティを上書きします。

不完全なオブジェクトをデフォルトのプロパティ値で埋めることは、コードを安全で耐久性のあるものにするための効率的な戦略です。状況に関係なく、オブジェクトには常にプロパティの完全なセットが含まれています。undefinedは生成できません。

ボーナスのヒント:nullish合体

演算子nullish合体は、オペランドがundefinedまたは:

null合体演算子は、デフォルト値を使用しながらオブジェクトプロパティにアクセスするのに便利です。このプロパティはundefinedまたはnullです:

stylesオブジェクトにはプロパティcolorがないため、styles.colorプロパティアクセサはundefinedです。styles.color ?? "black"はデフォルト値"black"に評価されます。

styles.fontSize18であるため、ヌル合体演算子はプロパティ値。

2.3関数パラメーター

関数パラメーターのデフォルトは暗黙的にundefinedです。

通常、特定の数のパラメーターで定義された関数は、同じ数の引数で呼び出す必要があります。そのとき、パラメーターは期待する値を取得します。

multiply(5, 3)の場合、パラメータabはそれぞれ53値。乗算は期待どおりに計算されます:5 * 3 = 15

呼び出しで引数を省略するとどうなりますか?関数内の対応するパラメーターはundefinedになります。

引数を1つだけ指定して関数を呼び出すことにより、前の例を少し変更してみましょう。

呼び出しmultiply(5)は単一の引数で実行されます。結果としてaパラメーターは5ですが、 bパラメーターはundefinedです。

ヒント6:デフォルトのパラメーター値を使用する

関数は、呼び出し時に引数の完全なセットを必要としない場合があります。値を持たないパラメータのデフォルトを設定できます。

前の例を思い出して、改善してみましょう。 bパラメータがundefinedの場合、デフォルトで2

この関数は、単一の引数multiply(5)で呼び出されます。最初は、aパラメーターは2であり、bundefined
条件ステートメントは、bundefinedであるかどうかを確認します。発生した場合、b = 2割り当てによってデフォルト値が設定されます。

デフォルト値を割り当てるために提供されている方法は機能しますが、undefinedと直接比較することはお勧めしません。それは冗長で、ハックのように見えます。

より良いアプローチは、ES2015のデフォルトパラメータ機能を使用することです。短く、表現力があり、undefinedと直接比較することはありません。

パラメータb = 2にデフォルト値を追加すると見栄えが良くなります:

関数シグネチャの

b = 2は、bundefinedの場合、パラメータのデフォルトは2です。

ES2015のデフォルトパラメータ機能は直感的で表現力豊かです。オプションのパラメータのデフォルト値を設定するには、常にこれを使用してください。

2.4関数の戻り値

暗黙的に、returnステートメントなしでJavaScript関数undefinedを返します。

returnステートメントは暗黙的にundefinedを返します:

square()関数は計算結果を返しません。関数の呼び出し結果はundefinedです。

returnステートメントが存在するが、近くに式がない場合にも同じ状況が発生します:

return;ステートメントが実行されますが、式は返されません。呼び出し結果もundefinedです。

もちろん、returnの近くを示すと、返される式は期待どおりに機能します:

これで、関数の呼び出しは4に評価されます。これは、2の2乗です。

ヒント7:セミコロンの自動挿入を信頼しないでください

JavaScriptの次のステートメントのリストは、セミコロンで終了する必要があります(;) :

  • 空のステートメント
  • letconstvarimportexport宣言
  • 式ステートメント
  • debuggerステートメント
  • continueステートメント、breakステートメント
  • throwステートメント
  • returnステートメント

If上記のステートメントのいずれかを使用する場合は、必ず最後にセミコロンを指定してください。

両方の最後にlet宣言とreturnステートメントには必須のセミコロンが記述されています。

これらを示したくない場合はどうなりますかセミコロン?このような状況では、ECMAScriptは自動セミコロン挿入(ASI)メカニズムを提供し、欠落しているセミコロンを挿入します。

ASIの助けを借りて、前の例からセミコロンを削除できます。

上記のテキストは有効なJavaScriptコードです。不足しているセミコロンは自動的に挿入されます。

一見すると、かなり有望に見えます。 ASIメカニズムを使用すると、不要なセミコロンをスキップできます。 JavaScriptコードを小さくして読みやすくすることができます。

ASIによって作成された、小さいながらも厄介なトラップが1つあります。改行がreturnと返される式return \n expressionの間にある場合、ASIは改行の前にセミコロンを自動的に挿入しますreturn; \n expression

return;ステートメントを持つ関数内とはどういう意味ですか?この関数はundefinedを返します。 ASIのメカニズムの詳細がわからない場合、予期せず返されるundefinedは誤解を招く恐れがあります。

たとえば、getPrimeNumbers()呼び出しの戻り値を調べてみましょう。

returnステートメントと配列リテラル式の間に改行があります。 JavaScriptは、returnの後にセミコロンを自動的に挿入し、コードを次のように解釈します。

ステートメントreturn;は、関数getPrimeNumbers()が期待される配列の代わりにundefinedを返すようにします。

この問題は、returnと配列リテラルの間の改行を削除することで解決されます:

このような状況を回避するために、自動セミコロン挿入がどのように機能するかを正確に調査することをお勧めします。

もちろん、returnと返された式の間に改行を入れないでください。

2.5void演算子

void <expression>は式を評価し、結果に関係なくundefinedを返します評価の。

void演算子の1つの使用例は、式の評価をundefined、評価の副作用に依存します。

3。配列で未定義

範囲外のインデックスを持つ配列要素にアクセスすると、undefinedが表示されます。

colors配列には3つの要素があるため、有効なインデックスは01、および2

インデックス5-1には配列要素がないため、アクセサーcolorscolorsundefinedです。

JavaScriptでは、いわゆるスパース配列に遭遇する可能性があります。これらはギャップのある配列です。つまり、一部のインデックスでは、要素が定義されていません。

スパース配列内でギャップ(別名空のスロット)にアクセスすると、undefinedも取得されます。

次の例では、スパース配列を生成し、それらの空のスロットにアクセスしようとします。

sparse1は、最初の引数が数値のコンストラクター。3つの空のスロットがあります。

sparse2は、2番目の要素が欠落している配列リテラルを使用して作成されます。

これらのスパース配列のいずれかで、空のスロットにアクセスすると、undefinedと評価されます。

配列を操作するときは、undefinedを回避するために、必ず有効な配列インデックスを使用し、スパース配列が作成されないようにしてください。

4。未定義とnullの違い

undefinednull?両方の特別な値は、空の状態を意味します。

undefinedはまだ初期化されていない変数の値を表し、nullは、オブジェクトが意図的に存在しないことを表します。

いくつかの例で違いを調べてみましょう。

変数numberが定義されていますただし、初期値は割り当てられていません:

number変数はundefinedで、初期化されていない変数を示します。

存在しないオブジェクトプロパティにアクセスすると、同じ初期化されていない概念が発生します。

div id = “0a5d62ecec”> プロパティがobjに存在しない場合、JavaScriptはobj.lastNameundefined

反対側では、変数がオブジェクトを期待していることがわかります。ただし、何らかの理由で、オブジェクトをインスタンス化できません。このような場合、nullはオブジェクトが見つからないことを示す意味のある指標です。

たとえば、clone()は次のような関数です。プレーンなJavaScriptオブジェクトのクローンを作成します。この関数はオブジェクトを返すことが期待されています:

ただし、clone()は、オブジェクト以外の引数で呼び出される場合があります:15またはnull。このような場合、関数はクローンを作成できないため、null(欠落しているオブジェクトのインジケーター)を返します。

typeof演算子は、undefinednullを区別します。

また、厳密な品質演算子===は from null

5。結論

undefinedの存在は、次の使用を許可するJavaScriptの寛容な性質の結果です。

  • 初期化されていない変数
  • 存在しないオブジェクトのプロパティまたはメソッド
  • 配列要素にアクセスするための範囲外のインデックス
  • 何も返さない関数の呼び出し結果

undefinedと直接比較することは、上記の許可されているが推奨されていない方法に依存しているため、安全ではありません。

効率的な戦略は、次のような適切な習慣を適用することにより、コード内のundefinedキーワードの出現を少なくとも減らすことです。

  • 初期化されていない変数の使用を減らす
  • 変数のライフサイクルを短くし、使用元に近づけます
  • 可能な限り変数に初期値を割り当てます
  • favor const、それ以外の場合はlet
  • 重要でない関数パラメーターにデフォルト値を使用
  • プロパティを確認します安全でないオブジェクトが存在するか、デフォルトのプロパティで埋める
  • スパース配列の使用を避ける

JavaScriptに両方のundefinedおよびnull

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です