0

I'm reading the explanation of closures on Mozilla developer site and am struggling a bit. Please have a look at the following code from Mozilla website. I kind of understand how it works but I'd think that the code below my comments should also work. Why does it not work if one click on 18 and 20?

/* mozilla dev code */
function makeSizer(size) {
  return function() {
      document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
/* end of mozilla dev example */
/* my code starts */
/* see - no inner function below */

function makeS(size) {
        document.body.style.fontSize = size + 'px'
}
/* Let's see if that works */

var size18 = makeS(18);
document.getElementById('size-18').onclick = size18;



/* What about that? */

document.getElementById('size-20').onclick = makeS(20);

Why

CodePen: http://codepen.io/wasteland/pen/qqoooW

Wasteland
  • 4,889
  • 14
  • 45
  • 91
  • Possible duplicate of [How do JavaScript closures work?](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – s3raph86 Jan 13 '17 at 02:13

4 Answers4

6

makeS(18) immediately invokes the function and changes the size. What you assign to the onclick event in that case is actually undefined, since that's what the function returns when invoked, as it has no explicit return.

function makeS(size) {
        document.body.style.fontSize = size + 'px'
}

console.log("font size before calling makeS:", document.body.style.fontSize); //it's actually an empty string, hence why it doesn't show up in the output

var size18 = makeS(18);

console.log("font size after calling makeS:", document.body.style.fontSize);

console.log("what is size18?", typeof size18);

By contrast, makeSizer(18) will create a new function that when called, will change the size.

function makeSizer(size) {
  return function() {
      document.body.style.fontSize = size + 'px';
    };
}

console.log("font size before calling makeSizer:", document.body.style.fontSize); //it's actually an empty string, hence why it doesn't show up in the output

var size18Function = makeSizer(18);

console.log("font size after calling makeSizer:", document.body.style.fontSize); //it's still an empty string

console.log("what is size18Function?", typeof size18Function);

//let's call it now
size18Function();

console.log("font size after calling size18Function:", document.body.style.fontSize); //it's now changed
VLAZ
  • 26,331
  • 9
  • 49
  • 67
4

A closure is fact of 'remembering' variables of the scope chain by the function when it is defined. Since function argument is just a local variable during execution, this:

function makeSizer(size) {
  return function() {
      document.body.style.fontSize = size + 'px';
    };
}

is an equivalent to:

function makeSizer() {
    var size = 1; // or some other value
    return function() {
        document.body.style.fontSize = size + 'px';
    };
}

In the above case, the anonymous function, which is the return value, will 'remember' value of size variable and use it each time it is called.

MakeSizer should be treated as a 'factory' function that provides different values for local variable size.

You cannot acheive this kind of 'remembering' without using a function definition statement inside of 'factory' function.

pttsky
  • 737
  • 5
  • 15
0

Onclick of 18 your calling makeS. It does not return anything to size18,siz18 would be undefined here,if you want to make it work through clousre change makeS(),to return a method as similar to makeSizer

check this snippet

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;


function makeS(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  }
}

/* Let's see if that works */

var size18 = makeS(18);
document.getElementById('size-18').onclick = size18;


/* What about that? */

document.getElementById('size-20').onclick = makeS(20);
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}
h1 {
  font-size: 1.5em;
}
h2 {
  font-size: 1.2em;
}
<h1>How closures work</h1>
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

<div>
  <a href="#" id="size-18">18</a>
  <a href="#" id="size-20">20</a>
</div>

Hope this helps

Geeky
  • 7,420
  • 2
  • 24
  • 50
0

A closure refers to the concept of a function retaining access to variables in the lexical scope that it was declared within, even as it is passed, and called in different locations and scopes throughout a program.

Huh?

What does this mean then? Consider the below piece of code

function functionFactory() {
    var x = 1;
    return function () {
        return ++x;
    };
}

var first  = functionFactory();
var second = functionFactory();

console.log(first()); // outputs 1
console.log(first()); // outputs 2
console.log(first()); // outputs 3

console.log(second()); // outputs 1

Each time functionFactory is called, it declares a private variable x, and then returns an anonymous function that mutates that variable. We can see in the output that each subsequent call of that returned function, first, returns a different value.

When we call second though, it returns 1. Why? Because it was created by a separate invokation of functionFactory, and so it has its own reference to its own closure over x.

Why do I care?

Because this is super-useful. In programming languages (like javascript, but other functional languages too) which treat functions as first-class variables, we can use closures for encapsulation of private variables.

So to your actual question...

The reason why your code is not working, is because onclick expects you to assign a function to it - instead you are assigning the output of your makeS function, which is void. makeSizer is returning a function, that establishes a closure over whatever number value is passed into it as a parameter, and is then assigned to the onclick variable of each element.

Hope this makes things clearer for you.

s3raph86
  • 556
  • 4
  • 17