I'm trying to understand difference between 4 and (4).
4.toString(); // SyntaxError: Unexpected token ILLEGAL
(4).toString(); // "4"
I thought it's because toString() method is defined on Number (object) and 4 is a primitive datatype i.e.number which doesn't have toString() method.
However, how does it works for (4).toString()?
4 === (new Number(4)) // false
(4) === (new Number(4)) // false. (4) is not an instance of Number object
Dot (.) doesn't only means for member access operator. It also means for decimal point in floating-point literals. When JS interpreter meets dot after digit (4 in your case) it expects floating-point literal, i.e. digits after dot. That's why do you need grouping operator (parens) there.
Related
How come this works:
var num = 1;
console.log(num.toString()); // "1"
But this does not?
console.log(1.toString()); // SyntaxError: Unexpected token ILLEGAL
Because the grammar expects a . after a number to be parsed as part of that number, as it would be for e.g. 1.5. You need to disambiguate the . if you want to use it as a member operator on a numeric literal:
1..toString(); // "1"
1.0.toString(); // "1"
(1).toString(); // "1"
In the first two cases the first . is parsed as a floating point. The second can only be parsed as a member operator because numeric literals can only contain a single floating point.
This is shown by the NumericLiteral grammar in the spec.
Can someone explain in detail what is going on here? Specifically the double dot notation.
(3.14).toFixed(); // "3"
3.14.toFixed(); // "3"
(3).toFixed(); // "3"
3.toFixed(); // SyntaxError: Unexpected token ILLEGAL
3..toFixed(); // "3"
source
According to ECMA Script 5.1 Specifications, grammar for decimal literals are defined like this
DecimalLiteral ::
DecimalIntegerLiteral . [DecimalDigits] [ExponentPart]
. DecimalDigits [ExponentPart]
DecimalIntegerLiteral [ExponentPart]
Note: The square brackets are just to indicate that the parts are optional.
So, when you say
3.toFixed()
After consuming 3., the parser thinks that the current token is a part of a Decimal Literal, but it can only be followed by DecimalDigits or ExponentPart. But it finds t, which is not valid, that is why it fails with SyntaxError.
when you do
3..toFixed()
After consuming 3., it sees . which is called property accessor operator. So, it omits the optional DecimalDigits and ExponentPart and constructs a Floating point object and proceeds to invoke toFixed() method.
One way to overcome this is to leave a space after the number, like this
3 .toFixed()
3. is a number, so the . is the decimal point and doesn't start a property.
3..something is a number followed by a property.
I was coding today and I made a mistake while declaring an Array of Arrays in javascript (using the literal notation), I had forgotten to place a comma between each element in the array. After some further tests I got:
[[0][0]] gave me [0]
[[1][2]] gave me [undefined]
[0][0] gave me 0
[3][3] gave me undefined
[3]3 gave me a SyntaxError: Unexpected number
[][] gave me SyntaxError: Unexpected token ]
[3]*3 gave me 9 (Number not inside an array)
[3,4]*3 gave me NaN
[3,3]*[3,3] gave me NaN
[3,3]*[[3,3]] gave me NaN
[3,3][3,3] gave me undefined
[3,3][[3,3]] gave me undefined
At first I thought that this behavior might be mathematical vector/matrix multiplication, but that does not seem to be the case.
So the no operator between each array is clearly different than adding a * operator, and the * operator itself does not seem to perform neither scalar multiplication nor matrix multiplication.
The minus and division signs seems to always yield NaN and the plus sign seems to call a toString on both arrays and concat the strings.
I found that to be very odd, what is the semantic meaning behind operations between two arrays? To me the thing that makes most sense is to either always give errors when declaring Array Array and to always give NaN when declaring Array _operator_ Array. But that is not the case at all. The + sign at least makes sense because Array inherits from Object and that also happens if you try new Date() + new Date() (and this automatic toString call might be useful sometimes, although I would not design the language this way).
the * operator itself does not seem to perform neither scalar multiplication nor matrix multiplication. The minus and division signs seems to always yield NaN.
Indeed. *, / and - only work on numbers, and they will cast their operands to numbers. The array [3,3] will in that process first be converted to the string "3,3, which is not a valid number, therefore NaN as the result. With [3]*3 it "works" because the array is casted to the number 3. Similarly, [3]-[1] would yield 2.
So the no operator between each array is clearly different than adding a * operator. I found that to be very odd, what is the semantic meaning behind operations between two arrays?
If you did place "no operator" between the arrays, the latter ones are no arrays any more. The first pair of […] does build an array literal, but all the following […] are property accessors in bracket notation. [0][0] just accesses the first item of the [0] array which happens to be 0.
That is why […][] is a syntax error - the bracket notation needs an expression for the property name.
What you did see with […][…,…] was the comma operator to delimit expressions, it is not an array literal but parsed as …[(…, …)]. Your [3,3][3,3] is equivalent to [3,3][3], and accessing the fourth item in the array [3,3] will yield undefined.
The + sign at least makes sense because Array inherits from Object and that also happens if you try new Date() + new Date() (and this automatic toString call might be useful sometimes, although I would not design the language this way).
Yes, the + operator is complicated in JS, as it deals with multiple different types and does either (numeric) addition or (string) concatenation.
This is even worse when you use it on objects. In that case, it is tried to be cast to a primitive value (string, number, boolean), and for that the [[DefaultValue]] algorithm is applied (with no hint). When both operands are identified to be numeric, the addition is performed - this can even happen to objects:
> 1 + {valueOf: function(){ return 2; }}
3
> 1 + {valueOf: function(){ return "2"; }}
"12"
This question already has answers here:
Why does ++[[]][+[]]+[+[]] return the string "10"?
(10 answers)
(![]+[])[+[]]... Explain why this works
(1 answer)
Closed 9 years ago.
While reading this article posted on dzone I found a snippet of JavaScript originally posted on Twitter by Marcus Lagergren.
The following code apparently prints the string "fail"
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]];
This involves implicit type casting and I'm trying to understand how exactly this line is interpreted.
I've isolated each character
(![]+[])[+[]] prints "f"
(![]+[])[+!+[]] prints "a"
([![]]+[][[]])[+!+[]+[+[]]] prints "i"
(![]+[])[!+[]+!+[]] prints "l"
I've also managed to break down the expressions returning each letter apart from "i"
letter "f"
![] an empty array is an Object, which according to ECMAScript documentation, point 9.2 evaluates to true when converted to a boolean so this is false
false+[] as per Point 11.6.1 both arguments of the binary + operator get converted to String, therefore we get "false"+"", which evaluates "false"
+[] a unary plus operator causes a ToNumber conversion followed by a ToPrimitive conversion if the argument is an Object. The result of such conversion is determined by calling the [[DefaultValue]] internal method of the object. In case of an empty array, it defaults to 0.
(ECMAScript Documentation, sections: 11.4.6, 9.3, 9.1 )
"false"[0] we're accessing the character at index 0, hence the "f"
letter "a"
Same story, the only difference here are additional conversions in the part in square brackets (which evaluates to a number to point at another character in the string "false"), triggered by the use of unary + and ! operators.
+[] evaluates to 0, as explained above.
!0 evaluates to true as defined in Section 9.2 and Section 11.4.9. First, 0 is converted to a boolean false and then the operator inverts the value.
+true again, the unary plus triggers a ToNumber conversion, which returns a 1 for binary true
(Section 11.4.6 and 9.3)
"false"[1] returns the second character in the string, which is "a"
letter "l"
!+[] evaluates to true as explained above
true+true using the binary + on primitives triggers a ToNumber conversion. In case of true, its result is 1 and 1+1 equals 2
"false"[2] - self explanatory
letter "i"
What leaves me stumped is the letter "i". I can see that the second part (in square brackets) evaluates to the string "10" and that the first part (in parentheses) returns "falseundefined" but I can't make heads or tails of how this is happening. Could someone explain it step by step? Especially the magic that happens with square brackets? (arrays and array access)
If possible, I'd like each step to contain a link to the underlying ECMAScript rules.
What I find the most cryptic is this part: [][[]]
Your cryptic part isn't all that cryptic if you rewrite it a little:
[]['']
[] will be coerced into a string because it isn't an integer, so you're looking for a property of [] with the name '' (an empty string). You'll just get undefined, as there is no property with that name.
As for the actual letter, break the expression up into the two main components:
The string ([![]]+[][[]]):
[![]] is [false].
[][[]] is undefined.
Add them together and you get "falseundefined".
And the index: [+!+[]+[+[]]]. Some whitespace and parentheses will make the operations much clearer: [+(!(+[])) + [+[]]]:
[+[]] is [0].
+[] coerces [] to an integer, so you get 0.
!+[] coerces 0 to a boolean and negates it, so you get true.
+!+[] coerces true to an integer, so you get 1.
Add them together, and you get ["10"].
When using a string to access the properties of the array and the string happens to be an element of the array, the string is coerced into an integer and you get back the actual element of the array:
> [1, 2, 3]["0"]
1
> [1, 2, 3]["1"]
2
So your final result is:
> "falseundefined"["10"]
"i"
Read this answer for an explanation of the [false] + undefined part.
([![]]+[][[]])[+!+[]+[+[]]] has two parts :
([![]]+[][[]]) and the other which you found yourself.
![] returns false. Then we use [...] to get the .toString() behavior of +.
([]+[] is the same as [].toString()+[].toString())
the [][[]] is undefined because we're trying to access the index [] (or [].toString(), which is '') of [] which is undefined.
Sorry for the previous answered, I misread your comment totally.
Why the first line below gives error although the second and third lines work fine?
1.toString(); // SyntaxError
(1).toString(); // OK
1['toString'](); // OK
The . presents ambiguity. Is it a decimal, or a property accessor?
The interpreter sees it as a decimal, so you can use .. to allow both the decimal, then the property syntax.
1..toString();
Or use one of the other ways you show to resolve the ambiguity.
The parser is trying to treat 1. as the start of a floating-point literal -- only toString turns it into an invalid number.
Compare with:
1.0.toString()
In (1).toString(), (1) forces it to evaluate before .toString() so it works.
In 1.toString(), 1 is not a valid identifier so it does not work.
In Javascript, using the dot (.) can be interpreted in one of two ways:
As a Property Accessor (e.g., var prop = myObject.prop;).
As part of a Floating-point Literal (e.g. var num = 1.5;).
In the above case, the leading 1. in 1.toString() is interpreted as a floating point number, hence the error:
SyntaxError: identifier starts immediately after numeric literal (learn more)
This is the same error you get if you try and declare a variable that starts with a number: var 1person = 'john';
To prevent the interpreter from seeing the 1. as a decimal and instead see it as accessing a property on our literal 1, there are several ways to accomplish this:
// Via white-space after the numeric literal
1 .toString();
1
.toString();
// Via a grouping-operator, aka, parentheses
// #see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Grouping_operator
(1).toString();
// Via an additional dot. Made clearer with parentheses as `(1.).toString()`
1..toString();
// Via an explicit fractional part (because `1. === 1.0`)
1.0.toString();
// Via bracket notation
1['toString']();
1.['toString']();
1.0['toString']();