I'm reading "Eloquent JavaScript". Chapter 3 introduces "Closure" concept and gives you a couple of examples. One of these is next one:
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
var twice = multiplier(2);
console.log(twice(5));
// → 10
I think I understood the concept. If first I execute console.log(twice), since variable number is undefined, what I get is [Function]. What I don't understand is how twice(5) works. Why local variable number is initialized with value 5?
Also, why if I execute console.log(multiplier(2,5)) I don't get 10 as a result?
Thanks.
Because multiplier returns a function, so twice is equal to the returned function, NOT the multiplier function.
However, when multiplier is called the factor variable is passed and used within the returned function.
So to make it easier to understand, consider that twice is basically:
var twice = function(number) {
return number * 2;
};
Where factor has been replaced by the value you passed in when calling multiplier(2).
I think I understood the concept. If first I execute console.log(twice), since variable number is undefined, what I get is [Function].
When you use console.log(twice) you are not actually calling the function twice, you are simply logging the value of it. So the output of [Function] is not because number is undefined, it is because you are outputting the actual function rather than the result of it.
Also, why if I execute console.log(multiplier(2,5)) I don't get 10 as a result?
Here you are calling the multiplier by providing 2 arguments, though you have only defined the function to accept one parameter (factor). In javascript, this won't cause an error, but you will just get the first value mapped in factor (factor = 2).
Note: There are ways to still access all the supplied arguments even if you don't have parameters defined for them (here's an example)
Something that would be possible to get a result of 10 which might be of interest is to use the following code:
var result = multiplier(2)(5); // result = 10
multiplier is a function which returns anonymous function which accepts an argument(number)
var twice = multiplier(2);
Basically is :-
var twice = function(number) {
return number * 2;
};
If you execute
console.log(multiplier(2,5))
you call the function giving two parameters, whereas
function multiplier(factor) {}
only takes one parameter.
Related
I'm still new to Javascript so please forgive me if this seems like a silly question.
I defined a function called randomNumberGenerator, and in it is just one line of code. Basically, the purpose of it is to return a number between 0 and 2.
function randomNumberGenerator() {
return Math.round(Math.random() * 2);
};
However, when I tried to log the result of this function in my console, it shows me the outline (is that what it's called) of that function instead of a random number. You can see it here:
ƒ randomQuestionSelector() {
return Math.round(Math.random() * 2);
}
I tried assigning 'Math.round(Math.random() * 2)' to a simple variable, and logging that variable to the console. It worked as expected (I got a random number), so I'm confused why doing the same with a function doesn't.
Hope someone can help me out with this. Thank you!
The reason why console.log(randomNumberGenerator) returned such a value is that you did not really invoke this function. You called the default .toString() method which exists on every object. And default implementation (which can be overriden) returns what you saw in the console. If you want to get the result of the function you need to use () operator which calls the function, like this:
randomNumberGenerator()
And to capture the returned value you can assign it to the variable, for example:
const random = randomNumberGenerator()
console.log(random);
That way you are calling the function and assigning the returned value to the new variable.
You need to call the method. What you are likely doing is calling console.log(randomNumberGenerator) rather than console.log(randomNumberGenerator())
I don't know if I chose the right title for this question, and maybe this is why I cannot also find an answer to this question.
While reading a javascript book, I found this example while talking about closures.
function multiplier(factor){
console.log('factor:'+factor);
return function(number){
console.log('number:'+number)
return number * factor;
};
}
var twice = multiplier(2);
console.log('twice:'+twice(5));
And in console I get this output:
factor:2
number:5
twice:10
I understand what a closure is meant to be, but I do not understand how the variable number, that by my knowledge I expected to be undefined, get the value 5.
My reasoning is the following:
When I call the function multiplier(2) the local variable factor is assigned the value 2, so the first output is correct.
But when it reaches the line return function(number){ it shall assign number undefined, since no value has been previously assigned to such a name.
So it shall crash at all, and not doing correctly the output I got.
May anyone help me understand why calling twice(5) it get the output
number: 5?
Thank you all, excuse me again If i did not post the question in the right way, feel free to modify anything to make this question more intelligible.
return function (number) { ... } returns a function. number is not a variable, it's a function parameter. The same way that factor in function multiplier(factor) is a parameter. It is neither undefined nor does it cause anything to crash either.
In essence, multiplier(2) returns this function:
function (number) {
console.log('number:' + number)
return number * 2;
}
...which you assign to twice, so twice is now the above function.
I think the key thing that you're missing here is that the returned function acts just like any other function. The function isn't entered until it's called.
return number * factor;
doesn't run until you call twice. It's just like how
console.log('factor:'+factor);
doesn't run until you call multiplier. The body of the function isn't entered until the function is called. number doesn't have a value until twice is called, but the code that uses number also doesn't run until twice is called.
But when it reaches the line return function(number){ it shall assign number undefined, since no value has been previously assigned to such a name.
Here's the misunderstanding. Remember: In Javascript, almost everything is an Object. Some will say that many things that you interact with regularly (strings, numbers, booleans (Notice how I put these types/primitives/words first letter in lowercase. I usually use uppercase for Classes and lowercase for primitives)) are primitives, not objects. This is true, but for the purpose of this thread let's consider (almost) everything is an Object.
Let's get back on this sentence you wrote:
when it reaches the line return function(number){ it shall assign number undefined
Here's the issue: when it reaches the line "return function(number){}", it actually returns a function, which is an object.
It does not execute this function, it only declares it, and returns it, as an Object.
You could have wrote "return 666", it would have returned an Object. A Number.
Let's continue.
Your variable "twice" now contains a function. Guess which one. This one:
function(number){
console.log('number:'+number)
return number * factor;
}
Remember, you've declared it and returned it in only one statement:
"return function(number){...}"
Your "twice" variable is now equivalent to a named function you could've declared this way :
function twice(number){
console.log('number:'+number)
return number * factor;
}
Yes, functions are Objects, named functions are like named variables, and variables can be functions.
You can call it this way for example: twice(9), or this way: twice(5).
That's what you've done.
Now let's answer your question:
why calling twice(5) it get the output number: 5?
Because:
var twice = function(number){
console.log('number:'+number)
return number * factor;
}
And you've executed "twice(5);" which in turn executed console.log this way:
console.log('number:'+5);
As far as I've understood, in your "function multiplier()", you do not want to return a function but rather the result of this function itself. I advise you to read about IIFE (Immediately-invoked function expression).
With this you will be able, in only one statement, to:
- declare a function
- execute that function
- (and eventually return its result)
Have fun playing with Javascript. Javascript is great, only when you know what's going behind.
Problem and Question
I have a small application that asynchronously gets JSON data from a server, formats it into a table, and allows the user to perform some simple operations on the table (sorting, filtering, etc.).
One of the sorting functions accesses DOM elements in the previously-generated table (definition given below).
var SORT = function(){
var my = {};
// public methods
my.byName = function(sign){
(!sign) ? sign=1 : 1;
var T = $("#resultArea").find("tbody");
var R = T.children("tr");
R.sort( function(a,b){
var an = $(a).attr("data-name");
var bn = $(b).attr("data-name");
return sign * an.localeCompare(bn);
});
R.detach().appendTo(T);
}
...
return my; }();
When specifying it as a callback for an element the user can click on, I have two formulations.
$("#sort-button").click(SORT.byName); (pass function as argument)
OR
$("sort-button").click(function(){SORT.byName();}); (pass anonymous closure that calls the function)
The first option fails, but the second works. Here is how it fails on a test case with 536 rows to be sorted:
Former row 2 (which precedes row 1 in alphabetical order) is moved to position 268.
Former row 269 is moved to position 536.
Former row 536 is moved to position 2.
I have tried and failed to construct a MWE that fails in the same way (will update question once I succeed). Questions are: What went wrong? Why does using the anonymous closure work?
Update
An earlier version of the snippet had been sanitized to remove the parameter sign and the line at the start (which would set sign to 1 if it evaluated to false or was undefined), the idea being that by passing sign=-1 as a parameter, a descending sort could be done. When I removed sign from the definition in the live code, the problem went away.
So I found that the offending line is (!sign) ? sign=1 : 1; When replaced by the more transparent if(sign !== undefined){sign=1;} the problem goes away. I think what it does is that it sets global sign to one on the first pass, then on the second pass, sign is defined so it returns 1, and the function ends (only one pass having been completed).
Then why does this error not crash the sort on the anonymous closure approach?
As you found out, the problem comes from that sign parameter. When you pass a function as the event handler and call SORT.byName() without arguments, you'll get sign=1 and everything is as expected. But when you pass the function directly as the handler, it will get called with the Event object as its argument. Suddenly you have an object in your sign variable, and multiplying that with a number will yield NaN (an invalid result for a comparison function), which completely messes up your sort.
When replaced by the more transparent if(sign !== undefined){sign=1;} the problem goes away. I think what it does is that it sets global sign to one on the first pass…
Nope. There is no global sign variable at all. And I guess you actually wanted if (sign === undefined) sign = 1;. Which wouldn't work as well when passing in events, so you might want to use if (!Number.isFinite(sign)) sign = 1;.
Without seeing more of your code I can't really tell what's going on. The snippet you have posted seems alright. Regarding your question, there is a subtle difference when you pass SORT.byName vs. sending it wrapped in an anonymous function. Specifically, it is the value of this in the byName function when it is executed.
When you do click(SORT.byName), you are sending a direct reference to the function, which means that when it gets called, the value of this is whatever the jQuery handler for click sets it to be when before it calls your callback function; usually this is a reference to the element that fired the event.
However, when you do click(function() { SORT.byName(); }), the value of this in byName is the SORT object (but this in the anonymous function is still whatever jQuery sets it to). This is because here you're invoking the function explicitly as a method of the SORT object.
So if your sort functions rely on the value of this and assume it to be the SORT object, you can run into issues.
Here's some code that demonstrates this behavior:
var obj = {
field: 10,
method: function() {
console.log(this);
}
};
// The first argument to apply sets the value of "this"
function call(f) {
f.apply("not this", []);
}
call(obj.method); //logs "not this"
call(function() { // logs obj
obj.method();
});
Searching through the DOM and sorting is not fun. You would be better off keeping some state, sorting, and then appending the new results to the DOM after you remove the old like so.
var myNumbers = [
[1,'one'],
[4,'four'],
[2,'two'],
[6,'six'],
[3,'three'],
[8,'eight'],
[7,'seven'],
[5,'five'],
[10,'ten'],
[9,'nine']
];
myNumbers.sort(function(a,b){
if (a[0] > b[0]) {
return 1;
}
if (a[0] > b[0]) {
return -1;
}
return 0;
});
var T = $("tbody");
var R = T.children("tr");
R.detach()
After that you can prepend your results in a loop like you addElemnt function but a loop instead.
I am learning about javascript closures and am having difficulty understanding the concept. If someone would be kind enough to guide me through this example, i.e., where inputs and outputs are going, I'd appreciate it.
var hidden = mystery(3);
var jumble = mystery3(hidden);
var result = jumble(2);
function mystery ( input ){
var secret = 4;
input+=2;
function mystery2 ( multiplier ) {
multiplier *= input;
return secret * multiplier;
}
return mystery2;
}
function mystery3 ( param ){
function mystery4 ( bonus ){
return param(6) + bonus;
}
return mystery4;
}
results;
Thank you.
Let's analyze this step by step.
The first call is to mystery with an argument of 3.
What does mystery do? It defines a variable secret with the value 4 and then it adds 2 to input.
So after the first two lines of mystery, you have:
secret = 4;
input = 5;
Then you have a nested function called mystery2 which takes in an argument called multiplier. In the first line it multiplies input to multiplier, and then returns secret * multiplier. We don't know the values of multiplier, but we do know the values secret and input. You might be wondering how that is. Well, in JavaScript when you create a closure, it is lexically bound to the current scope. This is just a fancy way of saying that the closure "knows" about all local variables that were created in the same scope that the closure itself was created. This behavior applies to nested functions as well which is why they can behave as closures in JavaScript. So what this means is that mystery2, when it is eventually called, will have secret set to 4 and input set to 5. So this mystery2, which knows about these two values, is returned from mystery. So after execution, the variable hidden doesn't contain a value, instead it contains a reference to an instance of mystery2 where the values of secret and input are the ones I mentioned earlier.
What is the advantage of this? The advantage is that you can have multiple copies of mystery2, which "know" different values of input based on what was passed into mystery. So here, mystery is sort of behaving like a constructor for mystery2.
Now we have hidden pointing to an instance of mystery2. So in this case hidden is sort of an alias for our own special copy of mystery2.
In the next line, you call mystery3 and pass in hidden as an argument. What happens inside mystery3? Well mystery3 accepts a parameter called param, then it does something similar to mystery; it returns a function. What does this function do? It accepts a parameter called bonus. Then it does param(6) + bonus.
What does that mean?
What is param? It is the argument that was passed into mystery3. Since mystery4 behaves like a closure, it "knows" about param. But actually, what is param? Well, param is hidden, which points to our special instance of mystery2! Now here is where we actually evaluate mystery2: we call it with an argument of 6, which will be the value of multiplier. So now you have multiplier *= input. The value of input that mystery2 "knows" is 5. So we basically have 6 * 5, which means that multiplier is now set to 30. Then we return secret * multiplier, which is 4 * 30, which is 120. So what this means is that param(6) returns 120, which we then add to bonus. Keep in mind that this will happen only when we actually execute mystery4.
When do we execute mystery4? Well after we call mystery3, we return a copy of mystery4, which is then assigned to jumble. After that we call jumble(2). So what is jumble? It is essentially this:
function mystery4(bonus) {
return param(6) + bonus
}
What is param? Well, that is basically mystery2:
function mystery2 ( multiplier ) {
multiplier *= input;
return secret * multiplier;
}
Let's go over the calculations again. When we call param(6), we basically have multiplier set to 6 inside mystery2. mystery2 "knows" that input is 5 (it is what we calculated inside mystery). So multiplier *= input, means that multiplier is now 30. Then we do secret * multiplier, which is 4 * 30, which is 120. So the return value of param(6) is 120. To this value we add bonus inside mystery4. We called mystery4 with an argument of 2, so we have 120 + 2, which means that the final result is 122.
Hope this helps you out!
This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 8 years ago.
I'm a javascript noob trying to wrap my head around the closure exercise below.
Now, I know the result is 122. Can anyone walk me through this step by step (what gets passed to what), so I can understand how closures work?
var hidden = mystery(3);
var jumble = mystery3(hidden);
var result = jumble(2);
function mystery ( input ){
var secret = 4;
input+=2;
function mystery2 ( multiplier ) {
multiplier *= input;
return secret * multiplier;
}
return mystery2;
}
function mystery3 ( param ){
function mystery4 ( bonus ){
return param(6) + bonus;
}
return mystery4;
}
In order to understand this you must know what is the difference between a function call and a reference to a function. As well as how scopes work in javascript.
Assuming you do know these things, let's get explaining.
So you first have a variable hidden that is being assigned a value of mystery(3). So immediately look at the function mystery and see what it returns. it returns a reference to an inner function mystery2. So now hidden holds a reference, meaning that it has no actual numeric value. Following you have a second variable declaration
var jumble = mystery3(hidden);. Now in order to know what jumble holds you need to look at the function mystery3 and the value it returns. It, again, returns a reference to an inner function mystery4. So now the two variables you have contain references to inner functions of the closures mystery and mystery3.
Now let's have a look at var result = jumble(2). Executing jumble(2) is an actual function call to the function that jumble holds a reference to, which is mystery4. When mystery4 runs you see it requires a parameter bonus, which will be 2 given from the line var result = jumble(2). It returns param(6) + bonus. bonus is 2, ok, but what is param(6)? That is the value given to jumble: hidden, which was a reference to mystery2, remember? So running param(6) will execute mystery2 with a parameter 6
And so, tracing back the functions may have turned out a little confusing, but let's follow that with actual values to make it a little clearer ( if that's even a word ).
Executing var result = jumble(2) will give us a return value of param(6) + 2 to get param(6) we go into mystery2 with multiplier = 6, where we set multiplier = 6 * input. Our input is equal to 3+2=5, so multiplier becomes 6*5=30 and as a return value we multiply that by 4 which is our var secret. By the end of the execution of mystery2 we have a value of 120, which is returned to our param(6) in mystery4. And if you remember that our bonus was 2, 120+2=122 Voila!
I get the feeling I didn't do a very good job at explaining this simply, but that's probably the best I could do. Hope that helped!