var test=(a=1,[b=2]["sort"])();
This code works in Firefox resulting test=window (window object),
Is it valid JavaScript code? (I failed to find it in JavaScript references)
It's "valid" but looks completely pathological to me. From the name of the var, I'd guess someone came up with this as a feature test at some point, but failed to add a comment explaining why.
So here's what it's doing. First, the two assignments will resolve to the assigned value, so we can replace them (they do assign variables, which is a side effect, but that doesn't affect the evaluation of this expression):
var test=(1, [2]["sort"])();
["sort"] is just .sort:
var test=(1, [2].sort)();
The comma operator will return the last value in the brackets, so we can lose that 1:
var test=([2].sort)();
So now the bracketed part is creating an array with the number 2 in it, and finding the sort method of that array. It then calls that method, but because of the first set of brackets it calls it without a specified context.
In non-strict mode, a function called with no context gets window as its this.
So it tries to sort window and returns the result, which is window, as you saw.
In strict mode, which the JS consoles in Firebug and Chrome are, functions called without context get undefined as their this, which means this example throws an error, as mplungjan noted above. https://developer.mozilla.org/en/JavaScript/Strict_mode
I would expect that code to give an error.
The comma basically evaluates the expression on the left, a=1 and then the expression on the right [b=2]["sort"] and returns the result of the expression on the right.
a=1 sets a to 1, creating a as a global if it's not in the current scope.
[b=2] sets b to 2, creating b as a global if it's not in the current scope, and also creates a one-element array with the value 2.
[b=2]["sort"] returns the array .sort method (it does not call the method).
So the result of the expression in parentheses is the array .sort method which is then executed by the final (), and the result would be assigned to test except that it doesn't work because by then it is not actually called on an array.
The final assignment is the equivalent of this: var test = ([2].sort)();.
Related
This is a very basic JS question, but this "issue" drives me crazy.
From simple algebra I would expect that both first and the second statement are vaild. But the second is always throwing "Invalid assignment" error.
Does anyone have a good explanation for it?
fieldname1 = document.getElementById("emailID1");
document.getElementById("emailID2") = fieldname2;
Thanks so much,
Most common programming languages, including JavaScript, require that the left-hand side of an assignment (the "target") be something called an l-value. That means it's an expression that denotes a place to put a value. A simple variable name, or a reference to an object followed by .propertyName suffix, works as an l-value.
In your case, the function call return value is not an l-value, because JavaScript does not make that possible (some languages do). A function call is always an r-value, meaning something that appears on the right-hand side of an assignment.
Now, in your particular case, because getElementById() returns a reference to a DOM element, you can do something like this:
document.getElementById("something").name = "frederick";
The function still returns an r-value, but that .name works as a property reference and thus as an l-value.
The assignment operator resolves the right side of the equal sign and stores it in the variable on the left side, which is what is happening in the first line.
The second line is basically trying to take the value of a variable fieldname2 and store it in a function call document.getElementById("emailID2")
JavaScript doesn't know how to resolve that at runtime, so it's throwing an invalid assignment operation.
There's more information on assignment from MDN here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
You can't assign a value to an object itself in this case.
document.getElementById("emailID2") = fieldname2;
As i guess you want to do something like this:
document.getElementById("emailID2").name = fieldname2;
var a = [1, 2, 3, 4];
var b = [10, 20, 30, 40];
console.log([a, b].length)
[a, b].some(function(x) {
x.push(x.shift())
});
I was extremely surprised today when this code caused
[a,b].some(function(x){ x.push(x.shift()) });
^
TypeError: Cannot call method 'some' of undefined
Obviously the JavaScript 'auto semicolon insertion' is not working as expected here. But why?
I know you might recommend to use ; everywhere to avoid something like that, but the question is not about whether it is better to use ; or not. I would love to know what exactly happens here?
When I'm worried about semicolon insertion, I think about what the lines in question would look like without any whitespace between them. In your case, that would be:
console.log([a,b].length)[a,b].some(function(x){ etc });
Here you're telling the Javascript engine to call console.log with the length of [a,b], then to look at index [a,b] of the result of that call.
console.log returns a string, so your code will attempt to find property b of that string, which is undefined, and the call to undefined.some() fails.
It's interesting to note that str[a,b] will resolve to str[b] assuming str is a string. As Kamil points out, a,b is a valid Javascript expression, and the result of that expression is simply b.
In general, one could say that implicit semi-colon's can easily fail when defining an array on a new line, because an array defined on a new line is interpreted as a property access of the value of the expression on the previous line.
Javascript does only consider new lines to mark the end of a statement if not ending the statement after this new line would cause a parse error. See What are the rules for JavaScript's automatic semicolon insertion (ASI)? and EcmaScript 5 spec for the exact rules. (Thanks to Rob W and limelights)
What happens is the following:
The code get interpreted as
console.log([a,b].length)[a,b].some(function(x){ x.push(x.shift()) });
i.e. all as one statement.
Now parse the statement:
some is called on the value of console.log([a,b].length)[a,b]
the value of console.log([a,b].length)[a,b] is computed by taking the returned value of console.log([a,b].length) (undefined) and then trying to access the property with the name of the value of a,b.
a,b evaluates to the value of b (try it in your console). There's no property with the value of b of undefined, so the resulting value will be undefined as well.
There's no method some on undefined, hence the error.
JavaScript doesn't treat every line break as a semicolon. It usually treats line
breaks as semicolons only if it can’t parse the code without the semicolons. Basically, JavaScript treats a line break as a semicolon if the next non-space character cannot be interpreted as a continuation of the current statement. JavaScript - The Definitive Guide: 6th Ed. section 2.4
So, in your case, it is interpreting the line as something like
console.log([a,b].length)[a,b].some(function(x){ x.push(x.shift()) });
And that is the reason for error. JavaScript is trying to perform array-access on the results of console.log([a,b].length). Depending on the JavaScript engine and the return value of console.log, you might get different errors.
If it is the last statement of the function or flow, you can avoid ';' but it is recommended to put ';' at the end of the each statement to avoid such error.
I saw this way to execute the code:
[eval][0]('alert("hello")')
I want to know what does unquoted 'eval' surrounded by square brackets mean and why this works. (For example, window['eval'][0]('alert("hello")') will lead to a TypeError). Is there any tutorials that describe such a syntax?
That is an indirect eval call
Quoting from the article:
According to ES5, all of these are indirect calls and should execute
code in global scope.
So you can use these kind of "tricks" to execute eval in the global scope instead of the current one.
It works before [eval] is an array where its 0-th element is the eval function, that is, [eval][0] === eval, and you are invoking it passing the string 'alert("hello")' as the argument.
The reason your example leads to a TypeError:
window['eval'][0]('alert("hello")')
is because you left out one set of brackets that would have made it equivalent:
[window['eval']][0]('alert("hello")')
The initial syntax you gave constructs an array with one element, the function eval. The 0th element of this array is therefore the eval function, which is immediately dereferenced using the square brackets.
Unquoted eval surrounded by brackets is an Array with one element which is eval.
[eval][0]('alert("hello")')
means take the first element of the Array [eval] and pass 'alert("hello")' to it. In other words,
eval('alert("hello")')
window['eval'][0]('alert("hello")')
on the otherhand tries to get the first element of window['eval'] which is not an Array, and thus the TypeError. window['eval'] is the same as eval unless there is another variable in scope called eval.
In Javascript
[x]
is a list of one element containing x. thus
[x][0]
is just a wordy and inefficient way of saying x.
Your example
[eval][0]('alert("hello")')
is therefore just like
eval('alert("hello")')
This kind of trickery is normally found in Javascript trojan or viruses, where the author tries to disguise what is happening.
Javascript however is full of special cases and when used with eval the code can also have the meaning of forcing global evaluation instead of local evaluation.
function defun(s) { [eval][0](s); }
function defun2(s) { eval(s); }
defun2("function square(x){return x*x}")
---> undefined
square(12)
---> ReferenceError: square is not defined
defun("function square(x){return x*x}")
---> undefined
square(12)
---> 144
See Matteo Tassinari answer link for details (note that the linked article is a bit dated, so ignore details about webkit behavior).
I was expecting JavaScript to reject objects with duplicated properties as invalid but it accepts them in some cases.
{"a":4,"a":5} results in an SyntaxError at least in Firefox and Chrome which seems obvious due to the property a being defined twice.
However ({"a":4,"a":5}) evaluates just fine and results in an object {"a":5} in both Firefox and Chrome.
Why is the expression with the parenthesis accepted?
Summing up the responses: The first example is simply not the construction of an object but a block of labeled statements. Duplicated properities in objects are perfectly valid in which case the last definition wins.
Thanks a lot for your answers!
It is perfectly legal in ECMAScript 3 to declare duplicate properties in an object literal; the SyntaxError you get probably comes from the fact that you used an object literal as a statement, which is not possible due to the confusion with block statements ({ doSomething(); }).
If you want this to be reported as an error, you may want to switch to ECMAScript 5's strict mode: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode.
What you state has no problem if you assign it to a variable, if you don't however, you get the error you mention. Which makes all the difference from a syntax point of view.
When you wrap any structure in parens you are causing that syntax to be evaluated as an expression, the result of which is stored as a temporary variable. The error I get when not doing so in Firefox is unexpected label or invalid label, so it seems without assignment, or parens, this object construction is not treated as an object construction - instead it is treated as a block with multiple label statements that are defined illegally:
{
a: function(){
alert('a');
},
b: function(){
alert('b');
}
}
The above should be totally acceptable as an object, however you get a similar error if you evaluate it without assinging it to some form of variable, or evaluating it with parens. Put simply the duplication of the attribute name is not causing the error :)
Basically imagine your first example, but like this:
function (){
"a": 4,
"b": 5
}
That is roughly how these browsers are treating it, which is now obviously illegal javascript syntax... whereas it wasn't so obvious before.
In the first notation (parentheses-less) the javascript syntax is ambiguous. From ecmascript specification:
An ExpressionStatement cannot start with an opening curly brace
because that might make it ambiguous with a Block.
A block basically evaluates all the statements inside, equivalent to evaluating "a":4,"a":5 which is not valid JS and, in fact, returns the same SyntaxError Unexpected token :
Wrapping that code in parentheses (or, rather, a grouping operator) removes that ambiguity since an assignment expression cannot be followed by a block statement:
var test = {"a":"a","a":"b"}; //test.a === "b"
Furthermore this ambiguity can be removed by any operator or expression that cannot be used with a block statement. A practical scenario hardly comes to mind, maybe if you wanted to return an object literal as part of a conditional operator?
//this *could* come out of a JS minifier:
return x ? (foo(),{"a":"b"}) : (bar(), {"b":"a"});
Why should it not be accepted? You're simply overwriting the values. I think it's rather a feature, than an error. And it works fine for me on various browsers: http://jsbin.com/oculon/1/edit
It's like writing
var a;
a = 4;
a = 5;
alert(a);
it's not Error you just overwrite value with another
I'm guessing (though not certain) that this evaluates as an error because of the difference between the way Firefox and Chrome's JS parsers treat statements and expressions. So because it's wrapped in parentheses the second time, it's considered an expression. Since it's looking for less information in an expression, it can ignore erroneous values. You'll see that if you do...
var a = {'a': 5, 'a': 4};
console.log(a);
It works fine! And also notice that here it's in the right hand side of the statement, giving a hint that it's an expression.
Original source: http://twitter.com/tobeytailor/status/8998006366
(x=[].reverse)() === window // true
I've noticed that this behavior affects all the native types. What exactly is happening here?
This is to do with the weird way this binding works in JavaScript.
[].reverse
is the method reverse on an empty list. If you call it, through one of:
[].reverse();
[]['reverse']();
([].reverse)();
then it executes with this bound to the list instance []. But if you detach it:
x= [].reverse;
x();
it executes with no this-binding, so this in the function points to the global (window) object, in one of JavaScript's worst, most misleading design mistakes.
(x=[].reverse)()
Is also doing the detach. The assignment operator returns the same function object it was passed so it looks like it's doing nothing, but it has the side-effect of breaking the limited special case that causes JavaScript to bind this.
So you are saying:
Array.prototype.reverse.call(window)
reverse, like many other Array.prototype methods, is defined by ECMAScript to work on any native sequence-like object. It reverses the items with number-string keys (up to object.length) and returns the object. So it'll return the object that was passed in for any type that has a length property.
window has a length property, which corresponds to window.frames.length, so calling this method with this pointing at window will work and return the window. In theory it may still fail, because:
window is allowed to be a “host object” rather than a “native object”; in this case the guarantees about what you can pass to other prototypes' methods don't necessarily apply; and
if the window actually has frames/iframes, it would try to reverse their order, which wouldn't work because the frame collection is read-only.
However, in current browsers the former case does work and the latter fails silently without an error, so you still get the ===window behaviour and not an Exception.