3

readability aside ... in terms of efficiency and/or functionality, i'm not clear on the difference between placing a declaration outside (my practice) or inside a loop (seen in other SO posts). or for that matter, why code declarations at all? here are some degenerate examples ... more comments below.

A1:

var x;
for (i=0; i<10; i++){
    x = i;
 }

A2:

for (i=0; i<10; i++){
    var x = i;
 }

B1:

var i;
for (i=0; i<10; i++){
 }

B2:

for (var i=0; i<10; i++){
 }

C1:

var x;
var y;

C2:

var x, y;

readability aside ... i suspect that there is no difference between B1 & B2 and between C1 & C2, but am leery of an efficiency or functional difference between A1 & A2. also, i am not sure what advantages exist for declarations at all except for using declarations in functions to eliminate(?) possible conflict with a global variable of the same name.

EDIT: added a couple semicolons

EDIT: 1st sentence clarity, changed function to functionality

EDIT: adding some code below to aid in a comment below that i made EDIT: comments

<!doctype html>
<html>
<head>
<script type='text/javascript'>
var w = (function(){  // wrapper
    alert('init');
    function p(){ // private
        alert('p');
        w.b(); //needs prefix
    }
    return{
        a: function(){ // public
            alert('a'); 
            p();
            w.b(); // needs prefix 
        },
        b: function(){ // public
            alert('b'); 
        }
    };
})(); // execute immediately
</script>
<script type="text/javascript">window.onload=w.a;</script>
</head>
<body>
sequence of alerts will be 'init' 'a' 'p' 'b'
</body>
</html>
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

4 Answers4

3

Variable declarations get hoisted up to the top of the function. It won't make any difference, unless of course you're talking about declaring a variable in a new function scope.


EDIT: Didn't notice at first, your first example didn't declare i with var. That's not good practice.


EDIT:

Generally it is a good idea to avoid creating global variables in order to avoid conflicts with other scripts that may be setting globals.

Whenever you use a variable without declaring it with var, it becomes global.

The only way a new variable scope is created in JavaScript is in the body of a function. Functions can be nested inside each other, creating a scope chain that begins with the innermost function, and ends with the global scope.

Because of this, it is common to place all your code in a function that is invoked immediately. That way any variables you create with var won't end up as globals.

(function() {   // <-- create a function

    // place your code in here

      // use "var" when declaring variables so they don't become global
    var name = "patrick dw";

    alert( name );

})(); // <-- invoke the function immediately so your code runs

In the preceding example, the name variable will be local to the immediately invoked function, and won't pollute the global scope.

Because traversing the variable scope takes some resources, it is common for people to invoke that function with some arguments being passed in, like window.

(function( window, undefined ) {

      // now the window object is referenced locally. Better for performance.

    var name = "patrick dw";

    alert( name );

})( window ); // <-- invoke the function passing in the global "window" object

Now the window object is referenced locally inside your function, so if you access some property of window, it won't have to follow the scope chain all the way outside your function.

I also included an extra undefined parameter in the function, but didn't pass an argument to it. This is to help avoid an odd issue brought about by some bad coding practices. But that's another issue.

Now taking that same function, if we didn't use var in declaring our name variable, it would automatically have become global. I'll update the example, and I'll add another function scope to illustrate.

(function( window, undefined ) {

          // create a function 
    function some_func() {

        name = "patrick dw";  // didn't use "var" when declaring!!
        alert( name );
    }

    some_func();  // call our function

    alert( window.name ); // but "name" is also accessible from the global window

})( window );

alert( name ); // outside those functions, we're global, and again we can see that
               //   the "name" variable is accessible

So as you can see, we never declared var name, so it became an automatic global variable even though it was nested two functions deep.

This is the source of many errors, and is the reason you need to be extra careful to use var.


Finally, I'd note that ECMAScript 5 disallows some bad coding practices when you run your code in "strict mode";. Not many browsers implement strict mode right now, but would be helpful to test your code every so often in one that does.

Here's a compatibility table for ECMAScript 5. Strict mode is at the bottom. You'll see that Firefox 4 supports it.

Here's an example:

(function( window, undefined ) {

    "strict mode"; // <-- use the strict mode directive

    name = "patrick dw";  // In Firefox 4, you should get a ReferenceError in the console
                          //   because "name" was never declared with "var"
})( window );
user113716
  • 318,772
  • 63
  • 451
  • 440
  • @patrick - is the following correct? suppose x is coded without declaration within some function. the js complier will first look to see if x exists in a broader namespace. if found in a broader namespace, then the compiler implements x as being the one found. on the otherhand, if not found in a broader namespace, then the compiler designates x as new variable in the function's namespace? –  May 18 '11 at 20:42
  • @mike: No. First, to be accurate, we should refer to it as *scope* instead of *namespace*. Variable scope is created in functions, and functions can be nested. There's also the default global scope. If `x` has been declared in the current function scope, it will be used. If it hasn't, it will search the outer scopes to see if it has been declared. If it hasn't been declared in any scope, the variable will be added to the *global* scope, not the current function. As such, it is very important to be meticulous about explicitly defining the scope in which your variables are being declared. – user113716 May 18 '11 at 21:07
  • ...Typically it is considered good practice to not use global variables at all, or just use one at the most if you need to expose some methods or data to other javascript that is being executed. This is easily accomplished by placing your code in a function that executes immediately: `(function() { /* all your code */ })()`. Then just make sure you declare your variables with `var` in the proper places. I'll update my answer with some examples. – user113716 May 18 '11 at 21:10
  • @patrick - thanks ... yikes! re: 'If it hasn't been declared in any scope, the variable will be added to the global scope' –  May 18 '11 at 21:17
  • i have a meeting ... back in 90 –  May 18 '11 at 21:23
  • @mike: Yikes is right! I've added a few examples. Check it out when you have a chance. – user113716 May 18 '11 at 21:25
  • *Variable declarations get hoisted up to the top of the function.* Really? http://jsfiddle.net/edDYY/ – KooiInc May 18 '11 at 21:41
  • @KooiInc: Yes, that's why you get `undefined` and not a `ReferenceError`. I'm just talking about the declaration, not the initialization. – user113716 May 18 '11 at 23:19
  • @patrick - ok that was more than 90. i am trying the (function(){...})(); technique ... i need to know how to call functions within that wrapper –  May 19 '11 at 15:49
  • @patrick - maybe JDO's method here http://stackoverflow.com/questions/2613310 is the way .... only way? ... which will take a fair amount of refactoring –  May 19 '11 at 16:23
  • @mike: I'll look at that link, but basically any variables or functions declared inside another function will not be visible outside that scope. It is common to use something called a *module pattern* to expose whatever you need in a single namespace. Typically in your wrapper you create an object, assign your functions and/or data to that object, and return it from the function to a single global variable. That way you're leaving a very small amount of pollution in the global namespace. – user113716 May 19 '11 at 16:37
  • ...yes, the accepted answer to that question looks like a good example of the module pattern. Important to note that the functions in the object that is being returned will continue to have access to those local variables inside the wrapper, even though the functions have been passed out of scope. That's called creating a *closure*. The functions have "closed around" those local variables and continue to have access to them. – user113716 May 19 '11 at 16:39
  • ...depending on your needs, if you can place *all* of your code in the wrapper, then there should be no need to expose anything globally and there won't be any refactoring. If you have a separate script file that needs to call your functions, then you do need to expose them. – user113716 May 19 '11 at 16:44
  • thanks ... actually i like the idea of _module pattern_ so i may refactor. my python scripts currently place _script_ elements within the _html head_. several are of the `src='file.js'` form, and several are of the innerHTML form, e.g. `window.event=func`. i found i could place the innerHTML within the wrapper of _file.js_ and that (one) page behaves as designed without refactoring. however, _file.js_ is shared by several pages, each requiring tis own set of functions to handle the events ... some thinking to do ... –  May 19 '11 at 17:04
  • @patrick - ok, the refactoring has been not too onerous afterall ... a couple functions in each file made public by placement in a return statement. but the private functions do not seem to have access to the public functions (else infinite loop), and the public ones do not have access to each other unless dot-prefixed with variable name at the top of the file that is assigned to the wrapper. this latter issue is a bit limiting, and if that is the case(?), a bit more refactoring is in order. i will add a code example to the question. –  May 19 '11 at 21:18
  • @mike: In order to make the ones you're add as properties available without the prefix, first make them private functions in the scope, then assign them to the object being returned. `function a(){/*code*/} function b(){/*code*/} return{a:a, b:b};` But certainly you don't want to end up with an infinite loop of function invocations with `a()` and `b()` calling each other. – user113716 May 19 '11 at 22:39
  • ...also, if the functions being exposed globally are being called via the global object, like `w.a()` and `w.b()` then within those functions, you can use `this` to refer to the `w` object. So inside the `a` function you can do `this.b();`. The value of `this` is always determined by *how* the function is called. So calling `some_obj.some_func()` sets the value of `this` in `some_func` to the object `some_obj`. – user113716 May 19 '11 at 22:43
  • @patrick - oops re: infinite ... i'll switch to `return{a:a, b:b};` ... thanks x10 for all your help –  May 19 '11 at 23:56
  • @ patrick, when i add to `a()` an `alert(this)` and then call `a()` via `window.onload=w.a;` the new alert displays `[object Window]` for the value of `this` ... more intricacies i guess. –  May 20 '11 at 00:13
  • @mike: Yes, there are some intricacies to the `this` value. And yes, if you assign a function directly to an element's `on[event]` property, the value of `this` will be that element. When you do `window.onload=w.a;`, it looks like you're assigning `w.a`, but really you're just assigning `a`, which you happen to be getting from the `w` object when you make the assignment. Ultimately `onload` is only getting the `a` function without the `w`. What you can do is assign an anonymous function that calls `w.a`, like: `window.onload = function() { w.a(); }`. Now when the `onload` is invoked, it is... – user113716 May 20 '11 at 00:26
  • ...invoking the anonymous function, which gets `window` assigned to `this`. The anonymous function then invokes `w.a()`. The `a()` method sees that it has been called from `w`, and automatically makes sure that `w` is used as the value of `this` in `a`. – user113716 May 20 '11 at 00:26
1

its all just syntax differences. I think even at B both declarations are in the same scope.

just declaring i without var will cause it to be defined in global scope.

It's mainly about what option fits situation best, while it should still be kept as consistent as possible.

If you already have a global variable x, I don't think any of this would prevent a collision.

sharp johnny
  • 814
  • 6
  • 16
  • "*...declaring i without var*" is impossible by definition. Undeclared variables are made properties of the global object when they are first assigned a value (i.e. when they are initialised). – RobG May 18 '11 at 21:03
  • 1
    mixed up declaring with initializing – sharp johnny May 19 '11 at 09:17
0

The scope of a javascript variable is shared with the entire function when using var, so there won't be any difference between A1 and A2 when those are both in a function.

You can however use the let keyword and restrict that variables scope to a block, for instance:

for(let x = 0; x < 10; x++){}

That will restrict the x variable to only have scope within the for{} block.

musicinmyhead
  • 1,466
  • 9
  • 11
  • There's also a similar SO question that I just forgot to link here: http://stackoverflow.com/questions/790558/variable-scope-in-javascript-for-loop – musicinmyhead May 18 '11 at 20:16
  • 2
    `let` is not widely available, nor is it part of any current standard, though it should be coming in the next version of ECMAScript. – user113716 May 18 '11 at 20:19
0

There is no functional or efficiency difference between A1 or A2 either. Try this:

var arr=[];
for(var i=0; i<10; ++i) {
  var x=i;
  arr.push( function() {alert(x);} );
}
for(var i=0; i<10; ++i) {
  arr[i]();
}

You'll see that the value shown by all functions is same, because its the same x that all the functions use. New variable is not created each time.

Raze
  • 2,175
  • 14
  • 30