JavaScript - Closures - javascript

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

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

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.

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>
12
14
16
<div>
18
20
</div>
Hope this helps

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.

Related

Cannot access properties by 'this' [duplicate]

This question already has an answer here:
passing this.method in setTimeout doesn't work?
(1 answer)
Closed 8 years ago.
In the following snippet I try to access the property offset from within the member function shift(). As it seems, I cannot access it this way, because console.log reports Offset: NaN:
function shiftImg() {
this.offset = 0;
this.shift =
function() {
this.offset++;
console.log("Offset: " + this.offset);
};
}
productImg = new shiftImg;
window.setInterval(productImg.shift, 100);
However, converting the code above from a template paradigm to a closure paradigm works as I'd expect:
function shiftImg() {
var offset = 0;
return {
shift: function() {
offset++;
console.log("Offset: " + offset);
}
}
}
productImg = shiftImg();
window.setInterval(productImg.shift, 100);
In my first example, why I cannot access offset via the operator this?
My Answer:
I'll post here my solution, as I cannot append a standalone answer.
Browsing again into the mess of the horribly-written MDN's documentation, I learned of the bind method:
function shiftImg() {
this.offset = 0;
this.shift =
function() {
this.offset++;
var img = document.getElementById('img');
img.style.paddingLeft = this.offset + 'px';
console.log("Offset: " + this.offset);
};
}
productImg = new shiftImg;
window.setInterval(productImg.shift.bind(productImg), 100);
The nested function doesn't have it's own this context (it'll simply refer to the window), so assign a variable to the this method within shiftImg to which you can refer in the nested function:
function shiftImg() {
var self = this;
this.offset = 0;
this.shift =
function() {
self.offset++;
console.log("Offset: " + self.offset);
};
}
productImg = new shiftImg();
window.setInterval(productImg.shift, 100);
The reason you need to do this is because the call to setInterval which invokes the method, is run in a separate execution context, where this is equal to the window. If you called this.shift() from within shiftImg() you'll see that you it works just fine without the need to add self. See this MDN article for more.
Alternatively you pass an anonymous function to the callback method in setInterval:
window.setInterval(function() {
productImg.shift();
}, 100);
If you use objects and jQuery then you'll find into this problem quite a lot, and jQuery's $.proxy utility method makes doing similar things to above fairly easy.
In the first example, the context of this for the offset is not the same context as shiftImage. The second function closure changes the scope of this.

need help understanding closures usage in this code

Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...

JavaScript and jQuery , creating and usng an object. Properties return undefined

function DialogWindow(contents, clickEvent){
// given a contents we can create a DialogWindow of that content
this.contents = contents;
this.domElement = $(".hideMe");
this.test = function(){
console.log(this.contents);
}
$("div").click(this.test); //<-note this spot
/*
This function simply returns the html representation of the contents
This function should be called withen WindowPage so that it can fully figure out what
Arguments:
container: the container that containes the WindowPage that this is apart of
lanes: the number of lanes on the page. Defined as: lanes = (number of columns in WindowPage) * 3 + 1
*/
this.toHtml = function(container, lanes){
var dialogWidth = container.width()/(lanes/2);
var dialogSep = container.width()/lanes;
var htmlWindow = jQuery('<div/>', {
id: "dialogWindow"
}).css("width", dialogWidth).css("height", dialogWidth).css("position","absolute");
jQuery(this.contents.toHtml()).appendTo(htmlWindow);
this.domElement = htmlWindow;
return htmlWindow;
}
}
My goal is to get a click of htmlWindow, to execute a function of DialogWindow. However whenever I do this all of DialogWindows properties return undefined. If I replace the line:
$("div").click(this.test);
with
$("div").click(this.test());
Then the function test() fires imediatelly and works (i.e. prints this.contents to the console). However if I leave that line as it was (i.e. I wait for a click to make the test() function fire) then it prints undefined to the console.
It is because this inside test does not point to DialogWindow object, it points to the clicked dom element
One solution is to pass a custom execution proxy to the event callback using $.proxy()
this.test = function(){
console.log(this.contents);
}
$("div").click($.proxy(this.test, this)); //<-note this spot
Another popular solution is to use a closure variable
var self = this
this.test = function(){
console.log(self.contents);
}
$("div").click(this.test); //<-note this spot
In this scenario I'll prefer to use the former method

Doubts About Use of Practical Closure

I'm trying to find out more about closures in Javascript and was going through this: https://developer.mozilla.org/en/JavaScript/Guide/Closures#Practical_closures
According to this article, by using such a function:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
We can then make use of such statements to increase/decrease the font-size of text on a page:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
While I understand the concept here - i.e. size12, size14 and size16 become closures that allow access to the internal function, I can't help but feel that this is unnecessary. Isn't it easier to just have:
function makeSizer(size) {
document.body.style.fontSize = size + 'px';
}
, and then invoke it with these?
document.getElementById('size-12').onclick = makeSizer(12);
document.getElementById('size-14').onclick = makeSizer(14);
document.getElementById('size-16').onclick = makeSizer(16);
Can anyone tell me if my thinking is right - or maybe I'm just a novice to Javascript and doesn't understand the advantage to using closure in this scenario, in which case I'll be most glad if you can explain the advantage of doing so.
Thanks in advance guys.
No, you can't do that.
It's as if you had written:
document.getElementById('size-12').onclick = (function(size) {
document.body.style.fontSize = size + 'px';
})(12);
The function gets immediately invoked, the style will be applied straight away, and no .onclick handler gets registered because the return value of the function is undefined.
The real point of the example is to show that you can return a function from another function, and that you can then assign that result to an event handler.
If you had left makeSizer() unmodified then you could assign the handlers as proposed without intermediate variables, i.e.:
document.getElementById('size-12').onclick = makeSizer(12);
but that won't work if you change makeSizer() the way you described.
It is also less efficient than storing the "sizer" in a variable if you use the same sizer more than once.
For the example you presented, of course closure is not necessary, but I guess it is just to make it simple to present the concept. There are cases though that closure is the best solution to use: think about how to implement a "private" attribute in javascript or when you need curryng to encapsulate arguments (ie, for a callback function).
I hope the following example helps:
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var sequencer = function () {
return _count++;
}
return sequencer;
}
var fnext = makeSequencer();
var v0 = fnext(); // v0 = 0;
var v1 = fnext(); // v1 = 1;
var vz = fnext._count // vz = undefined
Yes, those variables (sizeN) are unnecessary. You can directly assign the result of makeSizer() as handlers, which looks far better.
But, the use of these variables is not the concept of closures. The closure in this example is the function makeSizer, which returns a function (even without arguments), which still has access to the size variable.
Though, you need to see the difference between
function makeSizer(size) {
return function resize() {
document.body.style.fontSize = size + 'px';
};
}
and
function resize(size) {
document.body.style.fontSize = size + 'px';
}
Executing makeSizer(5) does not do anything, it returns a function that sets the size to the pre-defined size when invoked. Instead executing resize(5) does set the size directly. You can't use the result of the latter function as an event handler.

update numeric variables when changed in prototype function

I am trying to add two numbers but for some reason I am not getting NaN.
Following is the sample code
function Slider(container, nav, pagination){
this.articleWidth = this.div.width() + 20;
this.divWidth = 960;
this.articleMargin = 0;
this.pageMargin = this.divWidth + this.articleMargin
}
Slider.prototype.articleTransition = function(pageNum){
var currArticle = pageNum -1;
this.articleMargin = currArticle * this.articleWidth;
this.container.animate({'margin-left': -this.articleMargin});
}
Here everything works as expected. But this.pageMargin is always 0 even though this.articleMargin's value is changing in the articleTransition function. And when I console log this.pageMargin is says NaN. I am trying to change value of this.articleMargin's value, everytime it is being invoked in the function.
Here is how I am invoking the function in my HTML.
var slider = new Slider($('div#contentSleeve'), $('a.anav'), $('ul#scroller li'));
slider.pagination.live('click', function(){
slider.articleTransition($(this).data('num'));
});
I guess that's because you are calling in anonymous function scope. Try like this:
slider.articleTransition.call(slider, $(this).data('num'));
I did fix this. All I had to do was to create a global variable that will store the value of both pageMargin and ArticleMargin.

Categories

Resources