JavaScriptのNaNとは?初心者向けにエラー原因と対処法を解説

JavaScriptのNaN(Not a Number)で困る初心者必見!NaNが発生する原因と実践的な対処法を解説。数値計算のエラー回避方法から検出方法まで、コード例付きで詳しく説明します。

Learning Next 運営
30 分で読めます

みなさん、JavaScriptで計算をしていて困ったことはありませんか?

「計算結果がNaNって表示されるけど、これは何?」「なんで急に数値じゃなくなるの?」

こんな疑問を持っている初心者の方、きっと多いですよね。 実は、**NaN(Not a Number)**は数値計算でよく遭遇する特別な値なんです。

この記事では、JavaScriptのNaNについて初心者向けに分かりやすく解説します。 NaNが発生する原因から実践的な対処法まで、一緒に学んでいきましょう!

NaNって一体何?基本を知ろう

NaNの正体

NaNは「Not a Number」の略で、「数値ではない」という意味です。

簡単に言うと、「数値として無効な計算結果」を表すJavaScriptの特別な値なんです。 例えば、文字列を数値に変換しようとして失敗した時などに出てきます。

console.log(0 / 0); // NaN
console.log(Math.sqrt(-1)); // NaN
console.log(parseInt("hello")); // NaN

このように、数学的に無効な計算をした時にNaNが返されます。

NaNの不思議な特徴

NaNには、他の値にはない面白い特徴があります。

console.log(typeof NaN); // "number"
console.log(NaN === NaN); // false
console.log(NaN == NaN); // false

驚くことに、NaNは数値型なのに、自分自身と等しくない唯一の値なんです。 ちょっと不思議ですよね。

でも大丈夫です!これから一つずつ理解していきましょう。

NaNかどうかを調べる方法

NaNかどうかを判定するには、専用の関数を使います。

let result = 0 / 0;
console.log(isNaN(result)); // true
console.log(Number.isNaN(result)); // true
// 違いを確認してみましょう
console.log(isNaN("hello")); // true(文字列も数値でないためtrue)
console.log(Number.isNaN("hello")); // false(厳密にNaNのみをチェック)

Number.isNaN()の方がより厳密で、実際にNaNかどうかのみを判定します。 基本的には、こちらを使うのがおすすめです。

どんな時にNaNが発生する?よくある原因

文字列を数値に変換する時

初心者の方が最もよく遭遇するパターンです。

// 数値に変換できない文字列
console.log(parseInt("hello")); // NaN
console.log(parseFloat("world")); // NaN
console.log(Number("abc")); // NaN

文字列から数値への変換に失敗すると、NaNが返されます。

解決方法を見てみましょう。

function safeParseInt(str) {
if (typeof str === "string" && /^\d+$/.test(str)) {
return parseInt(str);
}
return 0; // デフォルト値
}
console.log(safeParseInt("123")); // 123
console.log(safeParseInt("hello")); // 0

事前に文字列が数値として有効かどうかをチェックしています。 これで安全に変換できますね。

無効な数学計算をした時

数学的に定義されていない計算を行った場合です。

// 無効な計算
console.log(0 / 0); // NaN
console.log(Infinity - Infinity); // NaN
console.log(Math.sqrt(-1)); // NaN

このような計算は数学的に無効なので、NaNが返されます。

事前にチェックする方法があります。

function safeDivision(a, b) {
if (b === 0) {
console.warn("0で割ろうとしています");
return null;
}
return a / b;
}
console.log(safeDivision(10, 2)); // 5
console.log(safeDivision(10, 0)); // null

計算を行う前に、有効な値かどうかを確認するのが大切です。

undefinedやnullと計算した時

undefined や null を数値として計算すると、問題が起きることがあります。

let value1; // undefined
let value2 = null;
console.log(value1 + 10); // NaN
console.log(value2 + 10); // 10(nullは0として扱われる)
console.log(value1 * 5); // NaN

undefinedが関わる計算では、NaNが発生しやすいです。

安全に処理する方法があります。

function safeCalculation(value, defaultValue = 0) {
const num = Number(value);
return Number.isNaN(num) ? defaultValue : num;
}
console.log(safeCalculation(undefined) + 10); // 10
console.log(safeCalculation("abc", 0) + 10); // 10

計算に使用する値が数値として有効かを確認しましょう。

配列やオブジェクトと計算した時

配列やオブジェクトを数値として扱おうとした場合です。

let arr = [1, 2, 3];
let obj = { value: 10 };
console.log(arr + 5); // "1,2,35"(文字列として連結)
console.log(arr * 2); // NaN
console.log(obj * 2); // NaN

配列やオブジェクトとの乗算では、NaNが発生します。

適切に値を抽出する方法があります。

function getNumericValue(value) {
if (typeof value === "number") {
return value;
} else if (Array.isArray(value) && value.length === 1) {
return Number(value[0]);
} else if (typeof value === "object" && value.value !== undefined) {
return Number(value.value);
}
return 0;
}
console.log(getNumericValue([5]) * 2); // 10
console.log(getNumericValue({value: 15}) * 2); // 30

配列やオブジェクトから数値を取得する場合は、適切な方法で値を抽出しましょう。

NaNを見つけて処理する方法

2つの判定関数の違いを理解しよう

isNaN()Number.isNaN()の違いを理解することが重要です。

// isNaN():数値でない場合にtrue
console.log(isNaN(NaN)); // true
console.log(isNaN("hello")); // true
console.log(isNaN(undefined)); // true
console.log(isNaN("123")); // false(文字列だが数値に変換可能)
// Number.isNaN():厳密にNaNのみでtrue
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("hello")); // false
console.log(Number.isNaN(undefined)); // false
console.log(Number.isNaN("123")); // false

用途に応じて適切な判定関数を選択しましょう。

実用的な判定関数を作ってみます。

function isValidNumber(value) {
return typeof value === "number" && !Number.isNaN(value);
}
console.log(isValidNumber(10)); // true
console.log(isValidNumber(NaN)); // false
console.log(isValidNumber("10")); // false

この関数を使うことで、有効な数値かどうかを簡単に判定できます。

配列からNaNを取り除こう

配列からNaNを除去する方法です。

let numbers = [1, NaN, 2, NaN, 3, "hello", 4];
// 方法1:Number.isNaN()を使用
let validNumbers1 = numbers.filter(num => {
const converted = Number(num);
return !Number.isNaN(converted);
});
console.log(validNumbers1); // [1, 2, 3, 4]

より厳密な方法もあります。

// 方法2:型チェックも含める
let validNumbers2 = numbers.filter(num => {
return typeof num === "number" && !Number.isNaN(num);
});
console.log(validNumbers2); // [1, 2, 3, 4]

関数として作っておくと便利です。

function filterValidNumbers(arr) {
return arr.filter(item => {
const num = Number(item);
return !Number.isNaN(num) && isFinite(num);
});
}
console.log(filterValidNumbers([1, NaN, "2", Infinity, 3])); // [1, 2, 3]

データ処理では、NaNの除去は重要な操作です。

NaNを適切な値に置き換えよう

NaNを他の値に置き換える方法です。

function replaceNaN(value, replacement = 0) {
return Number.isNaN(value) ? replacement : value;
}
// 使用例
let results = [10, NaN, 20, NaN, 30];
let cleanResults = results.map(val => replaceNaN(val, 0));
console.log(cleanResults); // [10, 0, 20, 0, 30]

より高度な置き換え方法もあります。

function smartReplaceNaN(arr, strategy = "zero") {
return arr.map((val, index) => {
if (!Number.isNaN(val)) return val;
switch (strategy) {
case "zero":
return 0;
case "average":
const validValues = arr.filter(v => !Number.isNaN(v));
return validValues.reduce((sum, v) => sum + v, 0) / validValues.length;
case "previous":
for (let i = index - 1; i >= 0; i--) {
if (!Number.isNaN(arr[i])) return arr[i];
}
return 0;
default:
return 0;
}
});
}
let data = [10, NaN, 20, NaN, 30];
console.log(smartReplaceNaN(data, "zero")); // [10, 0, 20, 0, 30]
console.log(smartReplaceNaN(data, "average")); // [10, 20, 20, 20, 30]

用途に応じて適切な置き換え戦略を選択できます。

実際に使える対処法

安全な計算関数を作ろう

エラーを防ぐ計算関数を作成してみましょう。

// 安全な加算
function safeAdd(a, b) {
const numA = Number(a);
const numB = Number(b);
if (Number.isNaN(numA) || Number.isNaN(numB)) {
console.warn("無効な値が含まれています");
return 0;
}
return numA + numB;
}
console.log(safeAdd(10, 20)); // 30
console.log(safeAdd("hello", 10)); // 0

除算も安全に処理できます。

function safeDivide(a, b) {
const numA = Number(a);
const numB = Number(b);
if (Number.isNaN(numA) || Number.isNaN(numB)) {
console.warn("無効な値が含まれています");
return null;
}
if (numB === 0) {
console.warn("0で割ろうとしています");
return null;
}
return numA / numB;
}
console.log(safeDivide(10, 2)); // 5
console.log(safeDivide(10, 0)); // null

安全な計算関数を使うことで、NaNによるエラーを防げます。

ユーザー入力をチェックしよう

フォームからの入力を安全に処理する方法です。

function validateNumericInput(input) {
// 空文字チェック
if (input === "" || input == null) {
return { isValid: false, error: "値が入力されていません" };
}
// 数値変換
const num = Number(input);
// NaNチェック
if (Number.isNaN(num)) {
return { isValid: false, error: "有効な数値を入力してください" };
}
// 無限大チェック
if (!isFinite(num)) {
return { isValid: false, error: "有効な範囲の数値を入力してください" };
}
return { isValid: true, value: num };
}
// 使用例
console.log(validateNumericInput("123")); // { isValid: true, value: 123 }
console.log(validateNumericInput("hello")); // { isValid: false, error: "有効な数値を入力してください" }

ユーザー入力の検証は、NaNエラーを防ぐ重要な手段です。

フォーム処理の例も見てみましょう。

function processForm(formData) {
const ageValidation = validateNumericInput(formData.age);
const salaryValidation = validateNumericInput(formData.salary);
if (!ageValidation.isValid) {
alert("年齢: " + ageValidation.error);
return false;
}
if (!salaryValidation.isValid) {
alert("給与: " + salaryValidation.error);
return false;
}
// 処理続行
console.log("年齢:", ageValidation.value);
console.log("給与:", salaryValidation.value);
return true;
}

このように事前にチェックすることで、安全にフォームデータを処理できます。

配列データを安全に集計しよう

配列の数値データを安全に集計する方法です。

function safeSum(arr) {
return arr
.map(val => Number(val))
.filter(val => !Number.isNaN(val))
.reduce((sum, val) => sum + val, 0);
}
function safeAverage(arr) {
const validNumbers = arr
.map(val => Number(val))
.filter(val => !Number.isNaN(val));
if (validNumbers.length === 0) {
return 0;
}
return validNumbers.reduce((sum, val) => sum + val, 0) / validNumbers.length;
}
// 使用例
let data = [10, "20", NaN, "hello", 30, null, 40];
console.log(safeSum(data)); // 100
console.log(safeAverage(data)); // 25

最大値を求める関数も作れます。

function safeMax(arr) {
const validNumbers = arr
.map(val => Number(val))
.filter(val => !Number.isNaN(val));
if (validNumbers.length === 0) {
return null;
}
return Math.max(...validNumbers);
}
console.log(safeMax(data)); // 40

データ集計では、無効な値を適切にフィルタリングすることが重要です。

NaNの問題を見つけてデバッグしよう

NaN発生源を特定する方法

デバッグ時にNaNの発生源を特定する方法です。

function debugCalculation(a, b, operation) {
console.log(`計算: ${a} ${operation} ${b}`);
console.log(`a の型: ${typeof a}, 値: ${a}`);
console.log(`b の型: ${typeof b}, 値: ${b}`);
let result;
switch (operation) {
case "+":
result = Number(a) + Number(b);
break;
case "-":
result = Number(a) - Number(b);
break;
case "*":
result = Number(a) * Number(b);
break;
case "/":
result = Number(a) / Number(b);
break;
default:
result = NaN;
}
console.log(`結果: ${result}`);
if (Number.isNaN(result)) {
console.error("NaNが発生しました!");
console.log("可能な原因:");
if (Number.isNaN(Number(a))) console.log("- 第1引数が数値に変換できません");
if (Number.isNaN(Number(b))) console.log("- 第2引数が数値に変換できません");
if (operation === "/" && Number(b) === 0) console.log("- 0で割り算を実行しています");
}
return result;
}
// 使用例
debugCalculation("hello", 10, "+"); // NaN発生源を特定
debugCalculation(10, 0, "/"); // 0除算を検出

デバッグ時は、値の型と内容を詳しく確認することが重要です。

エラーログを記録しよう

NaN発生時の詳細ログを記録する仕組みです。

class CalculationLogger {
constructor() {
this.logs = [];
}
calculate(a, b, operation) {
const logEntry = {
timestamp: new Date(),
inputs: { a, b, operation },
success: false,
result: null,
error: null
};
try {
const numA = Number(a);
const numB = Number(b);
if (Number.isNaN(numA)) {
throw new Error(`第1引数 '${a}' は数値に変換できません`);
}
if (Number.isNaN(numB)) {
throw new Error(`第2引数 '${b}' は数値に変換できません`);
}
let result;
switch (operation) {
case "+": result = numA + numB; break;
case "-": result = numA - numB; break;
case "*": result = numA * numB; break;
case "/":
if (numB === 0) {
throw new Error("0で割り算はできません");
}
result = numA / numB;
break;
default:
throw new Error(`不明な演算子: ${operation}`);
}
logEntry.success = true;
logEntry.result = result;
} catch (error) {
logEntry.error = error.message;
console.error("計算エラー:", error.message);
}
this.logs.push(logEntry);
return logEntry.result;
}
getErrorLogs() {
return this.logs.filter(log => !log.success);
}
}
// 使用例
const calculator = new CalculationLogger();
calculator.calculate(10, 5, "+"); // 正常
calculator.calculate("hello", 5, "+"); // エラー
calculator.calculate(10, 0, "/"); // エラー
console.log("エラーログ:", calculator.getErrorLogs());

詳細なログを記録することで、問題の原因を特定しやすくなります。

NaNを防ぐベストプラクティス

型安全な関数を作ろう

TypeScriptライクな型チェックを含む関数です。

function createTypeSafeCalculator() {
function validateNumber(value, paramName) {
if (typeof value !== "number") {
throw new TypeError(`${paramName}は数値である必要があります`);
}
if (Number.isNaN(value)) {
throw new Error(`${paramName}はNaNです`);
}
if (!isFinite(value)) {
throw new Error(`${paramName}は有限値である必要があります`);
}
}
return {
add(a, b) {
validateNumber(a, "第1引数");
validateNumber(b, "第2引数");
return a + b;
},
divide(a, b) {
validateNumber(a, "第1引数");
validateNumber(b, "第2引数");
if (b === 0) {
throw new Error("0で割ることはできません");
}
return a / b;
}
};
}
// 使用例
const calc = createTypeSafeCalculator();
try {
console.log(calc.add(10, 20)); // 30
console.log(calc.divide(10, 0)); // エラー
} catch (error) {
console.error(error.message);
}

型安全な関数を作成することで、実行時エラーを防げます。

設定値を安全に管理しよう

アプリケーション設定でNaNを防ぐ方法です。

class ConfigManager {
constructor() {
this.defaults = {
maxRetries: 3,
timeout: 5000,
pageSize: 10,
threshold: 0.5
};
}
set(key, value) {
if (key in this.defaults && typeof this.defaults[key] === "number") {
const num = Number(value);
if (Number.isNaN(num)) {
console.warn(`設定値 '${key}' に無効な値 '${value}' が指定されました。デフォルト値を使用します。`);
return;
}
this.defaults[key] = num;
}
}
validate() {
const errors = [];
for (let [key, value] of Object.entries(this.defaults)) {
if (typeof value === "number" && Number.isNaN(value)) {
errors.push(`設定値 '${key}' がNaNです`);
}
}
return errors;
}
}
// 使用例
const config = new ConfigManager();
config.set("maxRetries", "hello"); // 警告が表示される
config.set("timeout", "3000"); // 正常に設定される
console.log(config.validate()); // エラーチェック

設定値の管理では、無効な値を早期に検出することが重要です。

まとめ

JavaScriptのNaNについて詳しく学んできました。

NaNの主な発生原因をおさらいしましょう。

  • 無効な数値変換(文字列から数値)
  • 無効な数学的演算(0/0、√-1など)
  • undefinedやnullとの計算
  • 配列・オブジェクトとの数値計算

対処法の重要なポイントはこちらです。

  • **Number.isNaN()**を使った厳密な判定
  • 事前の型チェックと値検証
  • 安全な計算関数の作成
  • 適切なエラーハンドリング

予防策も忘れずに実践しましょう。

  • ユーザー入力の検証
  • デフォルト値の設定
  • 型安全な関数の実装
  • 詳細なエラーログの記録

NaNは最初は理解が難しいかもしれません。 でも適切な知識と対策により、安全で信頼性の高いJavaScriptプログラムが作成できるようになります。

数値計算を扱う際は、常にNaNの可能性を考慮しましょう。 適切な検証と処理を行うことで、ユーザーにとって使いやすいプログラムを作ることができます。

ぜひ今日から、NaNを恐れずに数値計算のプログラムに挑戦してみてくださいね!

関連記事