読み込み中...

【保存版】Undefinedを避けるJavaScriptの簡単な対処策28選

JavaScriptでUndefinedエラーに悩むプログラマを助ける解決策 JS
この記事は約26分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●JavaScriptでUndefinedエラーとは?

JavaScriptを学び始めたばかりの皆さん、こんにちは。

プログラミングを進めていく中で、きっと一度は「Undefined」というエラーメッセージを目にしたことがあるのではないでしょうか。

「Undefined」は、JavaScriptにおいて変数や関数、オブジェクトのプロパティが定義されていない状態を表します。

つまり、存在しないものにアクセスしようとした時に発生するエラーなのです。

○Undefinedの意味と発生シーン

具体的には、次のようなシーンでUndefinedエラーが発生します。

  1. 宣言していない変数を参照した時
  2. 関数の引数が足りない時
  3. オブジェクトの存在しないプロパティにアクセスした時
  4. 配列の範囲外の要素を参照した時

これらのシーンに遭遇すると、JavaScriptエンジンは「Undefined」というエラーを投げ、プログラムの実行を中断してしまいます。

○Undefinedが引き起こす問題

Undefinedエラーが発生すると、プログラムが予期せぬ動作をしたり、処理が途中で止まってしまったりと、様々な問題を引き起こします。

例えば、Webアプリケーションを開発している際に、ユーザー情報を表示する画面で、存在しないプロパティにアクセスしてしまうと、エラーが発生し、画面が真っ白になってしまうかもしれません。

これではユーザーに不便を感じさせてしまいますよね。

○Undefinedエラーの基本的な回避策

では、どうすればUndefinedエラーを回避できるのでしょうか。

基本的な対策として、次の点に気をつけましょう。

  1. 変数や関数、オブジェクトのプロパティを使う前に、必ず定義する
  2. 関数の引数はデフォルト値を設定するか、呼び出し時に必要な数だけ渡す
  3. オブジェクトのプロパティにアクセスする前に、存在チェックを行う
  4. 配列の添字は、配列の長さ以内の範囲で指定する

これらの点に注意しながらコードを書くことで、Undefinedエラーを未然に防ぐことができます。

でも、実際のプログラミングでは、もっと複雑なシーンでUndefinedエラーに遭遇することがあります。

そんな時のために、もっと実践的なUndefined回避テクニックを身につけておきたいですよね。

これから、変数宣言、条件分岐、関数、オブジェクト、配列操作など、様々なシーンで役立つUndefined回避策を、サンプルコードを交えながらご紹介していきます。

一緒にJavaScriptのUndefinedエラーを撃退していきましょう!

●変数宣言と初期化でのUndefined回避術

JavaScriptでUndefinedエラーを避けるために、まず変数宣言と初期化の時点で気をつけるべきポイントがあります。

変数を宣言する時は、できるだけ明示的に初期値を設定しておくことが大切です。

初期値を設定しないと、変数の値はundefinedになってしまいます。

○サンプルコード1:letとconstを使った変数宣言

ES2015以降のJavaScriptでは、変数宣言にletとconstキーワードを使うことが推奨されています。

let name = "John";
const age = 25;

letは値の再代入が可能な変数を宣言し、constは再代入不可能な定数を宣言します。

constで宣言する変数には必ず初期値を設定しなければなりません。

これにより、うっかりundefinedになることを防げます。

○サンプルコード2:変数のスコープに注意

JavaScriptでは、変数のスコープにも注意が必要です。

varキーワードで宣言した変数は関数スコープを持ちますが、letとconstで宣言した変数はブロックスコープを持ちます。

function example() {
  if (true) {
    var x = 5;
    let y = 10;
  }
  console.log(x); // 5
  console.log(y); // ReferenceError: y is not defined
}

変数のスコープを意識することで、意図しないundefinedエラーを避けることができます。

○サンプルコード3:グローバル変数の功罪

グローバル変数は、プログラムのどこからでもアクセスできる便利な変数ですが、乱用すると思わぬundefinedエラーを引き起こすことがあります。

let globalVar;
function func1() {
  globalVar = 10;
}
function func2() {
  console.log(globalVar); // undefined
}
func2();
func1();

グローバル変数は、宣言しただけでは初期化されないため、undefinedになります。

グローバル変数を使う場合は、確実に初期化されるタイミングを考える必要があります。

○サンプルコード4:undefinedの明示的代入

関数の引数や変数に、意図的にundefinedを代入することもあります。

function greet(name) {
  name = name || "Anonymous";
  console.log("Hello, " + name);
}
greet(); // Hello, Anonymous
greet(undefined); // Hello, Anonymous
greet("John"); // Hello, John

このように、undefinedを明示的に代入することで、引数が省略された場合のデフォルト値を設定することができます。

●条件分岐とUndefinedの正しい付き合い方

JavaScriptのコードを書いていると、if文などの条件分岐の中でundefinedとの遭遇は避けられません。

undefinedは、falsyな値として扱われるため、思わぬ判定結果になることがあります。

条件分岐の中で変数の値がundefinedかどうかを正しくチェックすることは、バグを防ぐために重要なスキルです。

○サンプルコード5:if文でのundefinedチェック

if文の条件式で、変数の値がundefinedかどうかを直接チェックする方法があります。

let x;
if (x === undefined) {
  console.log("xはundefinedです");
} else {
  console.log("xは定義されています");
}

このコードを実行すると、”xはundefinedです”と出力されます。

変数xには何も代入されていないため、undefinedと厳密に等しいかどうかを===演算子でチェックできます。

○サンプルコード6:三項演算子でのデフォルト値指定

三項演算子を使うと、undefinedの場合にデフォルト値を指定するコードを簡潔に書けます。

let name;
console.log(name ? name : "名無しさん");

このコードを実行すると、”名無しさん”と出力されます。

変数nameがundefinedの場合、三項演算子の条件式がfalseになるため、コロン以降の”名無しさん”が返されます。

○サンプルコード7:Null合体演算子の活用

ES2020で導入されたNull合体演算子(??)は、undefinedの対処に役立ちます。

let x;
console.log(x ?? "デフォルト値");

このコードを実行すると、”デフォルト値”と出力されます。

変数xがundefinedの場合、??演算子の右側の値が返されます。nullの場合も同様に右側の値が返されます。

○サンプルコード8:Optional Chainingでネストされたプロパティを安全にアクセス

ES2020で導入されたOptional Chaining(?.)は、ネストされたオブジェクトのプロパティにアクセスする際に、undefinedチェックを簡潔に書けます。

let user = {};
console.log(user?.address?.street);

このコードを実行すると、undefinedと出力されます。

userオブジェクトにaddressプロパティが存在しない場合、?.演算子によってundefinedが返されます。

undefinedのプロパティにアクセスしようとするとエラーになるのを防げます。

●関数とUndefinedの仲良くなるコツ

JavaScriptの関数は、引数や戻り値を介してundefinedと深く関わっています。

関数を定義する時も、呼び出す時も、undefinedとの付き合い方を意識することが大切です。

関数の引数にundefinedが渡されたり、関数がundefinedを返したりすると、思わぬバグに繋がることがあります。

そんな時は、デフォルト引数やreturn文を活用して、undefinedとうまく付き合っていきましょう。

○サンプルコード9:関数の引数にデフォルト値を設定

関数の引数にデフォルト値を設定しておくと、undefinedが渡された時の対策になります。

function greet(name = "ゲスト") {
  console.log(`こんにちは、${name}さん!`);
}

greet(); // こんにちは、ゲストさん! 
greet("太郎"); // こんにちは、太郎さん!

このコードでは、greet関数の引数nameにデフォルト値として”ゲスト”を設定しています。

引数が省略された場合や、undefinedが渡された場合は、自動的に”ゲスト”が使われます。

デフォルト引数を活用することで、undefinedによるエラーを防ぎ、より堅牢なコードを書くことができます。

○サンプルコード10:関数の戻り値を必ずreturn

関数内で値をreturnし忘れると、関数はundefinedを返してしまいます。

これは、意図しないバグの原因になることがあります。

function add(a, b) {
  a + b; // returnし忘れている
}

const result = add(1, 2);
console.log(result); // undefined

このコードでは、add関数内でa + bの計算結果をreturnしていません。

そのため、関数はundefinedを返し、resultにはundefinedが代入されてしまいます。

関数の最後では必ずreturn文を書くようにし、明示的に値を返すことを心がけましょう。

○サンプルコード11:アロー関数に気をつける

アロー関数は、通常の関数とは少し異なる挙動をします。

特に、引数が1つの場合は括弧を省略できるため、undefinedに注意が必要です。

const double = x => x * 2;

console.log(double(3)); // 6
console.log(double()); // NaN

このコードでは、double関数の引数xを省略して呼び出しています。

通常の関数であれば、xにはundefinedが渡されますが、アロー関数の場合は引数が1つの時に限り、括弧を省略できるため、xには何も渡されません。

その結果、x * 2はundefined * 2となり、NaNが返されてしまいます。

アロー関数を使う時は、引数の省略に気をつけましょう。

○サンプルコード12:コールバック関数の存在確認

コールバック関数を受け取る関数を作る時は、コールバック関数が渡されていることを確認することが大切です。

function calculate(a, b, callback) {
  if (callback) {
    return callback(a, b);
  }
  return a + b;
}

console.log(calculate(1, 2, (a, b) => a * b)); // 2
console.log(calculate(1, 2)); // 3

このコードでは、calculate関数の第3引数callbackが渡されているかどうかを、if文でチェックしています。

callbackが渡されていれば、callbackを呼び出して結果を返し、渡されていなければ、a + bを返します。

●オブジェクトとUndefinedの上手な関係

JavaScriptのオブジェクトは、プロパティの集合体です。

オブジェクトのプロパティにアクセスする時、そのプロパティが存在しない場合はundefinedが返されます。

オブジェクトとundefinedの関係を理解し、適切に処理することは、JavaScriptプログラミングにおいて非常に重要です。

undefinedによるエラーを回避し、安全にオブジェクトを操作する方法を身につけましょう。

○サンプルコード13:プロパティの存在確認

オブジェクトのプロパティが存在するかどうかを確認するには、in演算子やhasOwnProperty()メソッドを使います。

const user = { name: "John", age: 30 };

console.log("name" in user); // true
console.log("gender" in user); // false

console.log(user.hasOwnProperty("age")); // true
console.log(user.hasOwnProperty("email")); // false

このコードでは、in演算子とhasOwnProperty()メソッドを使って、userオブジェクトのプロパティの存在を確認しています。

“name”や”age”は存在するプロパティなのでtrue、”gender”や”email”は存在しないプロパティなのでfalseが返されます。

プロパティの存在を確認してから、プロパティにアクセスするようにすれば、undefinedによるエラーを防ぐことができます。

○サンプルコード14:プロパティアクセスにOptional Chainingを使う

ES2020で導入されたOptional Chaining(?.)を使うと、ネストされたオブジェクトのプロパティにアクセスする際に、undefinedチェックを簡潔に書けます。

const user = {
  name: "John",
  age: 30,
  address: {
    city: "New York",
    country: "USA"
  }
};

console.log(user?.address?.city); // "New York"
console.log(user?.address?.zipCode); // undefined

このコードでは、Optional Chainingを使って、userオブジェクトのaddressプロパティ、さらにその中のcityプロパティにアクセスしています。

addressプロパティが存在する場合は、cityプロパティの値が返されます。

zipCodeプロパティは存在しないため、undefinedが返されます。

Optional Chainingを使うことで、ネストされたプロパティにアクセスする際のundefinedチェックを、シンプルに書くことができます。

○サンプルコード15:分割代入でのデフォルト値

オブジェクトの分割代入時に、デフォルト値を指定することで、プロパティが存在しない場合のundefinedを回避できます。

const user = { name: "John", age: 30 };

const { name, age, gender = "Unknown" } = user;

console.log(name); // "John"
console.log(age); // 30
console.log(gender); // "Unknown"

このコードでは、userオブジェクトから、nameとageプロパティを分割代入しています。

genderプロパティは存在しないので、デフォルト値として”Unknown”を指定しています。

分割代入時にデフォルト値を設定することで、プロパティが存在しない場合でも、undefinedではなく指定したデフォルト値が使われます。

○サンプルコード16:hasOwnProperty()メソッドの使用

オブジェクトが特定のプロパティを持っているかどうかを確認するには、hasOwnProperty()メソッドを使います。

const user = { name: "John", age: 30 };

console.log(user.hasOwnProperty("name")); // true
console.log(user.hasOwnProperty("email")); // false

if (user.hasOwnProperty("age")) {
  console.log(`${user.name}は${user.age}歳です`);
} else {
  console.log(`${user.name}の年齢は不明です`);
}

このコードでは、hasOwnProperty()メソッドを使って、userオブジェクトがnameとemailプロパティを持っているかどうかを確認しています。

さらに、if文の中でhasOwnProperty()メソッドを使って、ageプロパティの有無に応じて、異なるメッセージを出力しています。

●配列操作とUndefinedのセーフティネット

JavaScriptの配列は、複数の値を順序付きで保持するデータ構造です。

配列の要素にアクセスする際に、存在しないインデックスを指定するとundefinedが返されます。

配列操作では、undefinedとの遭遇は避けられません。

しかし、適切な対策を講じることで、undefinedによるエラーを回避し、安全に配列を扱うことができます。

○サンプルコード17:配列の添字に注意

配列の要素にアクセスする際は、配列の長さを超えるインデックスを指定しないように注意しましょう。

const fruits = ["apple", "banana", "orange"];

console.log(fruits[0]); // "apple"
console.log(fruits[2]); // "orange"
console.log(fruits[3]); // undefined

このコードでは、fruitsという配列を定義し、インデックスを使って要素にアクセスしています。

インデックスが0から始まることに注意してください。

インデックス3は配列の長さを超えているため、undefinedが返されます。

配列の添字に注意し、必要に応じて長さのチェックを行うことが大切です。

○サンプルコード18:配列の長さチェック

配列の要素にアクセスする前に、配列の長さをチェックすることで、undefinedを回避できます。

const numbers = [1, 2, 3, 4, 5];

if (numbers.length > 3) {
  console.log(numbers[3]); // 4
}

if (numbers.length > 5) {
  console.log(numbers[5]); // 実行されない
}

このコードでは、if文を使って配列numbersの長さをチェックしています。

長さが3より大きい場合は、インデックス3の要素にアクセスし、4が出力されます。

一方、長さが5より大きい場合のif文は実行されません。これにより、存在しないインデックスへのアクセスを防いでいます。

配列の長さチェックは、undefinedによるエラーを未然に防ぐ効果的な方法です。

○サンプルコード19:map()やfilter()でのundefined要素のスキップ

配列のメソッドであるmap()やfilter()を使う際は、undefinedの要素を適切に処理する必要があります。

const numbers = [1, 2, undefined, 4, 5];

const doubledNumbers = numbers.map(num => num * 2);
console.log(doubledNumbers); // [2, 4, NaN, 8, 10]

const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]

このコードでは、numbersという配列の中にundefinedの要素が含まれています。

map()メソッドを使って、各要素を2倍した新しい配列doubledNumbersを作成しています。

undefinedに2をかけるとNaNになるため、doubledNumbersにはNaNが含まれています。

一方、filter()メソッドを使って、偶数の要素だけを抽出した新しい配列evenNumbersを作成しています。

undefinedは偶数ではないため、evenNumbersにはundefinedが含まれません。

map()やfilter()を使う時は、undefinedの要素がどのように処理されるかを理解しておくことが重要です。

○サンプルコード20:配列のネストに気をつける

配列がネストされている場合、undefinedとの遭遇リスクが高くなります。

const matrix = [
  [1, 2],
  [3, 4],
  [5]
];

console.log(matrix[0][0]); // 1
console.log(matrix[1][1]); // 4
console.log(matrix[2][1]); // undefined

このコードでは、matrixという2次元配列を定義しています。

matrix[0][0]は1、matrix[1][1]は4とアクセスできますが、matrix[2][1]はundefinedになります。

これは、matrix[2]が[5]という1つの要素しか持たない配列であるためです。

●非同期処理とUndefinedの付き合い

JavaScriptでは、非同期処理を使ってサーバーとの通信やファイルの読み書きなどを行うことが一般的です。

しかし、非同期処理ではundefinedとの遭遇リスクが高くなります。

コールバック関数やPromise、async/awaitを使った非同期処理では、処理の完了を待たずに次の処理が実行されるため、undefinedが返されることがあります。

非同期処理とundefinedの付き合い方を理解し、適切にエラーハンドリングを行うことが重要です。

○サンプルコード21:コールバック関数でのエラーハンドリング

コールバック関数を使った非同期処理では、エラーファーストコールバックパターンを使ってエラーハンドリングを行います。

function asyncOperation(callback) {
  setTimeout(() => {
    // エラーが発生した場合
    if (Math.random() < 0.5) {
      callback(new Error("非同期エラー"));
    } else {
      callback(null, "非同期処理の結果");
    }
  }, 1000);
}

asyncOperation((error, result) => {
  if (error) {
    console.error(error);
    return;
  }
  console.log(result);
});

このコードでは、asyncOperation関数内で非同期処理をシミュレートするために、setTimeout関数を使っています。

50%の確率でエラーを発生させ、コールバック関数の第1引数にエラーオブジェクトを渡しています。

コールバック関数内では、第1引数のエラーをチェックし、エラーが存在する場合はエラー処理を行い、そうでない場合は結果を処理します。

こうすることで、undefinedによるエラーを適切にハンドリングできます。

○サンプルコード22:Promiseのエラー処理

Promiseを使った非同期処理では、catch()メソッドを使ってエラー処理を行います。

function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // エラーが発生した場合
      if (Math.random() < 0.5) {
        reject(new Error("非同期エラー"));
      } else {
        resolve("非同期処理の結果");
      }
    }, 1000);
  });
}

asyncOperation()
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error(error);
  });

このコードでは、asyncOperation関数内でPromiseを使った非同期処理を行っています。

エラーが発生した場合は、reject()関数を呼び出してエラーを通知し、そうでない場合はresolve()関数を呼び出して結果を通知します。

Promiseのthen()メソッドで結果を処理し、catch()メソッドでエラー処理を行うことで、undefinedによるエラーをハンドリングできます。

○サンプルコード23:async/awaitでのtry-catch文

async/awaitを使った非同期処理では、try-catch文を使ってエラー処理を行います。

function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // エラーが発生した場合
      if (Math.random() < 0.5) {
        reject(new Error("非同期エラー"));
      } else {
        resolve("非同期処理の結果");
      }
    }, 1000);
  });
}

async function main() {
  try {
    const result = await asyncOperation();
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

main();

このコードでは、asyncOperation関数内でPromiseを使った非同期処理を行っています。

main関数内では、await演算子を使ってasyncOperation関数の完了を待ち、結果を変数resultに代入しています。

○サンプルコード24:非同期データのデフォルト値

非同期処理の結果がundefinedの場合に備えて、デフォルト値を用意しておくことも大切です。

function fetchUserData(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // ユーザーデータが存在しない場合
      if (Math.random() < 0.5) {
        resolve(undefined);
      } else {
        resolve({ id: userId, name: "John" });
      }
    }, 1000);
  });
}

async function main() {
  const userId = 123;
  const userData = await fetchUserData(userId);
  const name = userData?.name ?? "名無しのユーザー";
  console.log(name);
}

main();

このコードでは、fetchUserData関数内で非同期処理をシミュレートし、50%の確率でundefinedを返すようにしています。

main関数内では、fetchUserData関数の結果をuserDataに代入し、Optional Chaining演算子(?.)とNullish Coalescing演算子(??)を使って、nameプロパティにアクセスしています。

●型変換とUndefinedの裏側

JavaScriptでは、変数の型が動的に決定されるため、型変換が暗黙的に行われることがあります。

この暗黙的な型変換の過程で、undefinedが関わってくることがあるのです。

undefinedは、数値や文字列、真偽値など、他のデータ型に変換される際に、予期せぬ結果をもたらすことがあります。

型変換とundefinedの裏側を理解することで、より安全なJavaScriptプログラミングが可能になると思います。

○サンプルコード25:==と===の使い分け

JavaScriptでは、等価演算子として==と===の2種類があります。

==は暗黙的な型変換を行った上で等価性を比較するのに対し、===は型変換を行わずに厳密に等価性を比較します。

console.log(undefined == null); // true
console.log(undefined === null); // false

console.log(undefined == false); // false
console.log(undefined == true); // false

console.log(undefined == 0); // false
console.log(undefined == ''); // false

このコードを実行すると、undefinedとnullは==で比較した場合にtrueになりますが、===で比較するとfalseになります。

また、undefinedは、真偽値や数値、空文字列とは==でも等価にならないことがわかります。

==を使う場合は、暗黙的な型変換に注意が必要ですが、===を使えば型変換によるトラブルを避けられます。

○サンプルコード26:真偽値への変換に注意

JavaScriptでは、if文やwhile文の条件式、論理演算子などで、真偽値への暗黙的な型変換が行われます。
undefinedは、真偽値に変換されるとfalseとして扱われます。

if (undefined) {
  console.log('このコードは実行されません');
}

const value = undefined;
console.log(Boolean(value)); // false

console.log(undefined || 'デフォルト値'); // 'デフォルト値'
console.log(undefined && '値が存在する'); // undefined

このコードを実行すると、undefinedは真偽値に変換されるとfalseになるため、if文の中身は実行されません。

Boolean()関数を使って明示的にundefinedを真偽値に変換すると、falseが返されます。

また、論理演算子の||や&&でundefinedを扱う場合も、真偽値への変換に注意が必要です。

||はundefinedがfalseと見なされるため、右辺の値が返されます。

&&はundefinedがfalseと見なされるため、undefinedが返されます。

真偽値への変換では、undefinedがfalseとして扱われることを理解しておきましょう。

○サンプルコード27:Number()やparseInt()での変換時のundefined

Number()関数やparseInt()関数を使って文字列を数値に変換する際、undefinedを渡すとNaNが返されます。

console.log(Number(undefined)); // NaN
console.log(parseInt(undefined)); // NaN

const value = undefined;
console.log(value + 10); // NaN

このコードを実行すると、Number()関数やparseInt()関数にundefinedを渡すと、NaNが返されることがわかります。

また、undefinedに数値を加算しようとしても、NaNになってしまいます。

数値への変換では、undefinedがNaNをもたらすことに気をつけましょう。

○サンプルコード28:JSON.parse()とundefined

JSON.parse()メソッドを使ってJSONをパースする際、undefinedが含まれていると、シンタックスエラーが発生します。

const json = '{"name": "John", "age": undefined}';
const obj = JSON.parse(json); // SyntaxError: Unexpected token u in JSON at position 20

このコードを実行すると、JSONの中にundefinedが含まれているため、JSON.parse()メソッドがシンタックスエラーを投げます。

JSONではundefinedは有効な値ではないため、パースできないのです。

JSON.parse()メソッドを使う際は、JSONの中にundefinedが含まれていないか確認しましょう。

まとめ

この記事を通してundefinedとの付き合い方が少しわかってきたのではないでしょうか。

この記事で紹介した28のundefined回避テクニックを、ぜひ実践に活かしてみてください。

サンプルコードを参考に、自分なりのundefined対策を編み出していきましょう。

最初は大変かもしれませんが、undefinedを怖がらないようになる日が必ず来ます。