I have a twofold question which involves something I would consider to be incorrect Javascript code.
How is the following statement interpreted in Javascript, and why?
(1,2,3,4)
Why is there a difference between these two invocations:
var a = (1,2,3,4);
a();
which leads to a being equal to 4 and Uncaught TypeError: a is not a function being thrown, and
(1,2,3,4)();
which leads to Uncaught TypeError: (((1 , 2) , 3) , 4) is not a function?
How is the following statement interpreted in Javascript, and why?
(1,2,3,4)
That's a comma operator expression (actually, a chain of them) wrapped in grouping parentheses. The comma operator is quite unusual: It evalutes both of its operands, then takes the value of the second one as its value, throwing away the value of the first one. You have a chain of them there, so the value of 1 is evaluated, then 2, then 3, then 4, and the result of the comma operator chain is the value 4; the result of the grouped parentheses expression is therefore 4.
Why is there a difference between these two invocations:
var a = (1,2,3,4);
a();
Because of the syntax of the language. In the first case, it's clearly not a function call, as there's no value prior to the first ( to call. The parsing rules for a complex language like JavaScript are just that: Complex. The parser is context-sensitive, and knows how to differentiate between grouping parentheses and function-call parentheses.
which leads to a being equal to 4 and Uncaught TypeError: a is not a function being thrown, and
(1,2,3,4)();
which leads to Uncaught TypeError: (((1 , 2) , 3) , 4) is not a function?
In both cases, the error message is quoting the expression that yielded the result it then tried to call as a function.
There is an operator in JS (among other languages) called the comma operator. It simply takes two operands, and returns the rightmost one.
a = 1, 2; // a now equals 2
It is, however, not the same comma as the function parameter separator. It is an operator.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
So, the statement (1,2,3,4); returns 4, so var a = (1,2,3,4); means that a is equal to 4 which, is not a function, thus the error.
Likewise, (1,2,3,4) is just a grouping of comma operators and not a function, thus the second error.
Related
I have a twofold question which involves something I would consider to be incorrect Javascript code.
How is the following statement interpreted in Javascript, and why?
(1,2,3,4)
Why is there a difference between these two invocations:
var a = (1,2,3,4);
a();
which leads to a being equal to 4 and Uncaught TypeError: a is not a function being thrown, and
(1,2,3,4)();
which leads to Uncaught TypeError: (((1 , 2) , 3) , 4) is not a function?
How is the following statement interpreted in Javascript, and why?
(1,2,3,4)
That's a comma operator expression (actually, a chain of them) wrapped in grouping parentheses. The comma operator is quite unusual: It evalutes both of its operands, then takes the value of the second one as its value, throwing away the value of the first one. You have a chain of them there, so the value of 1 is evaluated, then 2, then 3, then 4, and the result of the comma operator chain is the value 4; the result of the grouped parentheses expression is therefore 4.
Why is there a difference between these two invocations:
var a = (1,2,3,4);
a();
Because of the syntax of the language. In the first case, it's clearly not a function call, as there's no value prior to the first ( to call. The parsing rules for a complex language like JavaScript are just that: Complex. The parser is context-sensitive, and knows how to differentiate between grouping parentheses and function-call parentheses.
which leads to a being equal to 4 and Uncaught TypeError: a is not a function being thrown, and
(1,2,3,4)();
which leads to Uncaught TypeError: (((1 , 2) , 3) , 4) is not a function?
In both cases, the error message is quoting the expression that yielded the result it then tried to call as a function.
There is an operator in JS (among other languages) called the comma operator. It simply takes two operands, and returns the rightmost one.
a = 1, 2; // a now equals 2
It is, however, not the same comma as the function parameter separator. It is an operator.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
So, the statement (1,2,3,4); returns 4, so var a = (1,2,3,4); means that a is equal to 4 which, is not a function, thus the error.
Likewise, (1,2,3,4) is just a grouping of comma operators and not a function, thus the second error.
I don't understand why the following evaluates to 3 instead of just declaring a syntax error when ran from a JavaScript REPL or through Chrome's Developer Tools:
{1, 2, 3};
3
As far as I can see, that should be a syntax error as demonstrated with:
var foo = {1, 2, 3};
Uncaught SyntaxError: Unexpected token ,
I feel like there's just some quirky behaviour I'm not aware of?
Here's the breakdown of the symbols:
{ Start code block
1 Number literal
, Comma operator (evaluates both sides, returns right side)
2 Number literal
, Comma operator
3 Number literal
} End code block
Code blocks aren't restricted to defining if, while etc. blocks, they can be used anywhere. Therefore, your code is simply a block that contains a chained comma operator sequence, which returns the last item in the chain, hence 3.
In the case of var foo = {1, 2, 3};, the { is indeed a "start object literal" symbol and not a "start code block" symbol.
The same symbol can have multiple meanings based on context.
Today I figured out that it is possible to use operations before the parenthesis of a function call.
E.g.
console.log|('Hello world!');
or
console.log>>(33);
Why is this possible and what happens?
Like in most "algol-like" languages, parentheses have multiple meanings in javascript:
grouping operator, as in 2 * (3 + 4)
function call operator, as in console.log(5)
part of function declaration syntax: function(x) {}
When you insert an operator between a function name and a function call parenthesis, this one becomes the grouping operator. It's not a function call anymore. So func(args) turns into func op (args), which is syntactically a valid expression, because functions are "first-class citizens" in javascript and can be used in expressions like any other value. Whether it makes sense is another question though, because, apart from +, operators don't produce anything meaningful when applied to functions.
Simply put: because an operator works on operands, and functions are, in JS at least, first class objects (meaning they can be passed around, and returned, and operated on).
To do so, you'll of course need to use operators. A simple, not too far fetched example:
function foo()
{
return foo.bar;//. is an operator
}
console.log(foo());//logs undefined
foo.bar = '123';//again, the . operator
console.log(foo());//logs 123
What happens in your case, however, is JS will evaluate both operands (the expressions on either side of the operator):
console.log (LOperand)
| (operator)
('hello world') (ROperand, to be evaluated further still because of the grouping operator ())
And attempt to evaluate them to compatible types for the operator to do its work. console.log is an object, a function instance in fact. hello world is a string constant inside a the grouping operator.
How these types will be coerced and evaluated is specified in the ECMAScript standard (google it), but bottom line, the function (console.log) could well be coerced to a string (as if you'd call console.log.toString(), which gives "function log() { [native code] }" on chromium). The upshot is that the code behaves the same as:
"function log() { [native code] }"|"Hello world"
which yields 0
Actually, you are not executing the function, you are just evaluating an expression (that makes no sense at all)
The console.log|('Hello world!'), will be evaluated as:
[native function] | 'Hello world!'
Which makes no sense, since "|" is a bitwise operator. Same as the other example that you gave.
So, you are not placing the operator between the function call. You are separating the values, and no longer performing the expected action (funcion call).
It is possible to use operations before the parenthesis of a function call.
Not quite so,
console.log|('Hello world!');
is equivalent to
console.log | 'Hello world!';
which has a different meaning than you intended :)
('Hello world!') is the same as 'Hello world!' and | is the bitwise OR operator in JavaScript (left | right will match the left operand with the right operand bit by bit).
In your example, console.log without parenthesis returns the function itself (as opposed to console.log() which returns undefined), so you are basically [Function] | 'Hello world!'.
Rather than use an if else statement, I'm trying to use the ternary operator but have a syntax error somewhere in my statement.
Can someone tell me where I am going wrong?
Statement is:
my_alert(status ? ('Accepted', 'alert-success') : ('Declined', 'alert-info'))
my_alert is a function which has 2 parameters.
Status just evaluates to true or false.
When I pass more than 1 parameter into the above expression, it doesn't like the use of the comma.
In chrome and firefox when the function runs it displays 'alert-success' or 'alert-info'. It misses out the first parameter.
I've looked on stackoverflow for the answer but by all means it's telling me that what i'm doing is correct.
Any help would be great.
Well, the comma operator does the following:
The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand.
That means, ('Accepted', 'alert-success') evaluates to 'alert-success' (as you already noticed). The comma here is different than the comma that separates function arguments. You cannot use it to pass two arguments to a function.
What you can do is store both arguments in an array and use .apply to pass them to the function:
// this is not the comma operator either, this is array literal syntax.
var args = status ? ['Accepted', 'alert-success'] : ['Declined', 'alert-info'];
my_alert.apply(null, args);
I don't think ternary operators can be used to control two values like that:
How about separating them:
my_alert(($status?"Accepted":"Declined"),($status?"alert-success":"alert-info"));
Alternatively, you could just wrap the function call in the ternary statement...
status ? my_alert("Accepted", "alert-success") : my_alert("Declined", "alert-info");
UPDATE:
Robin van Baalen makes a good suggestion...
my_alert.apply(this, status ? ["Accepted", "alert-success"] : ["Declined", "alert-info"]);
You can't use the comma like that. If you want to pass 2 parameters, you need to use 2 ternary statements.
my_alert((status ? 'Accepted' : 'Declined'), (status ? 'alert-success' : 'alert-info'));
In your case, the comma is read a the comma operator, which evaluates both operands and returns the last one. So, your ternary statement was equivalent to:
my_alert(status ? 'alert-success' : 'alert-info')
At wtfjs, I found that the following is legal javascript.
",,," == Array((null,'cool',false,NaN,4)); // true
The argument (null,'cool',false,NaN,4) looks like a tuple to me, but javascript does not have tuples!
Some quick tests in my javascript console yields the following.
var t = (null,'cool',false,NaN,4); // t = 4
(null,'cool',false,NaN,4) === 4; // true
(alert('hello'), 42); // shows the alert and returns 42
It appears to behave exactly like a semicolon ; separated list of statements, simply returning the value of the last statement.
Is there a reference somewhere that describes this syntax and its semantics? Why does it exist, i.e. when should it be used?
You are seeing the effect of the comma operator.
The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand.
The resultant value when a,b,c,...,n is evaluated will always be the value of the rightmost expression, however all expressions in the chain are still evaluated (from left to right).
As already explained this behaviour is caused by , operator. Due to this the expression (null,'cool',false,NaN,4) will always evaluate to 4. So we have
",,," == Array(4)
Array(4) - creates new array with allocated 4 elements. At the time of comparison with the string this array is converted to string like it would be with Array(4).toString(). For arrays toString acts like join(',') method called on this array. So for the empty array of 4 elements join will produce the string ",,,".
Try this alert((null,'cool',false,NaN,4)) and then you can see.
demo
The reason is because the comma operator evaluates all the statements and return the last one.
Think of this line: a = 1, b = 2, c = 3; it will run each expression so in essence it will set the variables to what you want and return the last value (in this case 3)