[RESOLVED]
Was calling the newGame function from an event listener so this would then refer to the event caller
I'm having this annoying issue with JS at the moment, I'm creating a game where all the variables for a particular game are stored in the Game() class/function/object and have a GameManager which creates the initial game and has a function to create a new one.
The problem is, I cannot overwrite the initial game in the newGame function, here it is:
var GameManager = {};
GameManager.currentGame = null;
GameManager.init = function() {
this.newGame();
}
GameManager.newGame = function() {
this.currentGame = new Game();
}
GameManager.init();
After running the newGame function, the result:
GameManager.currentGame == the original game, NOT the new one.
To put things in a simpler form, I've used int's here:
var GameManager = {};
GameManager.currentGame = null;
GameManager.init = function() {
this.currentGame = 12;
}
GameManager.newGame = function() {
this.currentGame = 0;
}
GameManager.init();
After running the newGame function, the result:
GameManager.currentGame == 12 (NOT 0, as expected)
I have a feeling this is an obvious issue with object referencing or something similar, I'd appreciate all responses!
Thanks
Related
I'm simply implementing a function that is called after a button is clicked. The function works fine, but i'm not capable of making it return a value and assign it to a variable.
I would like to call the function play once the button is clicked, and then return the values (as shown in the code) to a variable, in order to make them accessible for future uses.
I've already implemented all I need (i'm messing around with the web audio api) and it works fine, the test1 variable "embeddeds" the osc and lfo variables and i'm able to access them outside the function.
var ac = new AudioContext();
function play(){
var osc = ac.createOscillator();
var lfo = ac.createOscillator();
osc.frequency.value=1000;
console.log("Clicked!!");
returnedObject={};
returnedObject["value1"] = osc;
returnedObject["value2"] = lfo;
return returnedObject;
};
var test1= play();
var test= document.getElementById("mybtn").addEventListener("click", play);
The test1 variable contains what i need, but the test variable doesn't.
How can i assign to a variable the returned object of a function after it's been called by an event?
What i'm getting right now if i use test.value1 is an undefined error, this makes me think that the assignment of the variable failed.
Answer:
You can do this, but you have to consider that the value is undefined until the button is clicked.
var ac = new AudioContext();
function play(){
var osc = ac.createOscillator();
var lfo = ac.createOscillator();
console.log("Clicked!!");
returnedObject={};
returnedObject["value1"] = osc;
returnedObject["value2"] = lfo;
return returnedObject;
};
var test;
document.getElementById("mybtn").addEventListener("click", () => (test = play()));
<button id="mybtn">click me</button>
In order to perform functions with the data you probably want to create a middle function that takes care of whatever work you want done:
var ac = new AudioContext();
function play(){
var osc = ac.createOscillator();
var lfo = ac.createOscillator();
console.log("Clicked!!");
returnedObject={};
returnedObject["value1"] = osc;
returnedObject["value2"] = lfo;
return returnedObject;
};
function middle(fn) {
return function() {
let data = fn();
//do something
console.dir(data);
}
}
document.getElementById("mybtn").addEventListener("click", middle(play));
<button id="mybtn">click me</button>
Alternatively you can create a Constructor that holds onto the data, thus allowing you to add methods to the Constructor for manipulation at a later time:
function Player() {
let proto = {};
proto.ac = new AudioContext();
proto.data = [];
proto.play = function() {
var osc = proto.ac.createOscillator();
var lfo = proto.ac.createOscillator();
proto.data.push({
value1: osc,
value2: lfo
});
proto.latest = proto.data[proto.data.length-1];
console.log("Clicked!!", proto.latest);
};
return proto;
}
const myPlayer = new Player();
document.getElementById("mybtn").addEventListener("click", myPlayer.play);
<button id="mybtn">click me</button>
I will suggest you to use closure instead.
Try something like this.
// closure
function audioControl() {
var ac = new AudioContext();
var props = {
value1: ac.createOscillator(),
value2: ac.createOscillator()
};
// function play
function play() {
osc.frequency.value = 1000;
console.log("Clicked!!");
props.value1 = "_modified_value";
props.value2 = "_modified_value";
}
return {
props: props,
play: play
}
};
var test1 = audioControl();
var test = document.getElementById("mybtn").addEventListener("click", test1.play);
// now you can get values anywhere anytime using
// -- test1.props.value1;
addEventListener, by design, is meant to don't return anything at all (thanks #AmitJoki for mentioning that) as you can read in the MDN docs.
The relevant part is (excerpt of this section)
Event listeners only take one argument, the Event Object, which is automatically passed to the listener, and the return value is ignored
The reason behind that is that addEventListener is just meant to register an event, not to handle the result of it.
If you want to assign the result to the global variable test, you should just do that inside your play callback.
Recently I've been trying to use pixi.js for some fun project and I come across a concept that I do not understand at all. Quoting some code:
PIXI.loader
.add([
"images/one.png",
"images/two.png",
"images/three.png"
])
.on("progress", loadProgressHandler)
.load(setup);
function loadProgressHandler(loader, resource) {
console.log(`loading: ${resource.url}`);
};
How these arguments (loader, resource) are passed to the function since we only pass the reference to it in the event listener? Can someone show a generic implementation beneath that concept?
Lets say we have a function called callMe that just prints a number that its given:
function callMe(number) {
console.log(`I'm number: ${number}`);
}
callMe(2);
We can create a new variable to that same function, and call the newly created variable. This is possible since it's pointing to the same function that we've created earlier.
const callMeAswell = callMe;
callMe(3);
callMeAswell(4);
In short, this is what's happing inside the PIXI loaders, except for that it's stored somewhere else for you. Lets create a class to store the numbers and the function that we want to call:
function SomeLoader(){
this.numbers = []; // list of numbers we want to store for later usage
this.func = null; // function that we want to call when we're done loading
}
SomeLoader.prototype.add = function(number) {
this.numbers.push(number); // add the number to the list of numbers
}
SomeLoader.prototype.on = function(func) {
this.func = func; // just store the function for now, but don't do anything with it
}
SomeLoader.prototype.pretendToLoad = function() {
for(const number of this.numbers) {
this.func(number); // now we're going to call the function that we've stored (callMe in the example below)
}
}
const loader = new SomeLoader();
loader.add(5);
loader.add(6);
loader.on(callMe);
loader.pretendToLoad();
Or fluently:
function SomeLoader(){
this.numbers = [];
this.func = null;
}
SomeLoader.prototype.add = function(number) {
this.numbers.push(number);
return this;
}
SomeLoader.prototype.on = function(func) {
this.func = func;
return this;
}
SomeLoader.prototype.pretendToLoad = function() {
for(const number of this.numbers) {
this.func(number);
}
}
new SomeLoader()
.add(7)
.add(8)
.on(callMe)
.pretendToLoad();
Looks almost the same as the PIXI loaders, doesn't it? :)
Arguments are passed to the function when it is called.
The code which calls that function isn't in the question. It is done somewhere behind the on function.
In short: The same way as normal, you just aren't looking at the point where it happens.
const show = value => console.log(value);
const call_callback_with_hello_world = callback => callback("Hello, world");
call_callback_with_hello_world(show);
What #Quentin said is correct - adding on to that however...
A generic concept beneath that implemention is called a callback and would look like so:
function Loop(callback, index){
callback(index);
}
function CallbackFunction(val){
console.log(val)
}
for(var i = 0; i < 5; i++){
Loop(CallbackFunction, i);
}
I've been trying to implement some card game using Javascript. In the snippet below, I simply want to pull two cards from the top of the deck and give it to the player (simplified logic below)
function deck() {
var faceCards = [['jack', 11],['queen', 12],['king', 13]];
return faceCards;
}
function removeCard() {
var singleCard = deck().pop();
var faceValue = singleCard[0];
return faceValue;
}
var cardPair = [removeCard(),removeCard()];
console.log(cardPair);
However the faceCards array is still the same even after popping off its cards which means that the next card will be the same as well as seen in cardPair array.
I need to mirror the effects I used inside of the
removeCard() function to reflect back in the deck() function.
I suppose I could either create the faceCards array in the global scope or use 'this' in some way (which I don't really want as I'm not much familiar with it). How can I update one function from inside another function? Thank you very much for reading this.
Your error is here:
var singleCard = deck().pop()
The call to deck() creates a new array of cards every time it's called, and doesn't repeatedly return the same array.
If you don't want to go full OO yet, consider at least passing the deck as a parameter to the removeCard() function, i.e.
function removeCard(deck) {
var singleCard = deck.pop();
var faceValue = singleCard[0];
return faceValue;
}
var deck = newDeck();
var pair = [ removeCard(deck), removeCard(deck) ];
but ultimately you should longer term be going for a full OO solution, where your usage might then become:
var deck = new Deck();
var pair = [ deck.takeCard(), deck.takeCard() ];
implementation of this is out of scope of this particular question.
You're creating a new array every time you run deck(). Try saving the deck in an array and running pop on this:
function deck() {
var faceCards = [['jack', 11],['queen', 12],['king', 13]];
return faceCards;
}
function removeCard(fromDeck) {
var singleCard = fromDeck.pop();
var faceValue = singleCard[0];
return faceValue;
}
var thisDeck = deck();
var cardPair = [removeCard(thisDeck),removeCard(thisDeck)];
console.log(cardPair);
I've tried to search for an answer to my question, but I'm starting to think that, given the lack of results, I'm obviously not expressing the question properly. With that in mind, apologies if the title of this post is misleading, I am still very much learning.
I have a simplified version of my code below
var testData = ['a', 'b']
var addReceiver = (function () {
dataReceiver = function (dataInput) {
t = this
t.data = dataInput;
console.log(t.data);
t.interval = setInterval(function () {
t.update();
}, 1000);
t.stopUpdate = function () { clearInterval(t.interval); };
t.update = function () {
//t.data = dataInput;
console.log(t.data);
};
};
})()
var testLogger = new dataReceiver(testData);
</script>
The behaviour that I wish to emulate is when I type into the console
testData = ['c','d','e']
for testLogger to log the array ['c','d','e'] every second rather than ['a','b'].
I could change
t.data = dataInput to t.data = testData
to achieve this, but that would obviously be pointless, or else every new instance I created of dataReceiver would have a reference to the same data input.
So, my question is, how would I need to change the code to provide the testLogger vairable (or any instance of dataReceiver) access to a variable outside of its local scope?
Use the object that you have created instead of accessing the global variable,
var testLogger = new dataReceiver(testData);
testLogger.data = [1,2,3,4];
Now you will be able to print the newly injected data. I mean the setInterval will print the updated value.
I'm trying to create a simple game loop and trying to use OOP paradigm in JS. Here is my code:
HTML
<body onload="Game.OnLoad('gameField')" onkeydown="Game.KeyDown(event)">
<p id="info">1</p>
<p id="info2">2</p>
<canvas id="gameField"
width="896px"
height="717px"
class="game-field"
style="border: 4px solid aqua"
onclick="Game.MouseClick(event)"></canvas>
</body>
JavaScript
// class Timer
// version: 1
// only tick() functionality available
// right now
function Timer() {
var date = new Date();
var prevTick = 0;
var currTick = 0;
// update timer with tick
this.tick = function() {
prevTick = currTick;
currTick = date.getTime();
}
// get time between two ticks
this.getLastTickInterval = function() {
return currTick - prevTick;
}
}
// global object Game
// which handles game loop
// and provide interfaces for
var Game = new function() {
// variables:
this.canvas = 0;
var gameLoopId = 0;
this.timer = new Timer();
// events:
this.KeyDown = function(e) {}
// game loop:
this.Run = function() {
this.timer.tick();
this.Update(this.timer.getLastTickInterval());
this.Draw();
}
this.Update = function(dt) {
document.getElementById("info").innerHTML = dt;
}
this.Draw = function() {}
this.StopGameLoop = function() {
clearInterval(gameLoopId);
}
this.OnLoad = function(canvasName) {
this.canvas = document.getElementById(canvasName);
this.timer.tick();
// start game loop
setInterval(this.Run, 1000);
}
}
(Fiddle)
I'm trying to make Game class global. Other classes must be instantinated using new.
Classes Game and Timer are placed in different files called Game.js and Timer.js. When I run this code in Chrome I got an error in DevTools: "Uncaught TypeError: Cannot call method 'tick' of undefined" in Game.Run function at the line this.timer.tick();
So I wonder, what is the problem with my code? Thanks for reply.
Your problem is with the context. When you're calling tick this is window, not Game.
You can handle this, for example, by setting:
var self = this;
this.Run = function() {
self.timer.tick();
self.Update(self.timer.getLastTickInterval());
self.Draw();
}
Few things to consider in your code :
Concerning your issue, methode like setInterval or setTimeout loose the context. Easiest solution is to bind a context to the callback :
setInterval(this.Run.bind(this), 1000);
Secondly, avoid adding private methode inside an object function. In that case, every instance of Game will have its own set of functions (memory leak).
Prefer using prototypes :
function Game () {
this.canvas = null;
}
Game.prototype = {
init: function() {}
render: function () {}
};
Lastly, I see you redraw every seconds, which is ok. But if you want 60fps, you can use requestAnimationFrame for drawing oriented loops.
ps: just to be very nitpicking, functions' name should be camelCase so starting with lower case.