Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is wrong with my javascript scope? [duplicate]

The following alerts 2 every time.

function timer() {     for (var i = 0; i < 3; ++i) {         var j = i;         setTimeout(function () {             alert(j);         }, 1000);     } }  timer(); 

Shouldn't var j = i; set the j into the individual scope of the setTimeout?

Whereas if I do this:

function timer() {     for (var i = 0; i < 3; ++i) {         (function (j) {             setTimeout(function () {                 alert(j);             }, 1000);         })(i);     } }  timer(); 

It alerts 0, 1, 2 like it should.

Is there something I am missing?

like image 641
Naftali Avatar asked Nov 11 '13 19:11

Naftali


People also ask

What is $scope in JavaScript?

Scope in JavaScript refers to the current context of code, which determines the accessibility of variables to JavaScript. The two types of scope are local and global: Global variables are those declared outside of a block. Local variables are those declared inside of a block.

What are the three types of scopes in JavaScript?

JavaScript has 3 types of scope: Block scope. Function scope. Global scope.

How do you determine the scope of a variable in JavaScript?

The static structure of a program determines the variable scope. The scope of a variable is defined by its location within the source code, and nested functions have access to variables declared in their outer scope.

Which two factors determine the scope of a variable in JavaScript?

The scope of a variable is controlled by the location of the variable declaration, and defines the part of the program where a particular variable is accessible. JavaScript has two scopes - global and local.


2 Answers

Javascript has function scope. This means that

for(...) {     var j = i; } 

is equivalent to

var j; for(...) {     j = i; } 

In fact, this is how Javascript compilers will actually treat this code. And, of course, this causes your little "trick" to fail, because j will be incremented before the function in setTimeout gets called, i.e. j now doesn't really do anything different than i, it's just an alias with the same scope.

If Javascript were to have block scope, your trick would work, because j would be a new variable within every iteration.

What you need to do is create a new scope:

for(var i = ...) {     (function (j) {         // you can safely use j here now         setTimeout(...);     })(i); } 
like image 135
Ingo Bürk Avatar answered Oct 03 '22 02:10

Ingo Bürk


The alternative the the IIFE is a function factory:

function timer() {     for (var i = 0; i < 3; ++i) {         setTimeout(createTimerCallback(i), 1000);     } }  function createTimerCallback(i) {     return function() {        alert(i);     }; }  timer(); 

This being said, this is one of the most asked questions in the javascript tag. See:

  • JavaScript closure inside loops – simple practical example
  • Javascript infamous Loop issue?
like image 28
bfavaretto Avatar answered Oct 03 '22 03:10

bfavaretto