Understanding underscore's implementation of isNaN - javascript

Taken from the underscore.js source:
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
Why did they do it this way? Is the above implementation equivalent to:
_.isNaN = function(obj) {
return obj !== obj;
};
If it is, why the "more complicated" version? If it is not, what are the behavioural differences?

_.isNaN(new Number(NaN)) returns true.
And that's by design.
var n = new Number(NaN);
console.log(_.isNaN(n), n!==n); // logs true, false

The host environment (e.g. web-browser environment) may introduce other values that aren't equal to themselves. The _.isNumber(obj) part makes sure that the input is a Number value, so that _.isNaN only returns true if the NaN value is passed.

If + is given before of any value with no preceding value to + , JavaScript engine will try to convert that variable to Number.If it is valid it will give you the Number else it will return NaN. For example
+ "1" // is equal to integer value 1
+ "a1" // will be NaN because "a1" is not a valid number
In above case
+"a1" != "a1" // true, so this is not a number, one case is satisfied
+"1" == "1" // true, so it is number
Another simple case would be, why the below expression give this output
console.log("Why I am " + typeof + "");
// returns "Why I am number"
Because +"" is 0.
If you want to check whether it is a number or not you could use the below function
function isNumber(a){
/* first method : */ return (+a == a);
/* second method : */ return (+(+a) >= 0);
// And so many other exists
}
Someone correct me if I am wrong somewhere..

I found one case for _.isNaN
It shows different result with the native one if you pass there an object
_.isNaN({}) => false
//but
isNaN({}) => true

Related

Use of arguments causing function to always return false

I would like to sum the args of my function if and only if the two args are numbers (hence my first function).
function checkNum() {
var num = 0;
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] !== 'number') {
return false;
}
}
return true;
}
function addTogether() {
var num = 100;
if ( checkNum() ) {
return arguments[0] + arguments[1];
} else {
return undefined;
}
}
addTogether(2, "");
However my second function performs the sum no matter what the args values are. Any hints on how to fix this ?
checkNum() isn't declared to explicitly take any arguments (which implies to anyone looking at the function that none are expected) and you are not sending any when you call it, so arguments.length is always 0, you never enter into your loop body and you always return true.
Your second function is called by passing two arguments, so your references to arguments[0] and arguments[1] are valid there. But, even still, the use of arguments isn't really meant for all argument passing.
It's best to set up your functions with named parameters and then you can access them via those names. The use of arguments (while valid), is not encouraged as the default mechanism for accessing arguments. It's generally used for validation (ensure that the correct amount of parameters were passed to the function before the function attempts to operate on them, for example).
Also, it's best to test for numbers with a regular expression because typeof can "lie" to you. For example:
// Would you ever think that not a number is of type "number"?!
console.log(typeof NaN === "number");
Now, depending on your criteria for "number", there are two ways you could go.
Only numeric digits are allowed (i.e. 6 is allowed, "6" is not)
// It's better for this function to test one number
// at a time, so you can react to that particular
// success or failure
function checkNum(num) {
// No loop and no if/then needed, just return
// whether the argument is a number, but don't
// test for typeof number because typeof NaN === "number"
// Use a regular expression instead
var reg = /[0-9]+$/; // digits or strings of characters that are from 0 - 9
// Test for only digits not numbers passed as strings
// For example 6 is good, "6" is bad. Here, the use of "typeof"
// is safe because you are also testing that the input is digits
// or characters from 0 to 9 (NaN wouldn't pass this test)
return reg.test(num) && typeof num === "number"; // true or false will be returned
}
function addTogether(val1, val2) {
// Test each input, independantly so that you can react more granularly
if ( checkNum(val1) && checkNum(val2) ) {
return val1 + val2;
}
// It's not necessary to have an "else" that returns undefined because
// that's what will happen as long as you don't return anything else.
}
console.log(addTogether(2, "")); // undefined
console.log(addTogether(2, 6)); // 8
console.log(addTogether(2, "6")); // undefined because "6" is a string, not a digit
Numeric digits and numeric characters are allowed (i.e. 6 and "6" are allowed). In this case, you'd need to ensure that numeric characters get converted to numbers before addition is done so you get mathematical addition and not string concatenation.
// It's better for this function to test one number
// at a time, so you can react to that particular
// success or failure
function checkNum(num) {
// No loop and no if/then needed, just return
// whether the argument is a number, but don't
// test for typeof number because typeof NaN === "number"
// Use a regular expression instead
var reg = /[0-9]+$/; // digits or strings that are from 0 - 9
// Test for only digits and numbers passed as strings
return reg.test(num); // true or false will be returned
}
function addTogether(val1, val2) {
if ( checkNum(val1) && checkNum(val2) ) {
// If checkNum returns true for numeric characters as well as digits, then
// you'd need to ensure that the characters get converted to numbers so that
// you get mathmatical addition and not string concatenation. That would be done like this:
return +val1 + +val2
}
// It's not necessary to have an "else" that returns undefined because
// that's what will happen as long as you don't return anything else.
}
console.log(addTogether(2, "")); // undefined
console.log(addTogether(2, 6)); // 8
console.log(addTogether(2, "6")); // 8 because "6" is converted to 6, not a string of "6"
The arguments array, as evaluated within checkNum, contains the arguments passed to checkNum. But you aren't passing any arguments to checkNum. Try changing the if statement to
if ( checkNum(arguments[0], arguments[1]) )
You aren't passing any arguments to checkNum. You can fix this with apply:
// ...
if (checkNum.apply(this, arguments)) {
// ...
Edit: That would allow you to check any number of arguments passed to addTogether. If you only want to allow for two arguments, you can used named parameters:
function checkNum(a, b) {
return typeof a === 'number' && typeof b === 'number';
}
function addTogether(a, b) {
if (checkNum(a, b)) return a + b;
else return undefined; // line not needed
}
addTogether(2, "");

isFinite of space giving true value

I am trying to validate a price field. I was trying this:
var productId = document.getElementById("productId");
var productName = document.getElementById("productName");
var productPrice = document.getElementById("price");
alert(isFinite(productPrice.value));
if(isNaN(productPrice.value)||!isFinite(productPrice.value)){
error = document.getElementById("priceError");
error.innerHTML = "Please enter correct value";
productPrice.style.border="thin solid red";
}else{
error = document.getElementById("priceError");
error.innerHTML = "";
}
The line alert is giving me true when the input is space/ multiple spaces only.
This is my HTML page.
<td>Price</td>
<td><input type = "text" id = "price" size = "14"/></td>
Thanks
Why this happens i cant say, but this code should solve the problem
isFinite(parseFloat(" ")) // false
// because -->
parseFloat(" "); // => result NaN
// tested in Chrome 27+ on Win7
in the MDN refernce of isNaN here
it says
isNaN(" "); // false: a string with spaces is converted to 0 which is not NaN
Update:
in the Reference of isFinite found Here it states that isFinite only returns false if the argument is:
NaN
positive infinity, (Number.POSITIVE_INFINITY)
negative infinity (Number.NEGATIVE_INFINITY)
In any other Case it returns true. (like Paul S mentioned)
Now i Think i got all loose ends, and in that course learned something. :)
with window.isFinite, you must be aware of the issues that window.isNaN suffers from when coercing types.
window.IsNaN Summary
Determines whether a value is NaN or not. Be careful, this function is
broken. You may be interested in ECMAScript 6 Number.isNaN.
Examples
isNaN(NaN); // true
isNaN(undefined); // true
isNaN({}); // true
isNaN(true); // false
isNaN(null); // false
isNaN(37); // false
// strings
isNaN("37"); // false: "37" is converted to the number 37 which is not NaN
isNaN("37.37"); // false: "37.37" is converted to the number 37.37 which is not NaN
isNaN(""); // false: the empty string is converted to 0 which is not NaN
isNaN(" "); // false: a string with spaces is converted to 0 which is not NaN
// This is a false positive and the reason why isNaN is not entirely reliable
isNaN("blabla") // true: "blabla" is converted to a number. Parsing this as a number fails and returns NaN
In ECMAScript 6 there are new methods Number.isNaN and Number.isFinite that address these issues. (of course these are not available in many browsers)
Number.isFinite is equivalent to
function isFinite(number) {
return typeof number === "number" && window.isFinite(number);
}
So as a solution, you would need to consider something like this (cross-browser).
Note: this solution will still allow you to enter hexadecimal or scientific notations, "0xA", "1e10"
Javascript
function isFinite(number) {
return typeof number === "number" && window.isFinite(number);
}
function trim(string) {
return string.replace(/^\s+|\s+$/g, "");
}
var price = document.getElementById("price");
price.onchange = function (e) {
var evt = e || window.event,
target = evt.target || evt.srcElement,
value = trim(target.value) || "NaN",
number = +value;
console.log("number:", number);
console.log("isFinite:", isFinite(number));
}
On jsfiddle
You could do it using reqular expression.
Try this.
function validatePrice() {
var el = document.getElementById('price');
if (
el.value.length < 14 &&
/^ *\+?\d+ *$/.test( el.value )
)
{
return true;
}
return false;
}
This function checks if the input is positive integer. I didnt know if you want floated values also.
If you do, switch the regex to this /^ *+?\d+((.|,)\d+)? *$/

What causes isNaN to malfunction? [duplicate]

This question already has answers here:
Validate decimal numbers in JavaScript - IsNumeric()
(52 answers)
Closed 9 years ago.
I'm simply trying to evaluate if an input is a number, and figured isNaN would be the best way to go. However, this causes unreliable results. For instance, using the following method:
function isNumerical(value) {
var isNum = !isNaN(value);
return isNum ? "<mark>numerical</mark>" : "not numerical";
}
on these values:
isNumerical(123)); // => numerical
isNumerical("123")); // => numerical
isNumerical(null)); // => numerical
isNumerical(false)); // => numerical
isNumerical(true)); // => numerical
isNumerical()); // => not numerical
shown in this fiddle: http://jsfiddle.net/4nm7r/1
Why doesn't isNaN always work for me?
isNaN returns true if the value passed is not a number(NaN)(or if it cannot be converted to a number, so, null, true and false will be converted to 0), otherwise it returns false. In your case, you have to remove the ! before the function call!
It is very easy to understand the behaviour of your script. isNaN simply checks if a value can be converted to an int. To do this, you have just to multiply or divide it by 1, or subtract or add 0. In your case, if you do, inside your function, alert(value * 1); you will see that all those passed values will be replaced by a number(0, 1, 123) except for undefined, whose numerical value is NaN.
You can't compare any value to NaN because it will never return false(even NaN === NaN), I think that's because NaN is dynamically generated... But I'm not sure.
Anyway you can fix your code by simply doing this:
function isNumerical(value) {
var isNum = !isNaN(value / 1); //this will force the conversion to NaN or a number
return isNum ? "<mark>numerical</mark>" : "not numerical";
}
Your ternary statement is backward, if !isNaN() returns true you want to say "numerical"
return isNum ? "not numerical" : "<mark>numerical</mark>";
should be:
return isNum ? "<mark>numerical</mark>" : "not numerical";
See updated fiddle:
http://jsfiddle.net/4nm7r/1/
Now that you already fixed the reversed logic pointed out on other answers, use parseFloat to get rid of the false positives for true, false and null:
var isNum = !isNaN(parseFloat(value));
Just keep in mind the following kinds of outputs from parseFloat:
parseFloat("200$"); // 200
parseFloat("200,100"); // 200
parseFloat("200 foo"); // 200
parseFloat("$200"); // NaN
(i.e, if the string starts with a number, parseFloat will extract the first numeric part it can find)
I suggest you use additional checks:
function isNumerical(value) {
var isNum = !isNaN(value) && value !== null && value !== undefined;
return isNum ? "<mark>numerical</mark>" : "not numerical";
}
If you would like treat strings like 123 like not numerical than you should add one more check:
var isNum = !isNaN(value) && value !== null && value !== undefined && (typeof value === 'number');

Why using this method to return a function or value?

I'm just wondering why should I use this method to return a function :
clusters.prototype.get_local_storage_data = function(data_key) {
return +(this.localStorage.getItem(data_key) || 1);
};
What does the +() do there and why using that ? Is there a better way of returning the function or 1 if what the function gets is null ?
Using the + before a value forces that value to become a number. In the case above, the data key will be converted to a number (if it's found), or the number 1 will be returned. Either way, the result will be converted to a number.
+null; // 0
+"3.14"; // 3.14
+1; // 1
It's just ensuring that no matter what the output is, you will be returning a number.
The + is there to cast the result to a number -
typeof +"123" // "number"
The way it's implemented looks just fine, and doesn't need to be changed.
The + is just making sure the return value is a number or else 1 would be true and not the number one. It's a shortcut for:
Number( expression )

type checking in javascript

How can I check if a variable is currently an integer type? I've looked for some sort of resource for this and I think the === operator is important, but I'm not sure how to check if a variable is an Integer (or an Array for that matter)
A variable will never be an integer type in JavaScript — it doesn't distinguish between different types of Number.
You can test if the variable contains a number, and if that number is an integer.
(typeof foo === "number") && Math.floor(foo) === foo
If the variable might be a string containing an integer and you want to see if that is the case:
foo == parseInt(foo, 10)
These days, ECMAScript 6 (ECMA-262) is "in the house". Use Number.isInteger(x) to ask the question you want to ask with respect to the type of x:
js> var x = 3
js> Number.isInteger(x)
true
js> var y = 3.1
js> Number.isInteger(y)
false
A number is an integer if its modulo %1 is 0-
function isInt(n){
return (typeof n== 'number' && n%1== 0);
}
This is only as good as javascript gets- say +- ten to the 15th.
isInt(Math.pow(2,50)+.1) returns true, as does
Math.pow(2,50)+.1 == Math.pow(2,50)
A clean approach
You can consider using a very small, dependency-free library like Issable. Solves all problems:
// at the basic level it supports primitives
let number = 10
let array = []
is(number).number() // returns true
is(array).number() // throws error
// so you need to define your own:
import { define } from 'issable'
// or require syntax
const { define } = require('issable')
define({
primitives: 'number',
nameOfTyping: 'integer',
toPass: function(candidate) {
// pre-ECMA6
return candidate.toFixed(0) === candidate.toString()
// ECMA6
return Number.isInteger(candidate)
}
})
is(4.4).custom('integer') // throws error
is(8).customer('integer') // returns true
If you make it a habit, your code will be much stronger. Typescript solves part of the problem but doesn't work at runtime, which is also important.
function test (string, boolean) {
// any of these below will throw errors to protect you
is(string).string()
is(boolean).boolean()
// continue with your code.
}
I know you're interested in Integer numbers so I won't re answer that but if you ever wanted to check for Floating Point numbers you could do this.
function isFloat( x )
{
return ( typeof x === "number" && Math.abs( x % 1 ) > 0);
}
Note: This MAY treat numbers ending in .0 (or any logically equivalent number of 0's) as an INTEGER. It actually needs a floating point precision error to occur to detect the floating point values in that case.
Ex.
alert(isFloat(5.2)); //returns true
alert(isFloat(5)); //returns false
alert(isFloat(5.0)); //return could be either true or false
Quite a few utility libraries such as YourJS offer functions for determining if something is an array or if something is an integer or a lot of other types as well. YourJS defines isInt by checking if the value is a number and then if it is divisible by 1:
function isInt(x) {
return typeOf(x, 'Number') && x % 1 == 0;
}
The above snippet was taken from this YourJS snippet and thusly only works because typeOf is defined by the library. You can download a minimalistic version of YourJS which mainly only has type checking functions such as typeOf(), isInt() and isArray(): http://yourjs.com/snippets/build/34,2
You may also have a look on Runtyper - a tool that performs type checking of operands in === (and other operations).
For your example, if you have strict comparison x === y and x = 123, y = "123", it will automatically check typeof x, typeof y and show warning in console:
Strict compare of different types: 123 (number) === "123" (string)
Try this code:
alert(typeof(1) == "number");

Categories

Resources