-2

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();

3 Answers3

1

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);
    });
}
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Isn't it like that, that he uses `Function` and not `function`, which would imply that his function would be run in an isolated scope where `getCoordX` or other global references wouldn't be available (except for the fact that the `new` keyword is missing here)? – Icepickle Dec 23 '16 at 18:18
  • I asked about a hour ago here http://stackoverflow.com/questions/41304983/using-array-push-inside-my-own-array-method-gives-error-javascript, but this is a new case. Those two answers were for part 1 –  Dec 23 '16 at 18:19
  • @Icepickle Could you pls tell me what I should do to make those two functions accessible? –  Dec 23 '16 at 18:27
  • 1
    @AndiL.H. Yeah sure, here is a [fiddle](https://jsfiddle.net/Icepickle/r05m81yh/) that kind of does what you want (though, i'm very unsure what exactly you wish to do with your code :)) – Icepickle Dec 23 '16 at 18:31
  • @Icepickle Hi, great example. I didn't write new Function() bc I found some guy who didn't use the `new` keyword and I thought it's not needed. I am trying to make something like Desmos and those two functions help me to get the pixelY for every pixelX on the canvas to make the graph, that's why I had to make a function out of a string. I want to thank you a lot. Is there any other way to make it without having to add the two functions as arguments? –  Dec 23 '16 at 18:43
  • @Icepickle Thanks a lot. I did something based on your idea and it worked even better than expected –  Dec 23 '16 at 20:38
1

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 ;)

  • 1
    I didn't think about it but it actually did work. `var currvalue = f(i, getCoordX, getPixelY); var lastvalue = f(i-1, getCoordX, getPixelY);` –  Dec 26 '16 at 10:39
-1

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'));
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • I can't avoid it. I can't use eval in this case because I'm am drawing graphs in a canvas the size of the monitor and there would be a lot of lag. I thought of it before, and I have seen the result –  Dec 23 '16 at 18:51
  • Maybe you can move the `eval` outside the inner function? Can you include in your question an example of how are you using this? – Oriol Dec 23 '16 at 18:54
  • I am making a thing like desmos. The user will input something like 2*x+5 and that is the string that is going to come into `str`. I tried to make it with eval() by replacing x with values from the graph but it lags just too much. –  Dec 23 '16 at 18:58
  • OK, then I think you will need to make the functions global. – Oriol Dec 23 '16 at 19:01
  • You're assuming that `str` is something that needs to be `eval`'d in one way or another, but I'm suspecting that is not the case, and that he needs to use neither `new Function` nor `eval`. –  Dec 23 '16 at 19:14
  • @torazaburo See the comment above, the objective is drawing graphs of math expressions. Using `eval` or `new Function` you don't need a math parser. – Oriol Dec 23 '16 at 19:20
  • I tried your first suggestion before but it didn't work Oriol –  Dec 23 '16 at 19:22