spring
spring

Reputation: 18487

Scope within an anonymous function?

I'm coming from an Actionscript background and (very late to the party) I am trying to learn JavaScript. I am going through this AngularJS - Video Tutorial For Beginners on YouTube (it's pretty good) and saw something really basic that I don't understand.

At line 5 the var workcount is defined. Then two anonymous functions are defined and returned in an object. The functions reference workcount but isn't workcount in a different scope? Is this like blocks in Objective-C where local vars remain accessible within the block. Is there a name for what this is?

Or if a function "knows about" vars previously defined in its scope, would the function task2 "know about" task1?

It bugs me that I can't make sense of this.

Update: Thanks for all the replies. I get it now – and while I have seen the term "closures" before, I never understood it (it seems a not very descriptive term. In reading up, I saw the term "stack-frames" and then the light bulb lit up: stack... frame of reference);


var createWorker = function(){

  var workCount = 0;

  var task1 = function(){
    workCount += 1;
    console.log("task1" , workCount);
  };

  var task2 = function(){
    workCount += 1;
    console.log("task2" , workCount);
  };

  return {
    job1: task1,
    job2:task2
  }
};

worker=createWorker();

worker.job1();

worker.job2();

Output:

task1 1
task2 2

Upvotes: 17

Views: 17770

Answers (7)

Rajshekar Reddy
Rajshekar Reddy

Reputation: 18987

Just note that the variable and the two anonymous functions are wrapped inside the same function (let's call it a parent function). So the scope of this variable is available within this parent function.

So now this variable acts as a global variable for these two inner functions But the scope is limited to the parent function. Both the inner functions share the same variable.. Changing the value of the variable in one function will have effect in other function too..

So taking the logic in the post Let's say we execute task1 and task2 one after the other. The variable is initially set to 0. Then in your task1 it's incremented by one. Which makes the variable value 1 (0 + 1). Now in task two also its increased by one, making its value 2 (1 + 1).

This scope concept is called as closure in JavaScript.

Upvotes: 13

Enjayy
Enjayy

Reputation: 1074

This is called a closure in JavaScript.

The scope of a closure in JavaScript is lexical, which means that everything that is contained within the function the closure belongs to, has access to any variable that is in it

Basically createWorker is a scope and since task 1 and task 2 are declared inside createWorker they have access to all the variables declared in createWorkers scope.

But createWorker does not have access to any variables declared inside task 1 and task 2.

Upvotes: 9

Clomp
Clomp

Reputation: 3308

JavaScript has some interesting variable scoping rules. Here is a quick overview:

x = 0; // Global, as no "var" keyword preceeds it. Btw: var is optional!
var x =  0; // This is scoped to it's parent fn. Child fn's can use it.
let x = 0; // This is similar to var, but has a special use case. (See below.)

As an added bonus, this next line of code looks like it's a variable declaration, but it's not. It defines a constant. It's part of the EcmaScript 2015 spec, AKA ES6. Here's what's new in ES6.

const x = 0; // This is a constant (locked variable) and can't be changed.

While both var & let are accessible by their immediate function & their children functions, here is where they're different: The let keyword can allow someone to make duplicates variables with the same name, from both inside of the parent & child functions! It just makes JS stranger!

Since workCount is defined inside of the parent createWorker function with the "var" keyword, then the task1 & task2 functions can change it's value, as they are children functions.

Check out the MDN specs on how the var & let keywords work.

So the answers to some of your questions are:

  1. No, it's in the same parent "createWorker" function scope.
  2. Unknown as I don't write ObjectiveC code. Someone else can answer that one.
  3. Yes & No: Yes, because it can tell that task1 is a function, but No, because the code in task2's function block can't see inside of task1's function block.

Upvotes: 1

Gokhan Kurt
Gokhan Kurt

Reputation: 8277

You can write write your code as in below and javascript will interpret it the same.

var createWorker = function(){
  var workCount, task1, task2;

  workCount = 0;

  task1 = function(){
    workCount += 1;
    console.log("task1" , workCount);
  };

  task2 = function(){
    workCount += 1;
    console.log("task2" , workCount);
  };

  return {
    job1: task1,
    job2:task2
  }
};

What happens here is, variables are defined at the top of the enclosing function block. No matter in what order they are defined. So not only task2 knows about task1, task1 also knows about task2. However order of assignments in important. Consider the code:

function foo1() {
  console.log("foo1: " + a);
  var a = "Hello";
}

function foo2() {
  var a = "Hello";
  console.log("foo2: " + a);
}

function foo3() {
  console.log("foo3: " + a);
  let a = "Hello";
}

function foo4() {
  console.log("foo4: " + b);
}
var b = 5;

foo1(); // undefined
foo2(); // Hello

try {
  foo3(); // Throws ReferenceError
} catch (e) {
  console.log("foo3: " + e.message);
}
foo4(); // 5
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

  • When foo1 tries to use a, it was defined but it was not assigned.
  • foo3 uses let instead of var. let is more intuitive for some coming from other programming languages. But be careful as it is part of ES6 specification and not widely supported yet.
  • foo4 works since b was defined before foo4 was defined. b was assigned before foo4 was invoked.

Upvotes: 2

Alex Kudryashev
Alex Kudryashev

Reputation: 9460

This code illustrates how it works.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Closure JS</title>
    <script type="text/javascript">
        var createWorker = function () {
            var workCount = 0;
            var task1 = function () {
                var t = t || 0;
                workCount += 1;
                console.log("task1: " + workCount);
                console.log("task1 t: " + (t++));
            }
            var task2 = function () {
                var t = t || 0;
                workCount += 1;
                console.log("task2: " + workCount);
                console.log("task2 t: " + (t++));
            }
            return {
                job1: task1,
                job2: task2
            };
        }
        var app = new createWorker();
    </script>
</head>
<body>
<div>
    <input type="button" value="task1" onclick="app.job1()" />
    <input type="button" value="task2" onclick="app.job2()" />
</div>
</body>
</html>

Console output after several clicks on buttons:

task1: 1
task1 t: 0
task1: 2
task1 t: 0
task2: 3
task2 t: 0
task2: 4
task2 t: 0

It easy to see that task1 and task2 know about their parent scope and know nothing about each other and about their previous execution.
This way ball bounces.

Upvotes: 2

Matthew Herbst
Matthew Herbst

Reputation: 31983

Yes, functions are aware of everything in their scope, including each other.

There are two parts to your question.

The second part is easy to answer first: all variables and functions in a scope are "hoisted," allowing you to use a variable before you declare it:

x = 5;
var x;
console.log(x); // Gives 5

Back to the first part of your question: in terms of scope, I won't expand too much on it since it is a widely covered topic on this and other sites. w3schools has a good guide on this.

Basically, it boils down to global and local scope. Global scope works as you might imagine, with the variable (or function) being available globally:

var x = 10;
function foo() {
  console.log('Global scope! ' + x);
}

Local scope is, basically, for everything within a closure (a topic well beyond this question), which functions are:

function foo() {
  bar(); // This will work, since foo and bar share scope
  foobar(); // This will not work: foobar is only in scope within bar
}

function bar() {
  function foobar() {
    console.log('foobar');
  };

  console.log('bar');
  foobar(); // This will work, since foobar is defined within bar's local scope
}

Things get a little more complicated with var declarations. This is being greatly simplified by the ES6 let declaration. Read more.

And by the way, while your functions are anonymous, they aren't really since you are saving references to them. Functionally, the two examples below are perfectly equivalent:

// These give the exact same result
function foo() {}
var foo = function() {}

// You can use either by calling
foo();

Upvotes: 2

James Beem
James Beem

Reputation: 34

task 2 would not know about variables created within task 1, task 1 and task 2 do know about workCount.

Upvotes: 0

Related Questions