Automatic javascript type coercion - javascript

This has become more of a pondering about how javascript works, than an actual problem to fix. In the case of a statement like
var str = 9 + " some words here";
The resultant str var would contain the value "9 some words here". My question is what function does javascript use to automatically coerce the Number object '9' into a string to be concatenated with the String object " some words here", and is this function changeable/overrideable.
This started from me needing to output single digits with a preceding 0 on a page. This was easy enough to accomplish with a quick prototype function on the Number object
Number.prototype.SpecialFormat = function() {
if(this < 10) {
return "0" + this;
}
else {
return this.toString();
}
};
And call it with a simple (9).SpecialFormat() + " words here";
But that got me wondering if I could overwrite the toString function on a Number with a
Number.prototype.toString = function() {
if(this < 10) {
return "0" + this;
}
else {
return this;
}
};
, and just let javascripts automatic conversion handle it for me, so I could use a standard 9 + " words here"; to get the same result "09 words here". This did not just implicitly work, I had to end up adding .toString to the 9 (9).toString() + " words here" (which upon taking a further look, would have resulted in some infinite loops).
So is there a way to override the built in functionality of a javascript native type ?
*Note: I realize this is likely a 'worst idea ever'

The addition operator coerces its arguments to strings in step 7:
If Type(lprim) is String or Type(rprim) is String, then
Return the String that is the result of concatenating ToString(lprim) followed by ToString(rprim)
The ToString operation has special rules for numbers, detailed under "ToString Applied to the Number Type".
It appears that the abstract operation ToString(number) does not ever use Number.prototype.toString. In fact, ti's the other way around: the default Number.prototype.toString function employs the abstract numeric ToString algorithm. Thus, it's not possible to override the default behavior for the stringification of numbers during type coercion in ECMAScript 5, because there's no way to alter the language-defined ToString operation.
ToString does use the target's toString method when coercing objects to strings, but not number primitive values.

(not strictly an answer but I need some space)
Be very careful with type coercion, especially when combining primitive types with their object counterparts because:
var n = 9;
typeof n; // "number"
n instanceof Number; // false!!
n = new Number(9);
n instanceof Number; // true, oh thank God
typeof n; // "object" what!!
Also, toString on Number doesn't actually work:
Number.prototype.toString = function () { return "foo" + this; };
var n = new Number(9);
"" + n; // "9", not "foo9"
A way to avoid this mess is by having:
var ns = {
number: {
specialFormat: function () /* .. */ }
}
}
ns.number.specialFormat(9); // "09"

Related

Why JS doesn't coerce the value of i in a "for in loop"?

Given the two functions (with the expected output of //d):
function fearNotLetter(str) {
for (let i = 0; i < str.length; i++) {
let code = str.charCodeAt(i)
if (code !== str.charCodeAt(0) + i) {
return String.fromCharCode(str.charCodeAt(0) + i)
}
}
return undefined
}
fearNotLetter("abce");
// d
And
function fearNotLetter(str) {
for (let i in str) {
let code = str.charCodeAt(i)
if (code !== str.charCodeAt(0) + i) {
return String.fromCharCode(str.charCodeAt(0) + i)
}
}
return undefined
}
fearNotLetter("abce");
// ϊ
I discovered that the value of i is coerced to a String using the for...in loop. In my understanding, the if statement fails, because the value of i is no longer a number, therefore the sum can't be done.
My question is, why doesn't JS coerce i back to a number in the if statement (str.charCodeAt(0) + i )? And allow the sum to be done the same way as he for...loop?
Is it because, once iis coerced inside the funct, then it can't be coerced again?
In the first function you set the type of i to number when you initialized it to a number. In the second function i is a key to an iterable so it means it's type is string. There hasn't been type conversion at all.
Basically that's the coercion rule. As you can see in the table +, a String + a Number, the Number will always be implicitly coerced to String.
The intuition is, both String and Number have the + operator, but the Number will always be able to be coerced to a String, whereas not all String is (can be coerced to) a Number.
Have you tried:
str.charCodeAt(parseInt(i, 10))

Why doesn't typeof work but isNaN works ?

Why doesn't this block work? I Used isNaN() and that works but not this, why? Javascript is behaving weirdly.
if( (typeof parseInt(returnedArr[count]) == 'number')
{
totalWorth= parseInt(totalWorth)+ parseInt(returnedArr[count]);
//document.write(returnedArr[count]);
}
Code:
function addWorth()
{
var table1= document.getElementById("tableNetWorths");
var rowCount1= table1.rows.length;
//var row1= table1.insertRow(rowCount1);
var arr= [];
for(var count = 0; count < rowCount1; count++)
{
arr.push(table1.rows[count].cells[1].innerHTML);
}
arr.shift();
return arr;
}
function showWorthSum()
{
var returnedArr= addWorth();
//returnedArr.push(addWorth());
totalWorth= 0;
var arrCount= returnedArr.length;
for(var count = 0; count < arrCount; count++)
{
if( (typeof parseInt(returnedArr[count]) == 'number')
{
totalWorth= parseInt(totalWorth)+ parseInt(returnedArr[count]);
//document.write(returnedArr[count]);
}
}
return parseInt(totalWorth);
}
If I use isNaN then that works but not this, why? My array looks like this:
{"100", "200", "asdasdadsa", "1"}
Because typeof NaN is "number":
console.log(typeof NaN);
NaN is a special value* of the number type, not its own type.
You haven't shown your code that uses isNaN, but note that if you pass a string into isNaN, it will be implicitly converted to number before being tested to see if the result of doing that is NaN (as though you had called Number(x) on it, or applied unary + or any of the non-addition math ops [-, *, /, etc.]).
Separately:
Beware that parseInt will happily parse a string that only starts with a number, ignoring the part after it that isn't numeric. For instance, parseInt("123abc") is 123.
Beware that when used without its second argument, parseInt will infer the number base (radix) from the string, so parseInt("0x10") is 16.
When dealing with user input, handling both of those situations intentionally is usually best:
function parseIntStrict(str) {
str = str.trim();
if (!/^\d+$/.test(str)) {
return NaN;
}
return parseInt(str, 10);
}
(Note that doesn't attempt to support scientific notation input; users don't usually input it.)
And for floating point:
function parseFloatStrict(str) {
str = str.trim();
if (!str) {
return NaN;
}
return +str;
}
(That does support scientific notation, but only as a byproduct of only checking for blank strings before handing off to built-in numeric conversion.)
Applying that to your code:
// I assume totalWorth is already a number
var entry = parseIntStrict(returnedArr[count]);
if (!isNaN(entry)) {
totalWorth = totalWorth + entry;
}
* Technically, per the IEEE-754 standard JavaScript uses, NaN is any of a range of values that all mean "not a number."
This is because the values in the array that you consider to be numbers are actually strings. For example, 100 is a number, but '100' is a string. typeof 123 will return 'number', but typeof '123' will return 'string'.
This means that all the values in your array are of type string and your code will not enter the if statement, if you use that approach.
However, since isNaN checks if an element in not a number, your code will work with that.

Add empty string to first argument of parseInt

I'm just trying to understand how JS currying works. While I was looking for it I found a related example:
var add = function (orig) {
var inner = function (val) {
return add(parseInt(val+'', 10) == val ? inner.captured+val : inner.captured);
};
inner.captured = orig;
inner.valueOf = function () {return inner.captured;};
return inner;
};
What the point to add an empty string to the first argument in a parseInt
method?
I think it also can be related to the valueOf
val+'' converts expression to string.
Quick test to show what's going on:
typeof(1)
"number" // value is 1 (number)
typeof(1+'')
"string" // now value is "1" (a string)
what is the purpose of making it a string?
The purpose could be to avoid native code to call abstract ToString method to convert first argument of parseInt to string.
We can read in MDN that first argument to parseInt is string with following description:
The value to parse. If string is not a string, then it is converted to
a string (using the ToString abstract operation). Leading whitespace
in the string is ignored.
To explain we can re-write part of the code:
return add(parseInt(val+'', 10) == val ? inner.captured+val : inner.captured);
// could be written like:
if ( parseInt(val+'', 10) == val ) {
return inner.captured+val
}
else {
return inner.captured;
}
// Looking at:
parseInt(val+'', 10) == val
// we're checking if the number at base 10 is equal to itself
// parseInt takes a string as it's first parameter, hence
// the type-casting with +''.
// This step could probably be ignored as the docs say that the number is
// cast to a string automatically, however for completeness we might
// choose to manually cast it.
parseInt docs

Convert a string to number with +

Here's an easy one for you true believers: You can use + to convert a string to a number,
var thing = "12"
alert(thing);
alert(typeof thing); // string
thing = +thing;
alert(typeof thing); // number
if (thing == 112) alert("!"); // number
Can someone explain:
What is the name of this process?
How does + convert a string to a number?
Javascript uses a dynamic type system. For me it's a 'cast' operation.
The operator + could be a String operator ('a' + 'b') or an Number operator (1+2). It could be used also between Strings and numbers (remembering that 0 + '12' = 12 and '0'+'12' = '012')
By default, i think that the JS interpreter considered +thing as 0 + things so it casts this variable to a number

What's the best way to convert a number to a string in JavaScript?

What's the "best" way to convert a number to a string (in terms of speed advantage, clarity advantage, memory advantage, etc) ?
Some examples:
String(n)
n.toString()
""+n
n+""
like this:
var foo = 45;
var bar = '' + foo;
Actually, even though I typically do it like this for simple convenience, over 1,000s of iterations it appears for raw speed there is an advantage for .toString()
See Performance tests here (not by me, but found when I went to write my own):
http://jsben.ch/#/ghQYR
Fastest based on the JSPerf test above: str = num.toString();
It should be noted that the difference in speed is not overly significant when you consider that it can do the conversion any way 1 Million times in 0.1 seconds.
Update: The speed seems to differ greatly by browser. In Chrome num + '' seems to be fastest based on this test http://jsben.ch/#/ghQYR
Update 2: Again based on my test above it should be noted that Firefox 20.0.1 executes the .toString() about 100 times slower than the '' + num sample.
In my opinion n.toString() takes the prize for its clarity, and I don't think it carries any extra overhead.
Explicit conversions are very clear to someone that's new to the language. Using type coercion, as others have suggested, leads to ambiguity if a developer is not aware of the coercion rules. Ultimately developer time is more costly than CPU time, so I'd optimize for the former at the cost of the latter. That being said, in this case the difference is likely negligible, but if not I'm sure there are some decent JavaScript compressors that will optimize this sort of thing.
So, for the above reasons I'd go with: n.toString() or String(n). String(n) is probably a better choice because it won't fail if n is null or undefined.
The below are the methods to convert an Integer to String in JS.
The methods are arranged in the decreasing order of performance.
var num = 1
Method 1:
num = `${num}`
Method 2:
num = num + ''
Method 3:
num = String(num)
Method 4:
num = num.toString()
Note: You can't directly call toString() on a number. 2.toString() will throw Uncaught SyntaxError: Invalid or unexpected token.
(The performance test results are given by #DarckBlezzer in his answer)
Other answers already covered other options, but I prefer this one:
s = `${n}`
Short, succinct, already used in many other places (if you're using a modern framework / ES version) so it's a safe bet any programmer will understand it.
Not that it (usually) matters much, but it also seems to be among the fastest compared to other methods.
...JavaScript's parser tries to parse
the dot notation on a number as a floating point literal.
2..toString(); // the second point is correctly recognized
2 .toString(); // note the space left to the dot
(2).toString(); // 2 is evaluated first
Source
Tongue-in-cheek obviously:
var harshNum = 108;
"".split.call(harshNum,"").join("");
Or in ES6 you could simply use template strings:
var harshNum = 108;
`${harshNum}`;
The simplest way to convert any variable to a string is to add an empty string to that variable.
5.41 + '' // Result: the string '5.41'
Math.PI + '' // Result: the string '3.141592653589793'
I used https://jsperf.com to create a test case for the following cases:
number + ''
`${number}`
String(number)
number.toString()
https://jsperf.com/number-string-conversion-speed-comparison
As of 24th of July, 2018 the results say that number + '' is the fastest in Chrome, in Firefox that ties with template string literals.
Both String(number), and number.toString() are around 95% slower than the fastest option.
I recommended `${expression}` because you don't need to worry about errors.
[undefined,null,NaN,true,false,"2","",3].forEach(elem=>{
console.log(`${elem}`, typeof(`${elem}`))
})
/* output
undefined string
null string
NaN string
true string
false string
2 string
string
3 string
*/
Below you can test the speed. but the order will affect the result. (in StackOverflow) you can test it on your platform.
const testCases = [
["${n}", (n) => `${n}`], // 👈
['----', undefined],
[`"" + n`, (n) => "" + n],
[`'' + n`, (n) => '' + n],
[`\`\` + n`, (n) => `` + n],
[`n + ''`, (n) => n + ''],
['----', undefined],
[`String(n)`, (n) => String(n)],
["${n}", (n) => `${n}`], // 👈
['----', undefined],
[`(n).toString()`, (n) => (n).toString()],
[`n.toString()`, (n) => n.toString()],
]
for (const [name, testFunc] of testCases) {
if (testFunc === undefined) {
console.log(name)
continue
}
console.time(name)
for (const n of [...Array(1000000).keys()]) {
testFunc(n)
}
console.timeEnd(name)
}
I'm going to re-edit this with more data when I have time to, for right now this is fine...
Test in nodejs v8.11.2: 2018/06/06
let i=0;
console.time("test1")
for(;i<10000000;i=i+1){
const string = "" + 1234;
}
console.timeEnd("test1")
i=0;
console.time("test1.1")
for(;i<10000000;i=i+1){
const string = '' + 1234;
}
console.timeEnd("test1.1")
i=0;
console.time("test1.2")
for(;i<10000000;i=i+1){
const string = `` + 1234;
}
console.timeEnd("test1.2")
i=0;
console.time("test1.3")
for(;i<10000000;i=i+1){
const string = 1234 + '';
}
console.timeEnd("test1.3")
i=0;
console.time("test2")
for(;i<10000000;i=i+1){
const string = (1234).toString();
}
console.timeEnd("test2")
i=0;
console.time("test3")
for(;i<10000000;i=i+1){
const string = String(1234);
}
console.timeEnd("test3")
i=0;
console.time("test4")
for(;i<10000000;i=i+1){
const string = `${1234}`;
}
console.timeEnd("test4")
i=0;
console.time("test5")
for(;i<10000000;i=i+1){
const string = 1234..toString();
}
console.timeEnd("test5")
i=0;
console.time("test6")
for(;i<10000000;i=i+1){
const string = 1234 .toString();
}
console.timeEnd("test6")
output
test1: 72.268ms
test1.1: 61.086ms
test1.2: 66.854ms
test1.3: 63.698ms
test2: 207.912ms
test3: 81.987ms
test4: 59.752ms
test5: 213.136ms
test6: 204.869ms
If you need to format the result to a specific number of decimal places, for example to represent currency, you need something like the toFixed() method.
number.toFixed( [digits] )
digits is the number of digits to display after the decimal place.
The only valid solution for almost all possible existing and future cases (input is number, null, undefined, Symbol, anything else) is String(x). Do not use 3 ways for simple operation, basing on value type assumptions, like "here I convert definitely number to string and here definitely boolean to string".
Explanation:
String(x) handles nulls, undefined, Symbols, [anything] and calls .toString() for objects.
'' + x calls .valueOf() on x (casting to number), throws on Symbols, can provide implementation dependent results.
x.toString() throws on nulls and undefined.
Note: String(x) will still fail on prototype-less objects like Object.create(null).
If you don't like strings like 'Hello, undefined' or want to support prototype-less objects, use the following type conversion function:
/**
* Safely casts any value to string. Null and undefined are converted to ''.
* #param {*} value
* #return {string}
*/
function string (str) {
return value == null ? '' : (typeof value === 'object' && !value.toString ? '[object]' : String(value));
}
With number literals, the dot for accessing a property must be distinguished from the decimal dot. This leaves you with the following options if you want to invoke to String() on the number literal 123:
123..toString()
123 .toString() // space before the dot 123.0.toString()
(123).toString()
I like the first two since they're easier to read. I tend to use String(n) but it is just a matter of style than anything else.
That is unless you have a line as
var n = 5;
console.log ("the number is: " + n);
which is very self explanatory
I think it depends on the situation but anyway you can use the .toString() method as it is very clear to understand.
.toString() is the built-in typecasting function, I'm no expert to that details but whenever we compare built-in type casting verse explicit methodologies, built-in workarounds always preferred.
If I had to take everything into consideration, I will suggest following
var myint = 1;
var mystring = myint + '';
/*or int to string*/
myint = myint + ''
IMHO, its the fastest way to convert to string. Correct me if I am wrong.
If you are curious as to which is the most performant check this out where I compare all the different Number -> String conversions.
Looks like 2+'' or 2+"" are the fastest.
https://jsperf.com/int-2-string
We can also use the String constructor. According to this benchmark it's the fastest way to convert a Number to String in Firefox 58 even though it's slower than
" + num in the popular browser Google Chrome.
Method toFixed() will also solves the purpose.
var n = 8.434332;
n.toFixed(2) // 8.43
You can call Number object and then call toString().
Number.call(null, n).toString()
You may use this trick for another javascript native objects.
Just come across this recently, method 3 and 4 are not appropriate because how the strings are copied and then put together. For a small program this problem is insignificant, but for any real web application this action where we have to deal with frequency string manipulations can affects the performance and readability.
Here is the link the read.
It seems similar results when using node.js. I ran this script:
let bar;
let foo = ["45","foo"];
console.time('string concat testing');
for (let i = 0; i < 10000000; i++) {
bar = "" + foo;
}
console.timeEnd('string concat testing');
console.time("string obj testing");
for (let i = 0; i < 10000000; i++) {
bar = String(foo);
}
console.timeEnd("string obj testing");
console.time("string both");
for (let i = 0; i < 10000000; i++) {
bar = "" + foo + "";
}
console.timeEnd("string both");
and got the following results:
❯ node testing.js
string concat testing: 2802.542ms
string obj testing: 3374.530ms
string both: 2660.023ms
Similar times each time I ran it.
Just use template literal syntax:
`${this.num}`

Categories