In the following code, check executes first and sets this.canClick to false. Then resetClick sets this.canClick to true after this.clickDelay seconds. However, the change is not seen in when check is called again. I logged the value of this.canClick in both functions: resetClick logs true, then check logs false for the same variable.
this.resetClick=function(){
this.canClick=true;
};
this.check=function(){
if (isMouseDown&&this.canClick==true){
if (mouse.x>=this.x-this.sizex/2 && mouse.x<=this.x+this.sizex/2 && mouse.y>=this.y-this.sizey/2 && mouse.y<=this.y+this.sizex/2){
this.text=this.callback();
this.canClick=false;
setTimeout(this.resetClick, this.clickDelay);
}
}
};
I don't believe it is a scope problem, because both are member functions of the same object and are modifying a property of that object. Also, in case you're wondering, that is a function to check if a button is clicked, and do "debouncing" so you don't click the same button twice by mistake.
EDIT: Important missing information: If I just call resetClick, without using setTimeout, it works.
It is indeed a scope issue.
this inside the function does not refer to the object which will contain the function later.
You should bypass it with this one simple trick (tm): use an alias for this
var that = this;
this.resetClick=function(){
that.canClick=true;
};
this.check=function(){
if (isMouseDown&&that.canClick==true){
if (mouse.x>=that.x-that.sizex/2 && mouse.x<=that.x+that.sizex/2 && mouse.y>=that.y-that.sizey/2 && mouse.y<=that.y+that.sizex/2){
that.text=that.callback();
that.canClick=false;
setTimeout(that.resetClick, that.clickDelay);
}
}
};
The method you are calling with setTimeout is run outside of the object scope, so this will only refer to the global window object at the time the method is executed (and that means this.canClick will be window.canClick actually).
Use a closure to preserve the value of this inside the object.
Related
I have been building a React app that uses iteration a lot. I am using JSLint and get the annoying warning saying:
Don't make functions within a loop
On the following loop:
if(currentSearch[i].users.length > 0) {
var matched = false;
//I hate how JSLint just can't handle setting a variable to true within a function
//this is loop where I get the warning
currentSearch[i].users.some(childUser => {
if(childUser.id === user.id) {
return matched = true;
}
})
//^^^^^ Warning
if(!matched) {
var alert = new AlertObject(user,currentSearch[i],true,false);
alerts.push(alert);
}
}
I don't think I set a function in the loop? I am using the array.some function which will break the loop if I return true, which is what I do. I return a variable, declared outside of the loop, as true. This breaks me out of the loop, and allows me to do logic below.
I should also be noted that this is also entirely within a loop, as we are iterating over current search users. I get no runtime or compile errors, and this code works fine, but maybe I am setting myself up for disaster in the future.
Any ideas why I am getting this error? And if I am missing some best practice?
Since in the first line you reference currentSearch[i], because the [i] I assume the whole block of code you pasted here is inside some kind of loop, probably a for.
Then, you are creating a function for the Array.some callback, which triggers the error.
One solution would be to move that callback declaration to be outside the parent loop, but since you are using a variable from the scope, it will require some refactor.
Posible solution
You can declare a function outside the parent loop (the one outside the code you provided here) that checks for the child user.
//Please provide a better name for the function according to the context.
const checkChildUser = function (childUser) {
return this.id === childUser.id;
};
And then pass it to the Array.some function you are using:
currentSearch[i].users.some(checkChildUser, user);
I'm not familiar with React, but this looks like an ES6 arrow function:
childUser => { ... }
Which would be the equivalent of
function (childUser) { ... }
The problem here is that you create a function that modifies the matched variable. This variable is declared with var, so it's scope is the whole function, not a single loop iteration. This could lead to surprising results, since functions created in each iteration will actually refer to the same variable.
Simply using the value returned by some() instead of changing matched in the callback (as suggested by Yury Tarabanko) should remove the warning.
In your snippet, the Don't make functions within a loop warning is not caused by the if statement, but by this following anonymous function:
childUser => {
if(childUser.id === user.id) {
return matched = true;
}
}
Since you've said that the entire code is within a loop, a new instance of that anonymous function is being created for every iteration. This will affect the performance.
Well, you are providing a function in your .some() as a parameter so thats what triggers the warning.
The reason ESLint warns against it
Writing functions within loops tends to result in errors due to the way the function creates a closure around the loop - source
You can do it like this
function compareId(childUser) {
if (childUser.id === user.id) {
return true;
}
}
if (currentSearch[i].users.length > 0) {
var matched = currentSearch[i].users.some(compareId);
if (!matched) {
var alert = new AlertObject(user, currentSearch[i], true, false);
alerts.push(alert);
}
}
var rectbutton = function(x,y,width,height,bevel,label,basecolor,textcolor,hovercolor,changevar,changevarvalue){
textAlign(CENTER,CENTER);
textSize(height);
fill(basecolor);
rect(x-width/2,y-height/2,width,height,bevel);
fill(textcolor);
text(label,x,y+5);
if(mouseX>=x-width/2 && mouseX<=x+width/2 && mouseY>=y-height/2 && mouseY<=y+height/2){
fill(hovercolor);
rect(x-width/2,y-height/2,width,height,bevel);
fill(textcolor);
text(label,x,y+5);
if(mouseIsPressed){
var changevar=changevarvalue;
}
}
};
Use:
rectbutton(200,250,250,50,5,"PLAY",color(255,255,255),color(0,0,0),color(255,0,0),playerState,1);
Everything works until I click. Doesn't set changevar to changevarvalue. Did try changevar=changevarvalue; instead of var changevar=changevarvalue;
In your code snipped there is no mouseIsPressed defined anywhere, is it missing? You might need to provide us a more complete code example.
Also try to look at your console if any javascript errors occure.
In js objects are passed by reference and primitives are passed by value. If you want to update your changevar variable, you would have to pass an object in changevar parameter while calling the rectbutton function and not some primitive value. I'm assuming playerState contains some primitive and not an object as a value.
This is a scope issue, correct?
EDIT: http://jsfiddle.net/RPwLK/7/
On line 38 of the following function, where displayBossMessage() is called by itself, the variable "boss" seems to be out of scope. It should contain the name (string) of the current boss, and when the function is called, it should put it back in the "boss" argument.
But boss seems to always be undefined. I've tried creating a variable right before the jQuery listener is created to see if it would be within scope, but it seems as if it isn't.
Or maybe I'm just having a slow day. ;p
function displayBossMessage(boss,message,options,timer){
boss = bosses[boss];
//clear any possible lingering text/buttons
$(boss.messagebox).text('');
$(boss.optionsbox).text('');
displayMessage_CurrentBoss = boss;
//if no options provided, set a default "continue" button
if(options == ''){
options = {
'default' : {
'text' : 'Continue',
'func' : function(){}
}
}
}
$('#container div').hide();
$(boss.div).fadeIn(1500);
writeMessage(message,$(boss.messagebox),0);
setTimeout(function(){
$(boss.optionsbox).fadeIn(1000);
},3000);
//"listen" for a choice
var i = 0;
for(option in options){
$(boss.optionsbox).html($(boss.optionsbox).html() + '<button name="'+ i +'">'+ options[option].text +'</button> ');
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
options[option].func();
//close message screen or show defined response
if(typeof options[option].response != 'undefined'){
displayBossMessage(boss,options[option].response,'',true);
}else{
$(boss.div).hide();
$('#container div').fadeIn(1500);
}
});
}
if(timer){
//if they are afk/don't click after a minute, do it for them
setTimeout(function(){
$(boss.div+' button[name="0"]').click();
},60000);
}
}
Hope I'm not being completely oblivious and missing something so simple.
*Edit: Bosses variable (is global) *
(updated jsfiddle revision link to #11 which includes both solutions)
Looks like this could be a working fiddle: http://jsfiddle.net/RPwLK/11/
A minor problem: you have an extra ' on line 30 with the (second) alert call - the string literal was not closed correctly (or rather another was being opened). After that I was able to investigate and come up with the following conclusion (2 problems)...
The first problem was with the variable override here:
function displayBossMessage(boss,message,options,timer){
boss = bosses[boss]; // this line, boss was a string, now it will be an object
And the later usage in the same function here:
if(typeof options[option].response != 'undefined'){
displayBossMessage(boss,options[option].response,'',true); // first arg is now an object
The solution is to create a reference to the original boss when it was a string like:
function displayBossMessage(boss,message,options,timer){
var origBoss = boss; // let's remember what it was in its original string form
boss = bosses[boss];
And use it like so:
if(typeof options[option].response != 'undefined'){
displayBossMessage(origBoss,options[option].response,'',true); // now we're dealing with a string ref
The second problem is the reference to option within the for loop. It was always referencing the last value since the $(document).on('click'... is always delayed (asynchronous). There are a number of ways to solve this. I chose to use bind and pass in an argument with a reference to the value of option for each specific iteration.
Notice that in the original option is in the async function but not in a closure (for is not a closure):
for(option in options){
//...
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
options[option].func(); // here option is always the last item in options
So introduce an argument conveniently called option in the callback function for the click handler:
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){ // here
options[option].func(); // and now option is whatever the argument value is
And don't forget to pass it in the function declaration via bind:
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){
options[option].func();
// ...
}.bind(this,option)); // here we're passing whatever option is for the specific iteration as the first argument of the callback function
Note, this is just the scope, and each subsequent parameter for bind (see MDN) after the first corresponds to the arguments in the function definition.
I have a weird behavior on my arms :
I declare an object, properties shall execute in sequence, which they do, but in one there is a simple pass through nothing written in it happens.
Do I have a scope problem ?
Here is a code fragment where verteX is my object
stepsCalcul: function() {
var test = 2;
verteX.testCheck = test*2; //returns undefined
alert('stuff') //alerts nothing
console.log("logged stuff")//logs nothing
verteX.mouseCoordinates();
return true;
},
I sort a jsFiddle aswell
http://jsfiddle.net/AEWrK/3/
Thanks!
Based on your fiddle, you're not calling the init function.
Replace verteX.init; with verteX.init();
Below is my code fragment:
<div onclick = "myClick('value 1')">
button 1
</div>
<div onclick = "myClick('value 2')">
button 2
</div>
Basically when I for each click on a different div, a different value will be passed to the JavaScript function.
My Question is how can I keep track of the value passed in the previous click?
For example, I click "button 1", and "value 1" will be passed to the function. Later, I click on "button 2", I want to be able to know whether I have clicked "button 1" before and get "value 1".
Just add it to a variable in your script:
var lastClicked;
var myClick = function(value) {
lastClicked = value;
};
You can define somekind of variable, like var lastUsed;
add additional line to your function:
var lastUsed = null;
function myClick(value){
prevClicked = lastUsed; //get the last saved value
...
lastUsed = value; //update the saved value to the new value
...
}
And here you go
You need a variable. Variables are like little boxes in which you can store values. In this case, we can store the value that was last passed to the function myClick.
In Javascript, you can define a variable like this:
var lastClickedValue;
You can "put" a value into that variable. Let's say you want to put your name in there. You would do this:
lastClickedValue = 'sams5817';
Now here's the tricky bit. Variables have "scope". You might want to think about it as their "life-time". When a variable reaches the end of its scope, you cannot read or write to it anymore. It's as if it's never been. Functions define a scope. So any variable you define in a function will disappear at the end of the function. For example:
function myClick(value)
{
var lastClickedValue;
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
That looks almost right, doesn't it? We declared a variable, display its last value, and update it with the new value.
However, since the lastClickedValue was declared in the function myClick, once we've reached the end of that function, it's gone. So the next time we call myClick, lastClickedValue will be create all over again. It will be empty. We call that an "uninitialized" variable.
So what's the problem? We're trying to remember a value even after the end of myClick. But we declared lastClickedValue inside myClick, so it stops existing at the end of myClick.
The solution is to make sure that lastClickedValue continues to exist after myClick is done.
So we must delcare lastClickedValue in a different scope. Luckily, there's a larger scope called the "global scope". It exists from the moment your page loads, and until the user moves on to another webpage. So let's do it this way:
var lastClickedValue;
function myClick(value)
{
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
It's a very small difference. We moved the declaration of the variable lastClickedValue to be outside the function myClick. Since it's outside, it will keep existing after myClick is done. Which means that each time we call myClick, then lastClickedValue will still be there.
This will let you know what the last value passed to myClick was.
Finally, I'd like to advise you to look for some kind of Javascript tutorials. I wish I knew of some good ones to recommend, but I'm certain you can find a few on the Internet. If you try to write programs before understanding what you're doing, you'll find yourself producing work that is less than what you're capable of. Good luck!
I suppose you need something like this
var clickedButtons = [];
function myClick(value){
...
clickedButtons.push(value);
...
}
I am surprised that no one else mentioned this, but since functions are first class objects in JavaScript, you can also assign attributes and methods to functions. So in order to remember a value between function calls you can do something like I have with this function here:
function toggleHelpDialog() {
if (typeof toggleHelpDialog.status === 'undefined')
toggleHelpDialog.status = true;
else
toggleHelpDialog.status = !toggleHelpDialog.status;
var layer = this.getLayer();
if (toggleHelpDialog.status) layer.add(helpDialog);
else helpDialog.remove();
layer.draw();
}
Here I have added an attribute named 'status' to the toggleHelpDialog function. This value is associated with the function itself and has the same scope as the toggleHelpDialog function. Values stored in the status attribute will persist over multiple calls to the function. Careful though, as it can be accessed by other code and inadvertently changed.
we can leverage javascript static variables
One interesting aspect of the nature of functions as objects is that you can create static
variables. A static variable is a variable in a functionâs local scope whose value persists across
function invocations. Creating a static variable in JavaScript is achieved by adding an instance
property to the function in question. For example, consider the code here that defines a function
doSum that adds two numbers and keeps a running sum:
function doSum(x,y){
if (typeof doSum.static==='undefined'){
doSum.static = x+y;
}else{
doSum.static += x+y;
}
if (doSum.static >= 100){doSum.static = 0;doSum.static += x+y;}
return doSum.static;
}
alert(doSum(5,15))
alert(doSum(10,10))
alert(doSum(10,30))
alert(doSum(20,30))