How to partially apply member functions in JavaScript? - javascript

I currently have a partial-application function which looks like this:
Function.prototype.curry = function()
{
var args = [];
for(var i = 0; i < arguments.length; ++i)
args.push(arguments[i]);
return function()
{
for(var i = 0; i < arguments.length; ++i)
args.push(arguments[i]);
this.apply(window, args);
}.bind(this);
}
The problem is that it only works for non-member functions, for instance:
function foo(x, y)
{
alert(x + y);
}
var bar = foo.curry(1);
bar(2); // alerts "3"
How can I rephrase the curry function to be applied to member functions, as in:
function Foo()
{
this.z = 0;
this.out = function(x, y)
{
alert(x + y + this.z);
}
}
var bar = new Foo;
bar.z = 3;
var foobar = bar.out.curry(1);
foobar(2); // should alert 6;

Instead of your curry function just use the bind like:
function Foo()
{
this.z = 0;
this.out = function(x, y)
{
alert(x + y + this.z);
}
}
var bar = new Foo;
bar.z = 3;
//var foobar = bar.out.curry(1);
var foobar = bar.out.bind(bar, 1);
foobar(2); // should alert 6;

You're close. this.z inside of this.out references the this scoped to the function itself, not the Foo() function. If you want it to reference that, you need to store a variable to capture it.
var Foo = function() {
this.z = 0;
var self = this;
this.out = function(x, y) {
alert(x + y + self.z);
};
};
http://jsfiddle.net/hB8AK/

Related

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

Function Call Hierarchy in 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'));

Accessing outer scope from inner scope

I have a type that looks a little something like this:
var x = function(){
this.y = function(){
}
this.z = function(){
...
this.A = function(){
CALLING POINT
}
}
}
From calling point, I'm attempting to call function this.y. I don't need to pass any parameters, but when I set some stuff from this.A, I need to call this.y.
Is this possible? I'm OK with passing extra parameters to functions to make it possible.
Is this possible?
Yes, you can assign this reference to another variable and then call function y on it
this.z = function() {
var self = this;
this.A = function() {
self.y();
}
}
Version with bind, basically this adds a new method a to the object.
var X = function () {
this.y = function () {
document.write('y<br>');
}
this.z = function () {
document.write('z<br>');
this.a = function () {
document.write('a<br>');
this.y();
}
}.bind(this);
};
var x = new X;
//x.a(); // does not exist
x.z(); // z
x.a(); // a y
Working example with saved inner this.
var X = function () {
var that = this; // <--
this.y = function () {
document.write('y<br>');
}
this.Z = function () {
document.write('Z<br>');
this.a = function () {
document.write('a<br>');
that.y();
}
}
}
var x = new X,
z = new x.Z; // Z
z.a(); // a y
Instead of function() you can try modern JavaScript or Typescript ()=>. I also like .bind(this).
You cannot because this.y() is not within the scope that this.A() is in. You can if you set this.y() to a global function y:
var y = function() {};
var x = function() {
this.y = y;
this.z = function() {
...
this.A = function() {
this.y(); // will be successful in executing because this.y is set to the y function.
};
}
};

IFFE vs. function that returns a function

Consider the following:
var x = (function(){
var _private = 'start';
var _x = function(text){
if(text){
_private = text;
}
else{
return _private;
}
}
return _x;
})();
console.log(x()); //start
x('end');
console.log(x()); //end
var y = function(){
var _private = 'start';
var _y = function(text){
if(text){
_private = text;
}
else{
return _private;
}
}
return _y;
}
console.log(y()); //toString of function
y();//invoked function, should return _y?
y('end')
console.log(y()); //toString of function
I need some clarity as to why the y function does not behave the same as the x function after it has been invoked. Why does the y function behave differently, and what overarching concept about IFFEs am I not getting?
fiddle: http://jsfiddle.net/xmmddcgn/
In the first example:
var x = (function(){
var _private = 'start';
var _x = function(text){
if(text){
_private = text;
}
else{
return _private;
}
}
return _x;
})();
var x is the inner function since the outer function was invoked.
In the second example var y is the outer function after you invoke it like this:
var y = function () {
var _private = 'start';
var _y = function (text) {
if (text) {
_private = text;
} else {
return _private;
}
}
return _y;
}
y(); // nothing happens and nobody keep _y's ref.
var p = y(); //p is _y
console.log(p()); //start
p('end')
console.log(p()); //end
Then p will be exactly the var x above.

Anonymous self-invoking JavaScript function - returning multiple objects

If I have original function (as an example):
var x = function() { alert('tadaaa'); return 1; }
var y = function() { alert('tadaaa'); return 1; }
and I've gone ahead and made this into a self-invoking anonymous JS function, as such:
(function() {
var x = function() { alert('tadaaa'); return 1; }
var y = function() { alert('tadaaa'); return 1; }
})()
am I doing something paradoxical? I'd like to access x and y as global variables, but the self-invoking anonymous function is useful in other areas that I'm not going into detail right now - I just want to keep it.
Should I be doing something like:
var x= (function() {
var x = function() { alert('tadaaa'); return 1; }
var y = function() { alert('tadaaa'); return 1; }
// Should I be doing something like
return x
})()
or
var x= (function() {
var x = function() { alert('tadaaa'); return 1; }
return x
})()
var y = (function() {
var x = function() { alert('tadaaa'); return 1; }
return y
})()
seems somewhat redundant?
I'm not sure what the goal of al this is, but maybe you could return both functions in an object, like so:
var funcs = (function() {
var x = function() { alert('tadaaa'); return 1; };
var y = function() { alert('tadaaa'); return 1; };
return {x: x, y: y};
})();
funcs.x();
funcs.y();
This is basically what the Module Pattern is about (see for example http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth).
It's good! Depending on what you need, of course.
You can:
var x, y;
(function() {
x = function() { alert('tadaaa'); return 1; }
y = function() { alert('tadaaa'); return 1; }
})();

Categories