isNaN()‎

من موسوعة حسوب

الدالة isNaN()‎ تُحدِّد فيما إذا كانت القيمة المُمرَّرة إليها هي NaN أم لا. لاحظ أنَّ تحويل القيم داخل الدالة isNaN()‎ له قواعد مثيرة للاهتمام؛ لذا يمكنك استخدام الدالة Number.isNaN()‎ التي عُرِّفَت في ECMAScript 2015 بدلًا من هذه الدالة.

البنية العامة

isNaN(value)

value

القيمة التي ستُختبَر إن كانت NaN.

القيمة المعادة

القيمة true إذا كانت القيمة المُعطاة هي NaN، والقيمة false فيما عدا ذلك.

الوصف

ضرورة وجود الدالة isNaN

على النقيض من بقية القيم في JavaScript، لا يمكن الاعتماد على معاملات المساواة (== و ===) لتحديد إذا كانت القيمة NaN أم لا، لأنَّ قيمة التعبيرين NaN == NaN و NaN === NaN هي false، وبالتالي هنالك حاجةٌ لهذه الدالة.

أصل قيم NaN

تولَّد قيم NaN عندما تُنتِج العمليات الرياضية قيمًا غير معُرَّفة أو غير قابلة للتمثيل، وهذه القيمة تنتج أيضًا من محاولة تحويل القيم غير العددية قسريًا إلى قيم عددية والتي لا يمكن أن يكون لها تمثيلٌ عدديٌ أوليٌ.

فمثلًا، قسمة صفر على صفر ستُنتِج NaN، لكن قسمة بقية الأعداد على الصفر لا تُنتِج NaN.

السلوك المربك للحالة الخاصة للدالة isNaN

بدءًا من الإصدارات القديمة من مواصفة الدالة isNaN كان سلوكها بالنسبة إلى الوسائط غير العددية مربكًا، فعندما لا يكون الوسيط المُمرَّر إلى الدالة isNaN من النوع Number، فستحوَّل القيمة قسريًا إلى Number، ثم ستُختبَر القيمة الناتجة لمعرفة إن كانت NaN، وبالتالي عند تحويل القيم غير العددية إلى النوع Number قسريًا فسينتج لدينا قيمة non-NaN عددية (ونخص بالذكر السلسلة النصية الفارغة والقيم المنطقية الأولية true و false، التي ستعطي القيم العددية 0 أو 1 عند تحويلها قسريًا إلى عدد) وستُعيد الدالة true على الرغم من أننا نتوقع القيمة false (فمن المؤكد أنَّ السلسلة النصية الفارغة -مثلًا- ليست رقمًا Not-A-Number).

ينشأ هذا السلوك المربك بسبب وجود معنى خاص للمصطلح «ليس عددًا» (Not-A-Number) للأرقام التي تُمثَّل كقيم IEEE-754 ذات فاصلة عائمة، ويجب أن تُفسَّر الدالة isNaN على أنها إجابة عن التساؤل: «هل هذه القيمة، عند تحويلها قسريًا إلى قيمة عددية، هي قيمة Not-A-Number كما عرّفتها IEEE-754؟».

معيار ECMAScript 2015 أضاف الدالة Number.isNaN()، واستخدام الدالة Number.isNaN(x)‎ هي طريقة عملية لمعرفة إن كانت القيمة x هي NaN أم لا، لكن تعريف NaN سيبقى يحمل المعنى الرقمي ولن يعني «ليس رقمًا» (Not-A-Number)؛ ويمكن بشكلٍ بديل استخدام التعبير (‎‎x != x) الذي يُعدّ أكثر طريقة عملية لمعرفة إن كانت القيمة x هي NaN أم لا، وستكون نتيجة التعبير السابقة غير خاضعة للحالات التي تجعل الدالة isNaN غير عملية.

يمكن إنشاء حل التفافي للدالة isNaN، عبر الاستفادة من خاصية عدم مساواة قيمة NaN لنفسها:

var isNaN = function(value) {
  var n = Number(value);
  return n !== n;
};

أمثلة

هذه بعض الأمثلة عن القيم التي ستُعدّ Not-A-Number:

isNaN(NaN);       // true
isNaN(undefined); // true
isNaN({});        // true

أما هذه فهي قيم ليست NaN:

isNaN(true);      // false
isNaN(null);      // false
isNaN(37);        // false

أمثلة عن السلاسل النصية، لاحظ أنَّ السلسلة النصية "37" في أوّل مثال قد حُوِّلَت إلى العدد 37 الذي ليس NaN، وكذلك الأمر بالنسبة إلى 37.37 التي ستحوَّل إلى العدد 37.37؛ أما السلسلة النصية "123ABC" فهي NaN وذلك لأنَّ ناتج Number("123ABC")‎ هو NaN (على الرغم من أنَّ ناتج parseInt("123ABC")‎ هو 123). آخر مثالين عن سلاسل نصية فارغة أو فيها فراغ وحيد، والتي ستحوّل إلى العدد 0:

isNaN('37');      // false
isNaN('37.37');   // false
isNaN('123ABC');  // true
isNaN('');        // false
isNaN(' ');       // false

مثال عن الكائن Date، الذي سيُعيد رقمًا عند استخدامه مع الدالة البانية Number، بينما ستكون السلسلة النصية التابعة له (والتي سنحصل عليها عبر الدالة toString) هي NaN:

isNaN(new Date());                // false
isNaN(new Date().toString());     // true

هذه نتيجة إيجبية كاذبة (false positive)، وهي السبب وراء كون الدالة isNaN غير عملية تمامًا، وذلك لأنَّ السلسلة النصية "blabla" ستحوّل إلى عدد، وتفسير هذا العدد سيفشل وستُعاد القيمة NaN:

isNaN('blabla');   // true

السلوك المفيد للحالة الخاصة للدالة isNaN

هنالك طريقة يمكننا الاستفادة فيها من الدالة isNaN()‎: فلو أعادت الدالة isNaN(x)‎ القيمة false، فيمكننا استخدام x كتعبير رياضي لا يُسبب إعادة القيمة NaN، ولو أعادت true فستؤدي القيمة x إلى جعل جميع التعابير الرياضية التي تدخل فيها تُعيد القيمة NaN؛ وهذا أنَّه في JavaScript إذا أعادت الدالة isNaN(x)‎ القيمة true فهذا يعني أنَّ التعبير x - 0 (مثلًا) سيعيد القيمة NaN (لكن التعبير x - 0 == NaN سيعيد القيمة false دومًا في JavaScript لأن NaN لا تساوي نفسها، لذا لا يمكنك إجراء هذا الاختبار)، وفي الواقع ستُعيد الدوال isNaN(x)‎ و isNaN(x - 0)‎ و isNaN(Number(x))‎ و Number.isNaN(x - 0)‎ و Number.isNaN(Number(x))‎ القيمة نفسها؛ وأقصر طريقة للتعبير عنها هي استخدام الدالة isNaN(x)‎.

يمكنك استخدام السلوك السابق لاختبار إذا كان الوسيط المُمرَّر إلى الدالة قابلًا للاستخدام في التعابير الرياضية (أي يمكن معاملته كعدد)، وإذا لم يكن كذلك فيمكنا توفير قيمة افتراضية أو اتباع إجراء آخر؛ وبهذه الطريقة ستستفيد الدالة من مرونة لغة JavaScript التي توفّرها عند تحويل القيم اعتمادًا على سياق استخدامها.

أمثلة

سنُعرِّف الدالة increment، التي تُعيد ناتج إضافة القيمة 1 إلى الوسيط المُمرَّر إليها:

function increment(x) {
  if (isNaN(x)) x = 0;
  return x + 1;
}

// الدالة الآتية تكافئ الدالة السابقة
function increment(x) {
  if (Number.isNaN(Number(x))) x = 0;
  return x + 1;
}

في الحالات الآتية ستكون قيمة المعامل x (أي الوسيط المُمرَّر إلى الدالة) ليست NaN، لكن ليس من الضرورة أن تكون قيمة الوسيط عدديةً، وإنما يمكن استعمالها في التعابير الرياضية:

increment('');            // 1: "" => 0
increment(new String());  // 1: String object => 0
increment([]);            // 1: [] => 0
increment(new Array());   // 1: Array object => 0
increment('0');           // 1: "0" => 0
increment('1');           // 2: "1" => 1
increment('0.1');         // 1.1: "0.1" => 0.1
increment('Infinity');    // Infinity: "Infinity" => Infinity
increment(null);          // 1: null => 0
increment(false);         // 1: false => 0
increment(true);          // 2: true => 1
increment(new Date());    // تعيد الوقت والتاريخ الحاليين بالملي ثانية + 1

أما الحالات الآتية فتكون قيمة المعامل x (أي الوسيط الممرر إلى الدالة) ليست NaN، لكن الوسيط هو قيمةٌ عدديةٌ:

increment(-1);            // 0
increment(-0.1);          // 0.9
increment(0);             // 1
increment(1);             // 2
increment(2);             // 3
// ... وهلم جرًا ...
increment(Infinity);      // Infinity

أما في الحالات الآتيةـ فتكون نتيجة الدالة isNaN(x)‎ هي true دومًا وقيمة الوسيط المُمرَّر إلى الدالة ليست عدديةً (Not-A-Number)، لذا ستضع الدالة القيمة 0 بدلًا منه (كما عرّفنا ذلك داخلها) والقيمة النهائية المعادة هي 1:

increment(String);              // 1
increment(Array);               // 1
increment('blabla');            // 1
increment('-blabla');           // 1
increment(0 / 0);               // 1
increment('0 / 0');             // 1
increment(Infinity / Infinity); // 1
increment(NaN);                 // 1
increment(undefined);           // 1
increment();                    // 1

دعم المتصفحات

الميزة Chrome Firefox Internet Explorer Opera Safari
الدعم الأساسي نعم نعم نعم نعم نعم

مصادر ومواصفات