javascript, about closure function call - javascript

I am quite new to javascript and i have encounter problems about function call and the closure.
Below are the code that i tried in w3schools,
<!DOCTYPE html>
<html>
<body>
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>
<script>
var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();
/*function add(test){
var counter = 0;
return function (test) {return counter += test;}
}*/
function myFunction(){
document.getElementById("demo").innerHTML = add(123);
//document.getElementById("demo").innerHTML = add().call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
</script>
</body>
</html>
It works fine for the current code, that every time i press the button, the number in the paragraph(id="demo") increment by 123.
But when i tried the commented code, which create a function with closure with exact same code, the paragraph value remain at 123 every onclick.
In this situation, i have a few questions to ask.
1.For the code :
var add = (function (test) {...}) ();
What is the usage of the last bracket? If we provide parameter to the last bracket, how can we use it in the declaration of the anonymous function of variable (var add)?
2.Why these two ways to define the function result in different result?
Thanks a lot, any help is appreciated.
EDIT
<!DOCTYPE html>
<html>
<body>
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>
<script>
/*var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();*/
function add(test){
var counter = 0;
return function (test) {return counter += test;}
}
function myFunction(){
//document.getElementById("demo").innerHTML = add(123);
document.getElementById("demo").innerHTML = add().call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
</script>
</body>
</html>
It seems that the code run with no error occur, when i use the commented code for declaration of function, but just a bug that the integer of the paragraph didn't increment.

The reason you are getting 123 all the time is because every time you click on the button, you take a new inner function with a closure on counter variable with value 0; So value of counter always remains 0 and when you add 123 to 0 you get 123.
If you move closure part out of the event handler, you would get exact same result as in the first case.
Notice the line var inner = add(); //<---Notice this line. This would take the closure one time and subsequently you will keep increasing the value of counter.
Also, notice this line inside myFunction:
document.getElementById("demo").innerHTML = inner.call(this, 123);
Here we are calling the inner function that we had reference on earlier.
/*var add = (function (test) {
var counter = 0;
return function (test) {return counter += test;}
})();*/
function add(test) {
var counter = 0;
return function(test) {
return counter += test;
}
}
var inner = add(); //<---Notice this line
function myFunction() {
//document.getElementById("demo").innerHTML = add(123);
document.getElementById("demo").innerHTML = inner.call(this, 123);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", myFunction);
<p>Counting with a local variable.</p>
<button type="button" id="btn">Count!</button>
<p id="demo">0</p>

() is to invoke the anonymous function you just declare, and it return another anonymous function. If you provide a parameter, it will be fed to your function (test). In this case, you don't actually need the first test parameter. It's just not used.
For your commented code, it returns a function. And the function is never invoked.

() The last bracket is to execute it.
The example u have shown is that of an IIFE.
IIFE are used to limit scope of all ur variable so that namespaces can be demarkcated.
//without params
(function(){
//...
})()
//-OR- (both are same)
(function(){
//...
}())
//with params. if u ever wondered how that $ variable was being used in jquery, this is how.
(function(jquery){
})(jquery);
//es6
(
() => { ... }
)()
closure is a programming concept in which (otherwise) scoped-out variables are allowed to persist. DO NOT use closure with big object collections !!!
var outer = function(p1){
var inner = function (p2) {
return (p1 + p2);
return inner;
}; //outer ends.
var x = outer(10);
x(20); //20 + 10;
x(30); //30 + 10;
x(40); //40 + 10;

Related

Is it possible to call a function while not using all of its code in JavaScript?

Let's say I have a JS function that returns a value but has an alert command in it. I want to assign the returned value to a variable later on in the code. Is there a way to call the function but to ignore the alert command, in order to just assign the returned value to a variable later?
For example:
Let's say I havefunction f1(num) { alert ("hi); return num * 2; }
and thenfunction f2() { var x = f1(2); return x;}.
How can I ignore the alert and only save the returned
value in a variable on later functions?
Add an additional parameter to f1() to skip the alert:
function f1(num, skipAlert) {
if (skipAlert !== true) alert("hi");
return num * 2;
}
function f2() {
var x = f1(2);
return x;
}
function f2_skipAlert() {
var x = f1(2, true);
return x
}
document.querySelector('button#alert').addEventListener('click', function () {
console.log(f2());
});
document.querySelector('button#skip-alert').addEventListener('click', function () {
console.log(f2_skipAlert());
});
<button id="alert">Run with alert</button>
<button id="skip-alert">Run without alert</button>
Solution if you cannot change the f1 function
You can disable it with the following. Remember to define the disable function before the execution of the one that contains the alert function.
window.alert = function() {};
If you need to re-enable it, you can do temporarily associate it to another variable:
var oldalert = window.alert;
Then disable it as I did before and then re-enable later with window.alert = oldalert;
As esqew said, it's not a good practice to override it
Example of Use
function disableAlert(){
window.oldalert = window.alert;
window.alert = function() {};
}
function enableAlert(){
window.alert = window.oldalert;
}
And then, in your f2 function you can do like this:
function f2() {
disableAlert();
var x = f1(2);
enableAlert();
return x;
}
Solution if you can change the f1 function
Similar to esqew solution but using default parameters
function f1(num, skipAlert = false) {
if (skipAlert) alert("hi");
return num * 2;
}
function f2(skipAlert = false) {
var x = f1(2, skipAlert);
return x
}
If you want to call with alert you can use f2(true) otherwise just f2().

Declaration and assigning value to a variable inside the scope and trying to obtain it from the ouside doesn't work while only assigning does

After along gap, I've returned back to learning JavaScript. Here is an example of what I was trying to say in the title
function randomFunction(){
for(i = 0; i < 10; i++){
x = i;
}
return x;
}
console.log(randomFunction());
This returns the value of x without any error but instead of x = i, if I used let x = i;, now x is undefined error is shown. It is defined, right? I mean let x = 5; defines and assigns value both at the same time, then shouldn't let x = i do the same? Or can I only assign values and not variables to x (i being another variable).
Plus I had another question as well, can you call a function, that first was declared as a function which accepts parameters, without providing any parameters? for example,
function randomFunction(x){
return "Hello World";
}
console.log(randomFunction());
While testing that specific function only, I do get Hello World in return but when I use similar idea in other projects, I get some sort of error (I can't remember what the error was though). I just wanted to know can I do that? So that I can rule that out as not being the problem and look for bug somewhere else.
So a couple of things went wrong here:
Don't use the literal string "this.num1 + this.num2" but use the expression that take the variables and sum them together this.num1 + this.num2.
You used event listener on element that is not appeared on the screen, so how can you click it? You probably meant to use the click event on the id button and not the demo.
As a rule of thumb, I don't think it's good to involve UI logic with your classes logic. For future use, it usually better to keep them separated.
Good Luck
class Sum {
constructor(num1, num2) {
this.num1 = num1;
this.num2 = num2;
}
add() {
this.result = this.num1 + this.num2;
}
}
const s = new Sum(6, 7);
document.getElementById("button").addEventListener("click", () => {
s.add();
displayResult(s.result);
});
function displayResult(result) {
document.getElementById("demo").innerText = result;
}
<button id="button">Click</button>
<p id="demo"></p>
You don't nee the quotes in the add function and you are missing. this before the nums
this.result = this.num1 + this.num2
But as you are passing in strings to the constructor it will just concatenated the values so the output will be ”ab”
If those were supposed to be variables it should be:
var a = 5;
var b = 7;
const s = new Sum(a, b);
Here your code:
class Sum {
constructor(num1, num2) {
this.num1 = num1;
this.num2 = num2;
}
add() {
this.result = this.num1 + this.num2;
}
displayResult() {
document.getElementById("demo").innerText = this.result;
}
}
const s = new Sum("a", "b");
document.getElementById("button").addEventListener("click", () => {
s.add();
s.displayResult();
});
<button id="button">Click</button>
<p id="demo"></p>

Function does not return value

I've only started to learn how to refactor code and I'm failing at abstracting a simple function. I pass the parameters into checkAnwser and it works, but count is "lost"
I can't get/append "count" here:
jsfiddle
<input data-correctanswer="javascript" id="answer1" name="" type="text">This works fine (no special chars)
<br/>
<button id="btn1">check 1</button>
<br/>
<input data-correctanswer="jávascripté" id="answer2" name="" type="text">
<br/>
<button id="btn2">check 2</button>
<div style="border: 1px solid;" id="result"></div>
Javascript:
$(document).ready(function () {
var count;
$('#btn1').click(function () {
checkAnswer($('#answer1').data('correctanswer'), $('#answer1').val());
$('#result').append('result: ').append(count); <-- does not read count
}); // end of click
$('#btn2').click(function () {
checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val());
$('#result').append('result: ').append(count); // <-- does not read count
}); // end of click
function checkAnswer(coorAns, UserAnswer) {
var count = 0;
// var coorAns = $('input[type=text]').data('correctanswer');
// var UserAnswer = $('input[type=text]').val();
var mistakesAllowed = 1;
if (UserAnswer === coorAns) {
count = count + 2;
}
for (i = 0; i < coorAns.length; i++) {
if (coorAns.charAt(i) !== UserAnswer.charAt(i)) {
mistakesAllowed--; // reduce one mistake allowed
if (mistakesAllowed < 1) { // and if you have more mistakes than allowed
count = count + 1;
}
if (mistakesAllowed < 0) {
count = count - 2
break;
}
}
}
console.log('final count: ' + count);
return count;
}
});
What Adeneo said:
var count; //here it's defined.
$('#btn1').click(function () {
count = checkAnswer($('#answer1').data('correctanswer'), $('#answer1').val());
$('#result').append('result: ').append(count);
}); // end of click
$('#btn2').click(function () {
count = checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val());
$('#result').append('result: ').append(count);
}); // end of click
Your function checkAnswer returns a value called count. That value can be assigned to the variable count.
Probably the chain of thought you had was that assigning count in the function checkAnswer would also assign it to the variable count outside the function. However those two variables are in two different scopes and are not connected to eachother unless you assign the result of the function to the variable count outside the function.
To be more precise:
checkAnswer is in the same scope as the first count variable. That means you could do this:
var count = 0;
console.log(count); //will log 0.
function checkAnswer()
{
count = 1;
}
checkAnswer();
console.log(count); //will log 1.
However when you use the operator var inside a function it will create a variable that is bound to the scope of the function (it becomes a private variable) only accessible within that function. Unless you return it to a variable outside the scope of that function.
var count = 0;
console.log(count); //will log 0.
function checkAnswer()
{
var count = 1;
}
checkAnswer();
console.log(count); //will log 0.
More on scopes on Stack Overflow:
What is the scope of variables in JavaScript?
Bonus:
A little efficiency suggestion for your code
var count;
$('#btn1', '#btn2').click(function () {
var buttonId = $(this).attr("id").substr(4); //will result in 1 or 2
count = checkAnswer($('#answer' + buttonId ).data('correctanswer'), $('#answer' + buttonId ).val());
$('#result').append('result: ').append(count);
}); // end of click
This will reduce it to one generic function. So when you need to update your code you only need to update one function, not multiple instances of the same code.
Instead of
checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val());
use
var count=checkAnswer($('#answer2').data('correctanswer'), $('#answer2').val()); //Stored returned value to count variable.
Now you can access returned value.
Here is your updated fiddle.

I keep receiving Uncaught TypeError: Object [object global] has no method error during function call

I created a function that would do something unique on it's 3rd call. In this case, alert the incremented number. However, when trying to call this function from another function:
"MyFunction();", I get Uncaught TypeError: Object [object global] has no method 'MyFunction'. Can you please tell me what I'm doing wrong?
var counter = 0;
var num = 0;
function = MyFunction() {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}
I've also tried removing the = sign, as you can see here http://jsfiddle.net/DN3yC/6/ it doesn't work.
LIVE DEMO
Just remove the = sign function MyFunction() { and make sure in your fiddle that JS <script> is in the right placeSee additional explanation below.
Example:
var element = document.getElementById('button');
var counter = 0;
var num = 0;
function MyFunction() {
counter = ++counter % 3; // loop count
if ( !counter ) {
num++;
alert('the new number is: ' + num);
}
}
//On click:
element.addEventListener('click', MyFunction, false);
your new fiddle: http://jsfiddle.net/DN3yC/7
The old one of yours was not working cause you did not used No Wrap - in body for the JS in your fiddle. (See options panel at the left up corner)
So basically you need to put your <script> tags before your </body> closing tag in order to make sure the DOM is ready and parsed by JavaScript before elements manipulation.
Syntacs looks wrong to me, try
var foo = Myfuntion();
or
var foo = new MyFuntion();
depending on what MyFuntion() is.
It should be:
var MyFunction=function () {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}

Why exectuing function doesnt work?

I have an input which when im clicking - i want to see alert with '1,2,3,4...' (each press)
<input type='button' value='press' onclick='Clicked();' />
<script>
var t
function Clicked()
{
t=func;
t();
}
function func()
{
var count=0;
return new function () // <=== new or not new ???
{
count++;
alert(count);
}
}
</script>
If im adding the 'new' in the return and click , it says : '1,1,1,1,...'
If im removing the 'new' it doesnt work...
My goal is to use this to get : '1,2,3,4...'
Can someone explain to me what happens ?
You need to use the returned function:
var t = func()
function Clicked() {
t();
}
function func() {
var count=0;
return function () {
count++;
alert(count);
}
}
Example
You have to put the count declaration out of the "func" function and into the global namespace. Like this:
var count=0;
function func() {
count++;
alert(count);
}
You're creating a new var count every time the clickevent fires.. You should place the var count = 0 outside the function as a global variable..
oh and btw, throw out the return stuff please this will work properly:
var count = 0;
function func()
{
count++;
alert(count);
}
And call this method like this: func();
You are getting 1,1,1,1 because you are redefining count every time you call func().
In your case you will need to either put the count variable in the global scope:
var t;
var count = 0;
or change the way you are doing things a little:
var t = 0;
function Clicked(){
t = func(t);
}
function func(count){
count++;
alert(count)
return count;
}
Personally I like the second one. You have less global variables, one less function declaration, and it is cleaner
var t
function Clicked()
{
t=func;
t();
}
var count=0;
function func()
{
return new function () // <=== new or not new ???
{
count++;
alert(count);
}
}

Categories

Resources