I have a function that looks like this:
function SomeFunction() {
var SomeVar;
if (SomeCondition) {
SomeVar = 4;
}
}
This is equivalent to:
function SomeFunction() {
if (SomeCondition) {
var SomeVar = 4;
}
}
Does using the var statement only if the condition is true make a difference or not, is there any best practice or performance implications concerning this?
Thanks.
Edit: yes, I am aware that there's no block scope, just function scope.
They function exactly the same, but there are two schools of thought about where to put it:
One group says you should put it at the top of the function, because that avoids the false impression you are relying on (nonexistent) block-scoping.
The other group says you should put it in the appropriate block, because that signals you intend to treat the variable as if it were block-scoped and not use it anywhere else.
As far as I know, I am the sole member of the second group.
Where the var statement is put has no effect on your code. This is purely a matter of style.
Some people feel that it's much clearer to put the var at the start of the function block. The lifetime of a variable in Javascript is tied to the function in which it's declared and not the scope. Many other curly brace languages do the opposite and this is a source of confusion for many new users. Hence people believe declaring at the start of the function leads to clearer code.
In JavaScript, there is no block scope for variables like in C/C++. Therefore, even if you declare your variable in the if () {} block, the variable is actually created immediately at the beginning of the function. This effect of "pulling declarations up" is usually called "hoisting".
Although both codes are correct, the first one is probably semantically better, as it emphasizes the fact that the variable exists regardless of whether SomeCondition is true.
Those are both actually equivalent. Read up on "hoisting" and "function and block scope".
As far as speed goes, you're better off using the second case - combining the declaration and assignment in the same statement. See: http://jsperf.com/stackoverflow-frenchie
Put it at the beginning of the function. It is always moved there anyway during the runtime, but it clears the confusion / untrue assumption if you put it where it is really executed.
So in you case, the first snippet.
(also, purely style, write identifier of everything except classes camelCase, not PascalCase)
Related
I've wanted to switch to Coffeescript for a while now and yesterday I thought I'm finally sold but then I stumbled across Armin Ronachers article on shadowing in Coffeescript.
Coffeescript indeed now abandoned shadowing, an example of that problem would be if you use the same iterator for nested loops.
var arr, hab, i;
arr = [[1, 2], [1, 2, 3], [1, 2, 3]];
for(var i = 0; i < arr.length; i++){
var subArr = arr[i];
(function(){
for(var i = 0; i < subArr.length; i++){
console.log(subArr[i]);
}
})();
}
Because cs only declares variables once I wouldn't be able to do this within coffeescript
Shadowing has been intentionally removed and I'd like to understand why the cs-authors would want to get rid of such a feature?
Update: Here is a better example of why Shadowing is important, derived from an issue regarding this problem on github
PS: I'm not looking for an answer that tells me that I can just insert plain Javascript with backticks.
If you read the discussion on this ticket, you can see Jeremy Ashkenas, the creator of CoffeeScript, explaining some of the reasoning between forbidding explicit shadowing:
We all know that dynamic scope is bad, compared to lexical scope, because it makes it difficult to reason about the value of your variables. With dynamic scope, you can't determine the value of a variable by reading the surrounding source code, because the value depends entirely on the environment at the time the function is called. If variable shadowing is allowed and encouraged, you can't determine the value of a variable without tracking backwards in the source to the closest var variable, because the exact same identifier for a local variable can have completely different values in adjacent scopes. In all cases, when you want to shadow a variable, you can accomplish the same thing by simply choosing a more appropriate name. It's much easier to reason about your code if a local variable name has a single value within the entire lexical scope, and shadowing is forbidden.
So it's a very deliberate choice for CoffeeScript to kill two birds with one stone -- simplifying the language by removing the "var" concept, and forbidding shadowed variables as the natural consequence.
If you search "scope" or "shadowing" in the CoffeeScript issues, you can see that this comes up all the time. I will not opine here, but the gist is that the CoffeeScript Creators believe it leads to simpler code that is less error-prone.
Okay, I will opine for a little bit: shadowing doesn't matter. You can come up with contrived examples that show why either approach is better. The fact is that, with shadowing or not, you need to search "up" the scope chain to understand the life of a variable. If you explicitly declare your variables ala JavaScript, you might be able to short-circuit sooner. But it doesn't matter. If you're ever unsure of what variables are in scope in a given function, you're doing it wrong.
Shadowing is possible in CoffeeScript, without including JavaScript. If you ever actually need a variable that you know is locally scoped, you can get it:
x = 15
do (x = 10) ->
console.log x
console.log x
So on the off-chance that this comes up in practice, there's a fairly simple workaround.
Personally, I prefer the explicitly-declare-every-variable approach, and will offer the following as my "argument":
doSomething = ->
...
someCallback = ->
...
whatever = ->
...
x = 10
...
This works great. Then all of a sudden an intern comes along and adds this line:
x = 20
doSomething = ->
...
someCallback = ->
...
whatever = ->
...
x = 10
...
And bam, the code is broken, but the breakage doesn't show up until way later. Whoops! With var, that wouldn't have happened. But with "usually implicit scoping unless you specify otherwise", it would have. So. Anyway.
I work at a company that uses CoffeeScript on the client and server, and I have never heard of this happening in practice. I think the amount of time saved in not having to type the word var everywhere is greater than the amount of time lost to scoping bugs (that never come up).
Edit:
Since writing this answer, I have seen this bug happen two times in actual code. Each time it's happened, it's been extremely annoying and difficult to debug. My feelings have changed to think that CoffeeScript's choice is bad times all around.
Some CoffeeScript-like JS alternatives, such as LiveScript and coco, use two different assignment operators for this: = to declare variables and := to modify variables in outer scopes. This seems like a more-complicated solution than just preserving the var keyword, and something that also won't hold up well once let is widely usable.
The main issue here isn't shadowing, its CoffeeScript conflating variable initialization and variable reassignment and not allowing the programmer to specify their intent exactly
When the coffee-script compiler sees x = 1, it has no idea whether you meant
I want a new variable, but I forgot I'm already using that name in an upper scope
or
I want to reassign a value to a variable I originally created at the top of my file
This is not how you forbid shadowing in a language. This is how you make a language that punishes users who accidentally reuse a variable name with subtle and hard to detect bugs.
CoffeeScript could've been designed to forbid shadowing but keep declaration and assignment separate by keeping var. The compiler would simply complain about this code:
var x = blah()
var test = ->
var x = 0
with "Variable x already exists (line 4)"
but it would also complain about this code:
x = blah()
test = ->
x = 0;
with "Variable x doesn't exist (line 1)"
However, since var was removed, the compiler has no idea whether you meant meant "declare" or "reassign" and can't help.
Using the same syntax for two different things is not "simpler", even though it may look like it is. I recommend Rich Hickey's talk, Simple made easy where he goes in depth why this is so.
Because cs only declares variables once the loop will not work as intended.
What is the intended way for those loops to work? The condition in while i = 0 < arr.length will always be true if the arr is not empty, so it will be an infinite loop. Even if it's only one while loop that won't work as intended (assuming infinite loops are not what you're looking for):
# This is an infinite loop; don't run it.
while i = 0 < arr.length
console.log arr[i]
i++
The correct way of iterating arrays sequentially is using the for ... in construct:
arr = [[1,2], [1,2,3], [1,2,3]]
for hab in arr
# IDK what "hab" means.
for habElement in hab
console.log habElement
I know that this answer sound like going off on a tangent; that the main point is why CS discourages variable shadowing. But if examples are to be used to argument in favour of or against something, the examples ought to be good. This example doesn't help the idea that variable shadowing should be encouraged.
Update (actual answer)
About the variable shadowing issue, one thing that it worth clarifying is that the discussion is about whether variable shadowing should be allowed between different function scopes, not blocks. Within the same function scope, variables will hoist the whole scope, no matter where they are first assigned; this semantic is inherited from JS:
->
console.log a # No ReferenceError is thrown, as "a" exists in this scope.
a = 5
->
if someCondition()
a = something()
console.log a # "a" will refer to the same variable as above, as the if
# statement does not introduce a new scope.
The question that is sometimes asked is why not adding a way to explicitly declare the scope of a variable, like a let keyword (thus shadowing other variables with the same name in enclosing scopes), or make = always introduce a new variable in that scope, and have something like := to assign variables from enclosing scopes without declaring one in the current scope. The motivation for this would be to avoid this kind of errors:
user = ... # The current user of the application; very important!
# ...
# Many lines after that...
# ...
notifyUsers = (users) ->
for user in users # HO NO! The current user gets overridden by this loop that has nothing to do with it!
user.notify something
CoffeeScript's argument for not having a special syntax for shadowing variables is that you simply shouldn't do this kind of thing. Name your variables clearly. Because even if shadowing would be allowed it would be very confusing to have two variables with two different meanings with the same name, one in an inner scope and one in an enclosing scope.
Use adequate variable names according to how much context you have: if you have little context, e.g. a top-level variable, you'll probably need a very specific name to describe it, like currentGameState (especially if it's not a constant and its value will change with time); if you have more context, you can get away with using less descriptive names (because the context is already there), like loop variables: killedEnemies.forEach (e) -> e.die().
If you want to know more about this design decision, you may be interested in reading Jeremy Ashkenas opinions in these HackerNews threads: link, link; or in the many CoffeeScript issues where this topic is discussed: #1121, #2697 and others.
I apologize in advance, this might have been discussed on StackOverflow before, I just do not know what this is called, so I could not find a satisfactory answer.
However, I am learning JavaScript and with a book called "Eloquent JavaScript". There I found the following piece of code, which repeatedly prompts the user to enter his name until he did it.
while (!input) {
var input = prompt("Who are you?");
}
I simply do not understand why this actually works instead of raising an error. At the time the condition expression is being evaluated, there is no variable called input out there. If I understand it right, there is no evaluation possible, which would normally prevent further execution. The statement in the while loop's body, which then creates a variable called input, is still being executed, though.
However, this made me anxious, so I tried this:
while (!bool) {
console.log("Hi");
var bool = true;
}
This is even weirder. It's same problem when it comes to the condition expression: bool is being created within the scope of the loop's body, after the evaluation of the condition. And secondly, bool is constantly set to be true, but still the code is being executed once, in other words, Hi is being printed once.
I am confused and would appreciate some help. ;)
This is one of Javascript's little eccentricities. The key thing to remember is that, in Javascript, variables are not necessarily created at the point in the code where var x = appears. They are always created at the top of the scope. This means at the beginning of the function that contains them, or the top of the global scope if that's the scope we're working in. This is called hoisting.
So your code may look like this:
function doStuff() {
while (!bool) {
console.log("Hi");
var bool = true;
}
}
But it will run like this:
function doStuff() {
var bool = undefined;
while (!bool) {
console.log("Hi");
bool = true;
}
}
(Remember that there is no such thing as block scope in Javascript.)
The first time, bool is undefined. !undefined is true, so the conditional passes. After that, bool is true, so the conditional fails. It is a similar story with input in your first example.
The variable will always be created at the very top of the scope, no matter where it is declared. For this reason, it is sometimes recommended as good practice to declare your variables at the top of the scope, since that is where Javascript will consider them to be. This prevents surprises like the one you cite.
JavaScript does not need a variable to be declared explicitly - you can access or assign a value to the variable in before you declare it explicitly. (It has more to it, as lonesomeday has explained - I just wanted to put it simply).
When you access the variable before declaring it explicitly, it would have the value undefined.
So, in your code, try using an alert(input); before the while if you want to see it yourself.
JavaScript has the unusual property that variables are scoped at the level of the function in which they are declared, not within their enclosing block (as is the case in languages like C++ and Java). This means that a variable declared within a function is in scope the instant the function begins executing, regardless of where within the body of the function the variable is declared. This behavior is sometimes referred to as "hoisting" or "lifting".
Many JavaScript programmers consider it a good practice to declare all the variables within a function at the very beginning of the function, just to avoid the strange property of having a variable in scope before it is declared.
An even more unfortunate behavior of JavaScript is that allows one to define global variables on-the-fly simply by setting them. So, if your program has no variable named input, then this code:
input = prompt("Who are you?");
Creates a global variable named input and assigns it the string returned by the prompt method. This can lead to all sorts of hard-to-debug side-effects and so is considered poor practice.
There are 2 bits to realize. Initially, input is undefined, which is a "falsy" value, so !input evaluates to true. The second thing to realize is once the var input or var bool are assigned, they are valid at the same scope as the loop (ie just outside it), so when the loop iterates a second time the variable will be set. For the input case, anything null or undefined is considered falsy, so once the input has a value the loop will exit. You can kind of see this in this bit of code:
while(!bar) {
console.log('1', bar);
var bar = true;
console.log('2', bar);
}
console.log('3', bar);
Jslint will call out errors if functions are defined out of order.
Obviously jslint is strict, but I wonder if there are any benefits to putting functions in order.
By that I mean defining them before they are called.
Function definitions (and var statements) are hoisted to the top of their closure. In other words, code is executing in an order that is not the same as what you wrote.
Now, there is nothing more "wrong" about it than leaving off the ; at the end of lines, but jsLint is designed to test the human-readability of the code. Jumping around isn't human-readable, so it's considered invalid.
If you define function before calling it, the code can be handled by one pass compiler, otherwise, you need a multi-pass compiler (which is slower in general).
Maybe there are some one pass JavaScript compilers out there (however, I don't know any).
I always think JSLint shows that error because it wants to make sure your code can run in a one pass compiler successfully
Depends.
This will succeed:
var x = y();
function y() { return 9; }
But this will fail:
var x = y();
var y = function() { return 9; };
When the first script is interpreted it will see function y and will make that function available throughout its scope (being the global scope in that example).
Whereas with the second example a function is assigned to y. And just like any other variable the order matters.
I have a Sublimelinter installed in Sublime Text 2 and it's great. However it doesn't like the following code:
if(condition){
var result = 1;
}else{
var result = 2;
}
process(result);
It says for var result = 2; that result is already defined and for process(result); that it's used out of scope. Is it just mistaking the {} of the if statement for a more closed scope or should I really be doing it like this:
var result;
if(condition){
result = 1;
}else{
result = 2;
}
process(result);
No it is not "wrong"; it will get hoisted to the top of the nearest function definition, as per the ECMAScript specification.
Yes, your program "Sublimelinter" is incorrect to claim the variable is out of scope.
It is not wrong. If you get that error, you defined result earlier in your code.
You can also simplify your condition to this so you don't have to use result:
process( condition ? 1 : 2 );
Javascript doesn't have 'block-scoping' like many other languages. If it did, the variable result would not exist when you tried to call process(result) because it would not be possible to reference it outside of the {} block where it was defined.
However, javascript only has function scoping, where variables in one function cannot be accessed by another function. Where variables are declared in the function has no significance whatsoever, because it will still be accessible from anywhere inside that function (no block scope). Hence, both code snippets you posted are equivalence to whatever interpreter is running the code.
The second is preferable because it is clearer as it shows where the variable will be used (throughout the function). It also prevents the variable from being declared twice inside the scope of the function, which may not necessarily cause anything bad to happen, but it will definitely not cause anything beneficial. You should almost always declare a variable once at a higher level instead of twice at different lower levels, even though it doesn't really matter since the scope of the variable will be the entire function no matter where it is declared.
JavaScript does not have block scope. Variables are scoped to the function they are defined in, meaning that when you declare a variable inside of an if block, it is "hoisted" to the top of the function.
Since the variable is technically defined at the top of the function anyway, it is considered a best practice to move variable declarations to the top of the function so that the intent of the code is clear.
I'd say your code isn't "wrong" but it is misleading to people reading the code who aren't familiar with how scope works in JavaScript. I would definitely opt for the second version, because it actually reflects how the code is executed.
Here's a good article explaining variable hoisting.
Declare your var pointer once with-in the function you have your if statement at. Like ninjagecko mentioned all vars get sent to the top of their containing functions.
However; do be careful because if you declare the same var twice like you have it , it will reset the var.
I recommend doing this:
var result = MORE_LIKELY_OUTCOME;
if (LESS_LIKELY_CONDITION) {
result = LESS_LIKELY_OUTCOME;
}
process(result);
This way, you are setting result initially to what you are expecting it to be most of the times.
Then, the if statement will change result if the condition occurs.
It turns out that SublimeLinter is using JSHint which has the option to surpress this warning and explains why it exists.
funcscope This option suppresses warnings about declaring variables
inside of control structures while accessing them later from the
outside. Even though JavaScript has only two real scopes—global and
function—such practice leads to confusion among people new to the
language and hard-to-debug bugs. This is way, by default, JSHint warns
about variables that are used outside of their intended scope.
In essence, I am trying to declare a variable in the condition-part of a while loop in javascript:
while (var b=a.pop()) {
do_sth(b)
}
Yet, my browser (firefox) doesn't accept that. Instead I have to go like so:
var b
while (b=a.pop()) {
do_sth(b)
}
which works. Is this behaviour expected?
Yes, it is.
If you want to, you can use a for loop, like this:
for (var b; b = a.pop(); ) { //Note the final semicolon
do_sth(b);
}
The question is a little dated, but I think the answers all miss an important distinction. That is, a while loop expects an expression that evaluates to a conditional, i.e., a boolean or value that can be converted to a boolean. See Mozilla docs for details.
A pure assignment (without instantiation) is coerced to a boolean via its default return value (the value of the right-hand-side).
A var (or let or const) is a statement that allows an optional assignment but has a return value of undefined.
You can easily test this in your console:
var foo = 42; // undefined
bar = 42 // 42
The return values alone don't answer the question, since undefined is falsey, but does show that even if JS let you put a var in a conditional it would simply always evaluate to false.
Others have mentioned for statements and that they allow declaration and instantiation of variables. This is true, but the documentation explains that for expects a statement or assigment.
Opinions may vary, but for me all this adds up to an understandable consistency not a quirk in behavior with regard to loops. A while loop is better thought of as a looping version of an if statement than akin to a for loop. If there is quirkiness in all of this, it's the for statement's wholesale divergence from the language's normal syntax.
JavaScript does not have block scope. It has function scope. So to make sure that humans and JavaScript both read the code the same way, you should manually hoist your var declarations right up to the top of functions.
Here's what JSLint says about your code:
Problem at line 1 character 8: Expected an identifier and instead saw 'var'.
Use JSLint, at least while you're learning JavaScript. You'll learn a lot very quickly. It will hurt your feelings.
JavaScript doesn't have block scope. So all var declarations are at function scope. So declaring a variable in a while expression doesn't make sense in JavaScript.
Additionally, you should end your statements with a semicolon. It's not strictly necessary, but it's highly recommended.