Function Call Hierarchy in JavaScript - javascript

My case it this:
function s () {
this.funcs = [];
this.funcs.addF = function (str) {
/* this will push a function to the funcs array, which uses getCoordX() and getPixelY() */
this.push (Function("pixelX", "var x = getCoordX(pixelX); var f = " + str + "; return getPixelY(f);"));
}
function getCoordX(a){
return 0;
}
function getPixelY(a){
return 0;
}
}
As you can see, in that array I'm adding functions that are created from strings, and those functions need do use getCoordX() and getPixelY(), which are in the s() object. When I try to access them it gives this error: Uncaught ReferenceError: getCoordX is not defined.
What should I do to make it work? Please help.
Edit 2
How i would use this code:
function s () {
this.funcs = [];
this.funcs.addF = function (str) {
/* this will push a function to the funcs array, which uses getCoordX() and getPixelY() */
this.push (Function("pixelX", "var x = getCoordX(pixelX); var f = " + str + "; return getPixelY(f);"));
}
this.drawCanvas = function() {
//some code goes here
this.drawGraph(c);
}
this.drawGraph = function (c) {
c.lineWidth = 2;
var cnt = 0; //count how many pixels have been rendered
for(var i = this.limitLeft; i < this.limitRight; i+= this.pixelwidth) {
for(var u = 0; u < this.funcs.length; u++) {
var f = this.funcs[u];
//some if statements go here
}
}
}
function getCoordX(a){
return 0;
}
function getPixelY(a){
return 0;
}
}
var canvas = document.createElement("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.id = "canvas";
document.body.appendChild(canvas);
var c = new Canvas("canvas");
c.funcs.addF("2*x");
c.drawCanvas();

You might do this:
function s () {
this.funcs = [];
this.funcs.addF = function (str) {
/* this will push a function to the funcs array, which uses getCoordX() and getPixelY() */
this.push (Function("pixelX", "getCoordX", "getPixelY", "var x = getCoordX(pixelX); var f = " + str + "; return getPixelY(f);"));
}
this.drawCanvas = function() {
//some code goes here
this.drawGraph(c);
}
this.drawGraph = function (c) {
c.lineWidth = 2;
var cnt = 0; //count how many pixels have been rendered
for(var i = this.limitLeft; i < this.limitRight; i+= this.pixelwidth) {
for(var u = 0; u < this.funcs.length; u++) {
var f = this.funcs[u];
var currvalue = f(i, getCoordX, getPixelY);
var lastvalue = f(i-1, getCoordX, getPixelY);
//some if statements go here
}
}
}
function getCoordX(a){
return 0;
}
function getPixelY(a){
return 0;
}
}
var canvas = document.createElement("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.id = "canvas";
document.body.appendChild(canvas);
var c = new Canvas("canvas");
c.funcs.addF("2*x");
c.drawCanvas();
This will do it. Hope this helps ;)

this isn't implicit in JavaScript, you must precise it.
Also don't use a string to create a function, just use
this.funcs.addF = function (str) {
var obj = this;
/* this will push a function to the funcs array, which uses getCoordX() and getPixelY() */
this.push (function(pixelX){
var x = obj.getCoordX(pixelX);
return obj.getPixelY(str);
});
}

The problem is that the Function constructor creates functions which run in the global scope. So your function can't access your getCoordX in the closure.
You could make getCoordX and getPixelY global functions:
function getCoordX(a) {
return a;
}
function getPixelY(a) {
return a;
}
function s() {
this.funcs = [];
this.funcs.addF = function (str) {
this.push(new Function("pixelX",
"var x = getCoordX(pixelX);" +
"var f = " + str + ";" +
"return getPixelY(f);"
));
};
}
var obj = new s();
obj.funcs.addF('x*3 + 5');
console.log(obj.funcs[0](1)); // 8
Alternatively, you could use the Function constructor only to evaluate str, and move the other code outside.
function s() {
this.funcs = [];
this.funcs.addF = function (str) {
var f = new Function('x', 'return ' + str);
this.push(function(pixelX) {
var x = getCoordX(pixelX);
return getPixelY(f(x));
});
};
function getCoordX(a) {
return a;
}
function getPixelY(a) {
return a;
}
}
var obj = new s();
obj.funcs.addF('x*3 + 5');
console.log(obj.funcs[0](1)); // 8
Here you have a full example with canvas:
function s() {
this.funcs = [];
this.funcs.addF = function (str) {
var f = new Function('x', 'return ' + str);
this.push(function(pixelX) {
var x = getCoordX(pixelX);
return getPixelY(f(x));
});
};
this.drawGraph = function(c) {
c.lineWidth = 2;
for(var u = 0; u < this.funcs.length; u++) {
var f = this.funcs[u];
c.beginPath();
c.moveTo(0, 200-f(0));
for(var x=1; x<400; ++x) c.lineTo(x, 200-f(x));
c.stroke();
}
};
function getCoordX(a) {
return a;
}
function getPixelY(a) {
return a;
}
}
var canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 200;
document.body.appendChild(canvas);
var c = new s("canvas");
c.funcs.addF(".5*x");
c.funcs.addF("x + 50");
c.funcs.addF("3*x + 100");
c.drawGraph(canvas.getContext('2d'));

Related

JavaScript - Uncaught ReferenceError: checkAnswer is not defined, function not in the scope?

class Pairs extends Screen {
constructor() {
super();
var pair1 = null;
var nPair1;
var solution;
var rightCounter = 0;
var licao, nS;
}
pairScreen(screen, lesson, nScreen) {
var body = document.body
var nodes = xmlDoc.getElementsByTagName("PAIRS");
this.licao = lesson;
this.nS = nScreen;
this.solution = screen.getElementsByTagName("SOLUTION")[0].textContent.split(" ");
body.innerHTML = '';
Startup.h1(body, "Babel (" + languageName + ")");
Startup.hr(body);
var d = DynamicHTML.div(body, "border:3px solid black; display:table; padding:20px; margin-left:40px");
Startup.h1(d, "Match the pairs");
var p1 = Startup.p(d, "padding-left:40px; word-spacing:50px;");
Startup.text(p1, 16, " ");
Startup.text(p1, 32, " ");
var p2 = Startup.p(d, "padding-left:20px;");
var button;
var i;
var f = function(i) {
Startup.eventHandler2()
}
var original = screen.getElementsByTagName("ORIGINAL")[0].textContent;
var buttons = original.split(" ");
for (i = 0; i < buttons.length; i++) {
button = DynamicHTML.inpuButton(p1, i, buttons[i], "orangered");
Startup.eventHandler2(document.getElementById(i), "onclick", function() {
checkAnswer(buttons[i], i)
});
}
Startup.hr(body);
}
checkAnswer(pair, nPair) {
var index;
if (pair1 = null) {
pair1 = pair;
nPair1 = nPair;
} else {
for (index = 0; index < solution.length; index++) {
if (pair1 == solution[index]) {
if (index % 2 == 0 && solution[index - 1] == pair) {
DynamicHTML.play("general/right_answer.mp3");
rightCounter = rightCounter + 2;
document.getElementById(nPair).disabled = true;
document.getElementById(nPair1).disabled = true;
pair1 = null;
nPair1 = null;
} else if (solution[index + 1] == pair) {
DynamicHTML.play("general/right_answer.mp3");
rightCounter = rightCounter + 2;
document.getElementById(nPair).disabled = true;
document.getElementById(nPair1).disabled = true;
pair1 = null;
nPair1 = null;
} else {
DynamicHTML.play("general/wrong_answer.mp3");
pair1 = null;
nPair1 = null;
}
}
}
}
if (rightCounter == solution.length) {
if (xmlDoc.getElementsByTagName("LESSON")[licao].childNodes[nS + 2] != null) {
var fs = new Screen();
fs.functionScreen(licao, nS + 2);
} else fs.initialScreen();
}
}
}
Wrote this for a JavaScript project I'm working on but when I run it It says Uncaught ReferenceError: checkAnswer is not defined, I'd really appreciate if anyone knew the problem. Thank you!
P.S. Don't know if the checkAnswer function has bugs or note becausa I couldn't test it out, I will when I can run it :)
First you need to bind this at the end of this function:
Startup.eventHandler2(document.getElementById(i), "onclick", function() {
this.checkAnswer(buttons[i], i)
}.bind(this));
Basically this tells the anonymous function "hey I want this to refer to this outer class, not the function itself".
(edited)
You need to either bind checkAnswer in the constructor like-so:
class Pairs extends Screen {
constructor() {
super();
var pair1 = null;
var nPair1;
var solution;
var rightCounter = 0;
var licao, nS;
this.checkAnswer = this.checkAnswer.bind(this);
}
Or use and arrow function which gives you the exact reference to the this checkAnswer like-so:
checkAnswer = (pair, nPair) => {
//your code goes here ...
}
This way you will be able to call this.checkAnswer inside the scope of your pairScreen function or wherever you want to call the function like #Davelunny point out

How to get variable data by id

Recently i have used code in project that is still working it it http://kreditka.testovi-site.pw. But when i tried to reuse javascript the default rules js got broken and i got an error in console explaining that varibale cannot be found. Is it somehow related that sme o the code is not resusable and each line should be rewritten one by one for defferent projects?
(function () {
console.log("()");
"use strict";
var polosa = document.querySelector("#polosa");
alert(polosa);
var x = document.querySelectorAll("#slide1");
var o;
for (o = 0; o < x.length; o++) {
x[o].style.left = o*300+"px";
}
var x = document.querySelectorAll("#slide");
var a;
for (a = 0; a < x.length; a++) {
x[a].style.left = a*300+"px";
}
var doc = document.querySelectorAll('.btn-click');
for (var i = 0; i < doc.length; i++) {
doc[i].addEventListener("click", highlightThis, true);
}
var doc1 = document.querySelectorAll('.btn1-click');
for (var j = 0; j < doc.length; j++) {
doc1[j].addEventListener("click", highlightThis1, true);
}
var polosa = document.querySelector('#polosa');
polosa.style.left = '0px';
var left = 0;
var sliderItem = document.querySelector('#slide');
function highlightThis(event) {
console.log("highlightThis");
var currentActiveButton = document.querySelector(".btn-click.btn-slide-active");
currentActiveButton.classList.toggle("btn-slide-active");
event.target.classList.toggle("btn-slide-active");
console.log(event.target.dataset.value);
shiftSlide({
prev: currentActiveButton.dataset.value,
next: event.target.dataset.value
});
}
function highlightThis1(event) {
console.log("highlightThis1");
var currentActiveButton1 = document.querySelector(".btn1-click.btn-slide-active");
currentActiveButton1.classList.toggle("btn-slide-active");
event.target.classList.toggle("btn-slide-active");
console.log(event.target.dataset.value);
shiftSlide1({
prev: currentActiveButton1.dataset.value,
next: event.target.dataset.value
});
}
function shiftSlide(obj) {
var polosa = document.querySelector('#polosa');
console.log("shiftSlide");
var currentSliderPosition = parseInt(polosa.style.left);
if (obj.prev === obj.next) {
return
} else {
var left = (obj.prev - obj.next) * 300;
polosa.style.left = currentSliderPosition + left + "px";
}
}
function shiftSlide1(obj) {
var polosa = document.querySelector('#polosa1');
console.log("shiftSlide");
var currentSliderPosition = parseInt(polosa1.style.left);
if (obj.prev === obj.next) {
return
} else {
var left = (obj.prev - obj.next) * 300;
polosa1.style.left = currentSliderPosition + left + "px";
}
}
})();

I want to draw multiple image, but it works only for last image

This function is make "cards" array in target object, and I added some codes to draw each element in "cards" array(see the mark : this part), but it doesn't work. How can i do?
var player = {
cards = [];
};
function giveNCards(cardsArr, target, n) {
target.cards = [];
for (var i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
/////////// this part //////////
for (var i = 0; i < target.cards.length; i++) {
var cardImage = new Image();
cardImage.onload = (function(value) {
return function() {
ctx.drawImage(this, i * 100, 0);
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
//////////////////////////////////////////
console.log(target.cards);
return target.cards;
}
cardImage is getting overwritten on each loop.
Variables (when defined with the 'var' keyword) are local to the function (function scope) and get 'hoisted' (moved to the top). What does that mean? Basically, your code will run like this:
function giveNCards(cardsArr, target, n) {
var i, cardImage // <-- cardImage defined first here (hoisted)
target.cards = [];
for (i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
for (i = 0; i < target.cards.length; i++ ){
cardImage = new Image();
cardImage.onload = (function(value){
return function(){
ctx.drawImage(this, i * 100, 0);
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
console.log(target.cards);
return target.cards;
}
The simplest fix for this (though there are others) is to move the contents of your second for loop to another function which will give it it's own scope.
function giveNCards(cardsArr, target, n) {
var i
target.cards = [];
for (i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
for (i = 0; i < target.cards.length; i++ ){
renderCard(target, i)
}
console.log(target.cards);
return target.cards;
}
function renderCard(target, i) {
var cardImage = new Image();
cardImage.onload = (function(value){
return function(){
ctx.drawImage(this, i * 100, 0);
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
Pay attention, the i value that you used inside the IFFE is from the global scope, therefore you always modifies the same value
var player = {
cards = [];
};
function giveNCards(cardsArr, target, n) {
target.cards = [];
for (var i = 0; i < n; i++) {
target.cards.push(cardsArr.pop());
}
/////////// this part //////////
for ( var i = 0; i < target.cards.length; i++ ){
var cardImage = new Image();
cardImage.onload = (function(value){
return function(){
ctx.drawImage(this, value * 100, 0);
// -------------------^
}
})(i);
cardImage.src = "./images/" + target.cards[i] + ".png"
}
//////////////////////////////////////////
console.log(target.cards);
return target.cards;
}

Can't Ask object property twice in Javascript

Forgive me for what might be terrible javascript code. This is my first time trying something in javascript...
function Tile (window_id, size)
{
this.window_id = window_id;
this.size = size;
};
function Desktop ()
{
this.tiles = [];
this.ntiles = function () {return this.tiles.length;};
this.size = function ()
{
var sum = 0;
for (i = 0; i < this.ntiles(); i++) {sum += this.tiles[i].size;};
return sum;
};
this.addTile = function (tile)
{
if (this.size() === 1) {return -1;};
this.tiles.push(tile);
return 0;
};
};
function Layer ()
{
this.desktops = [];
this.ndesktops = function () {return this.desktops.length;};
this.addDesktop = function (desktop)
{
this.desktops.push(desktop);
return 0;
};
this.availableDesktopSize = function (size)
{
for (i = 0; i < this.ndesktops(); i++)
{
print(this.desktops[i].size());
print('hi');
print(this.desktops[i].size());
print('hihi');
var space = 1.0 - this.desktops[i].size();
print('hihihi');
print(space);
print(size);
if (space >= size) {return i;};
};
return -1;
};
};
var layer = new Layer();
var desktop1 = new Desktop();
var desktop2 = new Desktop();
var tile = new Tile(100, 0.5);
desktop1.addTile(tile);
desktop1.addTile(tile);
desktop2.addTile(tile);
layer.addDesktop(desktop1);
layer.addDesktop(desktop2);
print(layer.availableDesktopSize(0.51));
print(layer.availableDesktopSize(0.49));
I'm trying to make a method for the Layer class that finds the first desktop that has enough space left. In trying to achieve this, while trying my code, I observed that for some reason when I call the desktop.size() property I get the right value back the first time but when I call it a second time my script dies. This is the output
1
hi
TypeError: Result of expression 'this.desktops[i]' [undefined] is not an object.
So it does the first print fine but why can't it do the exact same function another time?
(If you have any other advice to improve my code, that would be very helpfull)
The problem is that you are using a global i variable in several loops.
You call a method from within such a loop, and that method has its own loop giving a different value to the same i variable. So when you come back from that call i no longer is the same as before.
Solution: declare your variables as local variables.
for (let i = 0; // ...etc
// ^^^
Use var to declare your iterator variable i to bind it to the scope. Now you're using a global scoped i that is causing trouble.
However in modern browsers let would be better because that binds the variable to the block scope. The current {} preventing the value of that variable to be used outside of that block.
function Tile (window_id, size)
{
this.window_id = window_id;
this.size = size;
};
function Desktop ()
{
this.tiles = [];
this.ntiles = function () {return this.tiles.length;};
this.size = function ()
{
var sum = 0;
for (var i = 0; i < this.ntiles(); i++) {sum += this.tiles[i].size;}; //var i binds i to this function scope.
return sum;
};
this.addTile = function (tile)
{
if (this.size() === 1) {return -1;};
this.tiles.push(tile);
return 0;
};
};
function Layer ()
{
this.desktops = [];
this.ndesktops = function () {return this.desktops.length;};
this.addDesktop = function (desktop)
{
this.desktops.push(desktop);
return 0;
};
this.availableDesktopSize = function (size)
{
for (var i = 0; i < this.ndesktops(); i++) //var i binds i to this function scope.
{
console.log(this.desktops[i].size());
console.log('hi');
console.log(this.desktops[i].size());
console.log('hihi');
var space = 1.0 - this.desktops[i].size();
console.log('hihihi');
console.log(space);
console.log(size);
if (space >= size) {return i;};
};
return -1;
};
};
var layer = new Layer();
var desktop1 = new Desktop();
var desktop2 = new Desktop();
var tile = new Tile(100, 0.5);
desktop1.addTile(tile);
desktop1.addTile(tile);
desktop2.addTile(tile);
layer.addDesktop(desktop1);
layer.addDesktop(desktop2);
console.log(layer.availableDesktopSize(0.51));
console.log(layer.availableDesktopSize(0.49));
I'm not positive but it seems to be the way i is assigned in your for loops.
in this bit of code
print(this.desktops[i].size());
print('hi');
print(this.desktops[i].size());
i is 0 but then is set to 2 in the line below
for (i = 0; i < this.ntiles(); i++) {sum += this.tiles[i].size;};
function Tile (window_id, size)
{
this.window_id = window_id;
this.size = size;
};
function Desktop ()
{
this.tiles = [];
this.ntiles = function () {return this.tiles.length;};
this.size = function ()
{
var sum = 0;
for (i = 0; i < this.ntiles(); i++) {sum += this.tiles[i].size;};
return sum;
};
this.addTile = function (tile)
{
if (this.size() === 1) {return -1;};
this.tiles.push(tile);
return 0;
};
};
function Layer ()
{
this.desktops = [];
this.ndesktops = function () {return this.desktops.length;};
this.addDesktop = function (desktop)
{
this.desktops.push(desktop);
return 0;
};
this.availableDesktopSize = function (size)
{
for (i = 0; i < this.ndesktops(); i++)
{
console.log(this.desktops[i].size());
console.log(i)
print('hi');
print(this.desktops[i].size());
print('hihi');
var space = 1.0 - this.desktops[i].size();
print('hihihi');
print(space);
print(size);
if (space >= size) {return i;};
};
return -1;
};
};
var layer = new Layer();
var desktop1 = new Desktop();
var desktop2 = new Desktop();
var tile = new Tile(100, 0.5);
desktop1.addTile(tile);
desktop1.addTile(tile);
desktop2.addTile(tile);
layer.addDesktop(desktop1);
layer.addDesktop(desktop2);
console.log(layer.availableDesktopSize(0.51));
console.log(layer.availableDesktopSize(0.49));

Transmitting a value from one function to another

This issue is asked already some times. But in the application of google apps script i can't solve this problem.
function two ()
{
var bridgeclubs = SpreadsheetApp.openById("1dfyI1jbz..........TVg4OoixKTz1");
var bridgeclubs_sheet = bridgeclubs.getSheetByName("Sheet1");
var data_bridgeclubs = bridgeclubs_sheet.getDataRange().getValues();
var numRows = bridgeclubs_sheet.getDataRange().getNumRows();
var sprshtname = SpreadsheetApp.getActiveSpreadsheet().getName();
for (var i=1; i<=numRows; i++)
{
if (sprshtname == data_bridgeclubs[i][6])
{
var A = i;
break;
}
}
}
In function one I do a call to function two, where I need this value A:
function one ()
{
two ();
//here I need this value A;
var C= 35 * A;for instance
}
Who can help me?
You can return value from function using return i; and obtain it in within one by var A = two();.
function two() {
var bridgeclubs = SpreadsheetApp.openById("1dfyI1jbz..........TVg4OoixKTz1");
var bridgeclubs_sheet = bridgeclubs.getSheetByName("Sheet1");
var data_bridgeclubs = bridgeclubs_sheet.getDataRange().getValues();
var numRows = bridgeclubs_sheet.getDataRange().getNumRows();
var sprshtname = SpreadsheetApp.getActiveSpreadsheet().getName();
for (var i=1; i<=numRows; i++) {
if (sprshtname == data_bridgeclubs[i][6]) {
return i;
}
}
}
function one() {
var C;
var A = two();
if (A) {
C = 35 * A;
}
}
You can use global variables.
var A;
function two() {
var bridgeclubs = SpreadsheetApp.openById("1dfyI1jbz..........TVg4OoixKTz1");
var bridgeclubs_sheet = bridgeclubs.getSheetByName("Sheet1");
var data_bridgeclubs = bridgeclubs_sheet.getDataRange().getValues();
var numRows = bridgeclubs_sheet.getDataRange().getNumRows();
var sprshtname = SpreadsheetApp.getActiveSpreadsheet().getName();
for (var i=1; i<=numRows; i++) {
if (sprshtname == data_bridgeclubs[i][6]) {
A = i;
break;
}
}
}
function one() {
two();
var C= 35 * A;
}

Categories