In this code, I add an action for a "bet", and pass its id as a parameter to a function. But when I call this arrow function later, the argument of this.undoBet equals to this.local_bets[this.local_bets.length].bet_id - the last bet_id that was passed inside the loop.
How to make it so that inside every arrow function, this.undoBet would preserve the bet_id assigned to it in that loop?
for (var k in this.local_bets) {
var bet = this.local_bets[k];
if (bet.status == BetStatus.accepted) {
// Here bet_id is correct for every "bet" variable
this.addUndo( "undo_bet", () => {
// When calling this later, bet_id equals to one that belongs to the last bet inside this.local_bets
this.undoBet( bet.bet_id );
});
}
}
A common JavaScript mistake. Its because for (var k in this.local_bets) { the index will actually be the last one by the time the continuing function executes.
Fix
use let:
for (let k in this.local_bets) {
let bet = this.local_bets[k];
More
This is covered here https://basarat.gitbooks.io/typescript/content/docs/let.html 🌹
Try this (another local variable will be scoped in the each new undo bet function):
this.local_bets
.filter(bet => bet.status == BetStatus.accepted)
.forEach(bet => this.addUndo("undo_bet", () => this.undoBet(bet.bet_id)));
All solutions are good, but better is to use const keyword when variable is not supposed to change. Check the code below:
for (const k in this.local_bets) {
const bet = this.local_bets[k];
if (bet.status == BetStatus.accepted) {
// Here bet_id is correct for every "bet" variable
this.addUndo( "undo_bet", () => {
// When calling this later, bet_id equals to one that belongs to the last bet inside this.local_bets
this.undoBet( bet.bet_id );
});
}
}
Related
I was a little stuck on figuring out how to trigger different functions if the same key was pressed. For instance, if space was pressed, it would call function1. If space was pressed again, it would call function2. If space was called one more time, it will call function3.
I currently have the following code and it seems like my code never enters the last two if statements. Any tips on how to fix this? Thanks in advance!
document.addEventListener("keydown", function (e) {
if (e.code == "") {
function1();
if (e.code == "") {
function2();
}
if (e.code == "") {
function3();
}
}
JavaScript events are what make sure that they trigger a function when something happens. So, if you want to add several functions you have to make sure something happens to trigger them. So if you say you have to run several functions when a key is pressed it has no meaning if you are not binding some other function to that key. Because if the only thing you have is one key pressing event you cannot possibly think of running multiple functions each time that key is pressed. But you can tweak the code a little bit to run multiple functions too.
Here's an example of how to do that.
HTML:
<p class='p'>Hello<p>
CSS:
.red{
background:red;
}
.green{
background:green;
}
.blue{
background:blue;
}
JS:
let v=0
const p = document.querySelector('.p')
document.body.onkeydown= function (e) {
if (e.keyCode == 32) {
switch(v % 3){
case 0:
p.classList.add('red')
p.classList.remove('green')
p.classList.remove('blue')
v += 1
console.log(v)
break
case 1:
p.classList.add('green')
p.classList.remove('red')
p.classList.remove('blue')
v += 1
break
case 2:
p.classList.add('blue')
p.classList.remove('green')
p.classList.remove('red')
v += 1
break
}
}
}
Here, you can see that I am making sure that another function is running hidden to select the functionality I want.
Since your event handler will be called for every single event, you have to use some sort of debounce function. Basically a timer and some logic that keeps track of how many times the event occured in a given time frame.
Let's start with a little helper function that takes care of delaying the call of your 3 event handler functions.
const debounceKeyPress = (keyCode, ...fns) => {
let noOfOccurrences = -1; // we're starting with -1 here, so we don't need to re-calculate for use with index
let timeout;
return (ev) => {
if (ev.keyCode !== keyCode) {
return;
}
noOfOccurrences += 1;
clearTimeout(timeout);
if (fns[noOfOccurrences]) {
timeout = setTimeout(() => { fns[noOfOccurrences](ev); noOfOccurrences = -1; }, 500);
}
}
}
Now you have a general function that takes a key code and a number of functions. The last part is variable. So you can also pass 4 or 5 functions. It returns another function that you can then use as event listener.
const fn1 = (ev) => console.log(`key ${ev.keyCode} was hit once.`);
const fn2 = (ev) => console.log(`key ${ev.keyCode} was hit twice.`);
const fn3 = (ev) => console.log(`key ${ev.keyCode} was hit three times.`);
document.addEventListener('keydown', debounceKeyPress(32, fn1, fn2, fn3));
You can play with the time parameter of the setTimeojut function a bit to see how long or short it actually needs to be. It is also possible to include that as a param of your helper function debounceKeyPress to get more flexibility.
Hope that works for you.
Cheers.
A.
I have a property defined but when I try to access it from inside a setInterval anonymous function, it is not being recognized.
this.game.seconds = 6;
useTimer() {
let timer = setInterval(
function () {
this.game.seconds--;//here the keyword this is not being recognized
}, 1000
);
}
The problem occurs because you are not not using an Arrow Function.
Arrow functions take this from their outer execution context.
The ref mentions:
arrow functions which do provide their own this binding (it remains
the this value of the enclosing lexical context).
Read more in No Binding of this.
So just to this instead:
let timer = setInterval(
() => {
this.game.seconds--; // 'this' takes its value from the outer context
}, 1000
);
Well I could fulfill my goal with arrow functions as commented by Dummy and answered by gsamaras:
this.game.seconds = 6;
useTimer() {
let timer = setInterval(
() => {
this.game.seconds--;
}, 1000
);
}
more info here.
I am having trouble passing a kata. I believe I am on the right track, but do not fully understand how to retrieve the desired results.
The Instructions
Write a method that takes in a function and the arguments to the function and returns another function which when invoked, returns the result of the original function invoked with the supplied arguments.
Example Given
Given a function add
function add (a, b) {
return a + b;
}
One could make it lazy as:
var lazy_value = make_lazy(add, 2, 3);
The expression does not get evaluated at the moment, but only when you invoke lazy_value as:
lazy_value() => 5
Here is my half a day endeavor conclusion
var make_lazy = function () {
var innerFunction = null;
var array = [];
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == 'function') {
innerFunction = arguments[i];
} else {
array.push(arguments[i]);
}
}
innerFunction.apply(innerFunction, array);
innerFunction();
};
I'm using arguments and apply() and think I am close? However I am getting TypeError: lazy_sum is not a function at Object.exports.runInThisContext within test results. Any help, especially understanding what is going on, is appreciated. Thanks
...
return function() {
return innerFunction.apply(this, array);
};
};
Thanks again all. Problem solved.
I'm attempting to gradually refactor existing code. I have a set of functions that are defined, and only differ by one of the internal arguments:
function loadGame1():void
{
loadGame("save1");
}
function loadGame2():void
{
loadGame("save2");
}
function loadGame3():void
{
loadGame("save3");
}
//... snip many, many lines
// Note- I cannot pass function arguments at this time!
picker(loadGame1, loadGame2, loadGame3 ...);
I'm trying to refactor at least part of this (I can't completely replace the whole thing yet, too many interdependencies).
Basically, I want to be able to generate a big set of functions with the difference between the functions being a internal parameter:
var fNames:Array = new Array("save1", "save2", "save3");
var funcs:Array = new Array();
for (var i = 0; i < fNames.length; i += 1)
{
trace("Creating function with indice = ", i);
funcs.push(
function() : void
{
saveGame(fNames[i]);
}
)
}
picker(funcs[0], funcs[1], funcs[2] ...);
However, as I understand it, closure is causing the state of i to be maintained beyond the scope of the for loop, and any attempt to call any of the generated functions is failing with an out-of-bounds error, which is what you would expect given that i will reach fNames.size + 1 before i < fNames.size evaluates to false.
So, basically, given that I need to generate functions that are passed as arguments to a pre-existing function that I cannot change currently. How can I dynamically generate these functions?
Try to use IIFE:
for (var i = 0; i < fNames.length; i += 1)
{
(function(i){
trace("Creating function with indice = ", i);
funcs.push(
function() : void
{
saveGame(fNames[i]);
}
)
})(i);
}
I'm no code genius, but a fan of action script.
Can you help me on this:
I have a function that depending on the object selected, will call event listeners to a set of 'sub-items' that are already on stage (I want to reuse this subitems with changed parameters upon click, instead of creating several instances and several code).
So for each selected 'case' I have to pass diferent variables to those 'sub-items', like this:
function fooMenu(event:MouseEvent):void {
switch (event.currentTarget.name)
{
case "btUa1" :
trace(event.currentTarget.name);
// a bunch of code goes here
//(just cleaned to easy the view)
/*
HELP HERE <--
here is a way to pass the variables to those subitems
*/
break;
}
}
function fooSub(event:MouseEvent):void
{
trace(event.target.data);
trace(event.currentTarget.name);
// HELP PLEASE <-> How can I access the variables that I need here ?
}
btUa1.addEventListener(MouseEvent.CLICK, fooMenu);
btUa2.addEventListener(MouseEvent.CLICK, fooMenu);
btTextos.addEventListener(MouseEvent.CLICK, fooSub);
btLegislacao.addEventListener(MouseEvent.CLICK, fooSub);
Anyone to help me please?
Thank very much in advance. :)
(I'm not sure I got your question right, and I haven't developed in AS3 for a while.)
If you want to simply create function with parameters which will be called upon a click (or other event) you can simply use this:
btUa1.addEventListener(MouseEvent.CLICK, function() {
fooMenu(parameters);
});
btUa2.addEventListener(MouseEvent.CLICK, function() {
fooMenu(other_parameters)
}):
public function fooMenu(...rest):void {
for(var i:uint = 0; i < rest.length; i++)
{
// creating elements
}
}
If you want to call event listeners assigned to something else you can use DispatchEvent
btnTextos.dispatchEvent(new MouseEvent(MouseEvent.CLICK))
Remember, you can't use btTextos.addEventListener(MouseEvent.CLICK, carregaConteudo("jocasta")); because the 2nd parameter you pass while adding Eventlistener will be considered as function itself - there are two proper ways to use addEventListener:
1:
function doSomething(event:MouseEvent):void
{
// function code
}
element.addEventListener(MouseEvent.CLICK, doSomething); //notice no brackets
2:
element.addEventListener(MouseEvent.CLICK, function() { // function code });
So:
function fooSub(event:MouseEvent, bla:String):void
{
trace(event.currentTarget.name+" - "+bla);
// bla would be a clip name.
}
codebtTextos.addEventListener(MouseEvent.CLICK, function(e:MouseEvent) { fooSub(e, "jocasta") } );
Or try something like this if you want content to be dynamically generated:
btUa1.addEventListener(MouseEvent.CLICK, function() {
createMenu(1);
});
btUa2.addEventListener(MouseEvent.CLICK, function() {
createMenu(2);
});
function createMenu(id):void
{
// Switching submenu elements
switch (id)
{
case 1:
createSubmenu([myFunc1, myFunc2, myFunc3]); // dynamically creating submenus in case you need more of them than u already have
break;
case 2:
createSubmenu([myFunc4, myFunc5, myFunc6, myFunc7]);
break;
default:
[ and so on ..]
}
}
function createSubmenu(...rest):void {
for (var i:uint = 0; i < rest.length; i++)
{
var mc:SubItem = new SubItem(); // Subitem should be an MovieClip in library exported for ActionScript
mc.addEventListener(MouseEvent.CLICK, rest[i] as function)
mc.x = i * 100;
mc.y = 0;
this.addChild(mc);
}
}
Your question is rather vague; what "variables" do you want to "pass"? And what do you mean by "passing the variable to a sub item"? Usually "passing" means invoking a function.
If you can be more specific on what exactly your trying to do that would be helpful. In the meantime, here are three things that may get at what you want:
You can get any member of any object using bracket notation.
var mc:MovieClip = someMovieClip;
var xVal:Number = mc.x; // The obvious way
xVal = mc["x"]; // This works too
var propName:String = "x";
xVal = mc[propName] ; // So does this.
You can refer to functions using variables
function echo(message:String):void {
trace(message);
}
echo("Hello"); // The normal way
var f:Function = echo;
f("Hello"); // This also works
You can call a function with all the arguments in an array using function.apply
// Extending the above example...
var fArgs:Array = ["Hello"];
f.apply(fArgs); // This does the same thing
Between these three things (and the rest parameter noted by another poster) you can write some very flexible code. Dynamic code comes at a performance cost for sure, but as long as the frequency of calls is a few hundred times per second or less you'll never notice the difference.