JavaScript では、なぜ はisNaN(" ")
に評価されるのにfalse
、 にisNaN(" x")
評価されるのtrue
ですか?
テキスト入力フィールドで数値演算を実行しており、フィールドがnull
、""
、またはであるかどうかを確認していますNaN
。誰かがフィールドにいくつかのスペースを入力すると、私の検証は 3 つすべてで失敗し、なぜisNaN
チェックを通過するのか混乱します。
JavaScript では、なぜ はisNaN(" ")
に評価されるのにfalse
、 にisNaN(" x")
評価されるのtrue
ですか?
テキスト入力フィールドで数値演算を実行しており、フィールドがnull
、""
、またはであるかどうかを確認していますNaN
。誰かがフィールドにいくつかのスペースを入力すると、私の検証は 3 つすべてで失敗し、なぜisNaN
チェックを通過するのか混乱します。
これは驚くべきことかもしれませんし、そうでないかもしれませんが、JavaScriptエンジンの奇抜さを示すためのテストコードを次に示します。
document.write(isNaN("")) // false
document.write(isNaN(" ")) // false
document.write(isNaN(0)) // false
document.write(isNaN(null)) // false
document.write(isNaN(false)) // false
document.write("" == false) // true
document.write("" == 0) // true
document.write(" " == 0) // true
document.write(" " == false) // true
document.write(0 == false) // true
document.write(" " == "") // false
つまり、これは
" " == 0 == false
と
"" == 0 == false
しかし
"" != " "
楽しむ :)
理解を深めるには、 43ページのEcma-Script仕様pdf「文字列型に適用されるToNumber」を開いてください。
文字列に数値構文があり、空白文字をいくつでも含めることができる場合は、数値型に変換できます。空の文字列は0と評価されます。また、文字列「Infinity」は次のようになります。
isNaN('Infinity'); // false
MDN
直面している問題の理由から
isNaN 関数への引数が Number 型でない場合、値はまず Number に強制変換されます。結果の値は、NaN かどうかを判断するためにテストされます。
等号の NaN 比較もカバーする次の包括的な回答を確認することをお勧めします。
アントニオ・ヘイリーの非常に支持され、受け入れられた回答parseInt
here は、このプロセスが JavaScript の機能を通過するという誤った仮定をしています。
文字列にparseIntを使用できます...結果はisNANに失敗するはずです。
このステートメントは、次の文字列で簡単に反証できます"123abc"
。
parseInt("123abc") // 123 (a number...
isNaN("123abc") // true ...which is not a number)
これにより、JavaScript の関数がnumber としてparseInt
返すことがわかりますが、関数はそれが数値ではないことを示しています。"123abc"
123
isNaN
"123abc"
ECMAScript-262 は、セクション 18.2.3isNaN
でチェックがどのように機能するかを定義します。
18.2.3
isNaN
(数値)
isNaN
関数は固有%isNaN%
オブジェクトです。isNaN
関数が 1 つの引数番号で呼び出されると、次の手順が実行されます。
- しましょう
num
か?ToNumber(number)
.- である場合
num
はNaN
、 を返しtrue
ます。- それ以外の場合は、 を返し
false
ます。
それが参照する関数は、 ECMAScript-262 のセクション 7.1.3ToNumber
でも定義されています。ここでは、この関数に渡された文字列を JavaScript がどのように処理するかを説明します。
質問に示されている最初の例は、空白文字のみを含む文字列です。このセクションでは、次のように述べています。
空である
StringNumericLiteral
か空白のみを含む は に変換され+0
ます。
したがって" "
、例の文字列は、数値である に変換され+0
ます。
同じセクションには次のようにも記載されています。
String
文法がを の展開として解釈できない場合、StringNumericLiteral
の結果はToNumber
ですNaN
。
そのセクションに含まれるすべてのチェックを引用しないと" x"
、質問に示されている例は、として解釈できないため、上記の条件に該当しますStringNumericLiteral
。" x"
したがって、 に変換されNaN
ます。
正確なisNumber関数を実装したい場合は、Javascriptから実装する1つの方法があります。DouglasCrockfordによるGoodParts [105ページ]
var isNumber = function isNumber(value) {
return typeof value === 'number' &&
isFinite(value);
}
この関数isNaN("")
は、文字列から数値への型変換を実行します
ECMAScript 3-5 では、typeof 演算子の次の戻り値が定義されています。
関数本体でテストをラップすることをお勧めします。
function isNumber (s) {
return typeof s == 'number'? true
: typeof s == 'string'? (s.trim() === ''? false : !isNaN(s))
: (typeof s).match(/object|function/)? false
: !isNaN(s)
}
この関数は、変数の型をテストするためのものではなく、強制された値をテストします。たとえば、ブール値と文字列は強制的に数値に変換されるため、この関数を次のように呼び出したい場合があります。isNumberCoerced()
stringとnumber以外の型をテストする必要がない場合は、次のスニペットを条件の一部として使用できます。
if (!isNaN(s) && s.toString().trim()!='') // 's' can be boolean, number or string
alert("s is a number")
整数かどうかを適切にチェックしたい場合は、次の関数を使用することをお勧めします。
function isInteger(s)
{
return Math.ceil(s) == Math.floor(s);
}
これは、非数値を数値型に強制するためisNaN(" ")
、グローバル関数の紛らわしい動作の一部です。isNaN
MDNから:
関数仕様の最も初期のバージョン以来、
isNaN
数値以外の引数に対するその動作は混乱を招きました。関数への引数がisNaN
Number 型でない場合、値はまず Number に強制変換されます。次に、結果の値をテストして、それが であるかどうかを判断しますNaN
。したがって、数値型に強制されたときに有効な非 NaN 数値になる非数値 (特に、強制されたときに数値が 0 または 1 になる空の文字列とブール型プリミティブ) の場合、"false" の戻り値は予期しないものになる可能性があります。たとえば、空の文字列は確かに「数値ではありません」。
Number.isNaN
また、ECMAScript 6 では、 MDN によると次のメソッドも存在することに注意してください。
isNaN()
グローバル関数と比較してNumber.isNaN()
、パラメーターを強制的に数値に変換するという問題は発生しません。これは、通常は に変換されるNaN
が、実際には と同じ値ではない値を安全に渡すことができるようになったことを意味しますNaN
。NaN
これは、数値型の値のみがを返すことも意味しますtrue
。
残念ながら:
ECMAScript 6Number.isNaN
メソッドにも独自の問題があります。ブログ記事の修正、醜い JavaScript と ES6 NaN の問題で概説されています。
この問題を解決するために、この簡単な小さな関数を書きました。
function isNumber(val) {
return (val != undefined && val != null && val.toString().length > 0 && val.toString().match(/[^0-9\.\-]/g) == null);
};
数値 (0 ~ 9) でなく、'-' または '.' でなく、未定義、null、または空ではない文字を単純にチェックし、一致するものがない場合は true を返します。:)
他の人が説明したように、isNaN
関数は検証する前に空の文字列を数値に変換し、空の文字列を 0 (有効な数値) に変更します。ただし、空の文字列またはスペースのみの文字列を解析しようとすると、parseInt
関数が返されることがわかりました。NaN
そのため、次の組み合わせがうまく機能しているようです。
if ( isNaN(string) || isNaN(parseInt(string)) ) console.log('Not a number!');
このチェックは、正の数、負の数、および小数点付きの数に対して機能するため、すべての一般的な数値ケースをカバーしていると思います。
JavaScript組み込みのisNaN関数は、デフォルトで予想されるように、「動的型演算子」です。したがって、(DTC プロセス中に) 単純な true | を生成する可能性があるすべての値。などの false を"", " ", " 000"
NaN にすることはできません。
提供された引数が最初に次のように変換されることを意味します。
function isNaNDemo(arg){
var x = new Number(arg).valueOf();
return x != x;
}
説明:
関数本体の一番上の行では、(最初に) 引数を数値オブジェクトに正常に変換しようとしています。そして (2 つ目) ドット演算子を使用して、便宜上、作成されたオブジェクトのプリミティブ値をすぐに取り除きます。
2 行目では、前のステップで取得した値を取得しています。また、NaNは宇宙のどの要素とも等しくなく、それ自体とも等しくないという利点を利用しています。たとえばNaN == NaN >> false
、最終的にそれを (不等式のために) 自分自身と比較します。 .
このように、関数 return は、指定された引数 return が数値オブジェクトへの変換の試行に失敗した場合、つまり数値ではない数値である場合にのみtrueを返します。例: NaN。
isNaNstatic( )
ただし、静的型演算子の場合、必要に応じて、必要に応じて、次のようなはるかに単純な関数を記述できます。
function isNaNstatic(x){
return x != x;
}
また、引数が明示的に NaN でない場合は false を返すように、DTC を完全に回避します。したがって、以下に対してテストします。
isNaNStatic(" x"); // will return false
それはまだ文字列だからです。
ただし
isNaNStatic(1/"x"); // will of course return true.
、たとえばisNaNStatic(NaN); >> true
.
しかし、 とは反対にisNaN
、isNaNStatic("NaN"); >> false
それ (引数) は通常の文字列だからです。
ps: isNaN の静的バージョンは、最新のコーディング シナリオで非常に役立ちます。そして、それが私がこれを投稿するのに時間をかけた主な理由の1つである可能性が非常に高い.
よろしく。
Number クラスに固有のメソッドを用意し (parseInt のような変換を行う他の関数では、これらの値の一部に対して異なる出力が得られるため)、プロトタイプの継承を使用すると便利です。
Object.assign(Number.prototype, {
isNumericallyValid(num) {
if (
num === null
|| typeof num === 'boolean'
|| num === ''
|| Number.isNaN(Number(num))
) {
return false;
}
return true;
}
});