I'm building a game and was trying to figure out the workings of the melonJS game engine I am using. I'm at a point where I need to create my own version of their CanvasRender object, so was trying to understand what they've done.
Why wrap the canvas renderer in an anonymous, self-invoking function? Can't I just create the function as such?
me.CanvasRenderer = me.CanvasRenderer || {};
me.CanvasRenderer = {
init: function() {
//...
Or even within a namespace:
var CanvasStuff = {
CanvasRenderer : function() {
}
};
CanvasStuff.CanvasRenderer();
I don't understand how they've laid out this code and the purpose. How and when is (function () { invoked?
Here's a snippet of their code: (link to full code)
(function () {
// The canvas renderer object
me.CanvasRenderer = (function () {
var api = {},
var canvas = null,
//...
api.init = function () {
//...
};
return api;
//...
})();
})();
The self-invoking function is executed immediately, so it is similar to the alternatives you posted.
However, the advantage of the self-invoking function is that you can declare variables within that scope that are not accessible outside the closure. For example, in the code snippet you posted there is a var canvas that is accessible within any of the api functions, but not accessible to any outside code. In the first alternative you suggested, there is no way to declare canvas without it being accessible to any clients/callers of the api. Your second alternative (the namespace) isn't filled out enough to see how it would be used, so you might be able to wrap private vars into that closure as well.
Related
I'm trying to run trusted JS code in an "isolated" context.
Basically came up with this method:
function limitedEval(src, context) {
return (function() {
with(this) {
return eval(src)
}
}).call(context)
}
This works great, however when the script is using the var keyword it is stored in the execution context as opposed to the provided context in the with statement (which I understand is by design). So for example, the following code doesn't work:
var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo)', ctx); // error: foo is undefined
I'd like to be able to call limitedEval() multiple times and reuse the context. Is that possible?
This seems like a very interesting problem. The problem with your code is that you generate new function every time you execute limitedEval. This means that whatever variables you create using var keyword, will only exist within the context of that function. What you really need is to have 1 function per context and reuse that function's context. The most obvious way to do that is by using generators. Generators are essentially functions that can be paused and then restarted.
// IIFE to store gen_name
var limitedEval = function() {
// Symbol for generator property so we don't pollute `ctx` namespace
var gen_name = Symbol();
return function limitedEval(src, context) {
if(!(gen_name in context)) {
// Generator that will run eval and pause til getting the next source code
var generator = function * () {
with(this) {
while(true) {
yield eval( yield );
}
}
};
context[gen_name] = generator.call(context);
// Initially, we need to execute code until reaching the first `yield`
context[gen_name].next();
}
// First, send the source to the `eval`
context[gen_name].next( src );
// Second, get the `eval` result
return context[gen_name].next().value;
};
}();
And now you can call
var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo);', ctx);
Every limitedEval call will now reuse whatever generator function it will find on the provided ctx object. If the function doesn't exist, it will create it and put it on the ctx object. Because you now have only one function per ctx, it will reuse the function's context when creating variables with var keyword. Note: you will not be able to look up those variables via the ctx object, because they will only exist within the function's context.
I'm not entirely sure if you can achieve the same result without using generators.
Edit: others made great suggestions so I replaced randomly generated property with Symbol and did with(this) instead of with(context)
I have a couple of variables in my application that I have to use in most of my closures, like variables holding preloaded requests, or variables holding the current state of application (that need to be changed dynamically in different places).
So my application structure looks like this:
(function() {
var MainModule = (function () {
})();
var Utils = (function () {
})();
var Events = (function () {
})();
})();
And I create these variable inside MainModule, but want to change them, remove them, etc, inside Utils and Events. I've been thinking about two ways:
Creating Context closure that keeps an array of those variables, and have get() and set() access methods.
Passing these variables to closures as arguments, but I coulnd't unset them and I'd have difficulties because of the way javascript passes arrays/objects to functions.
How should I handle it?
(function() {
var MainModule = (function () {
var mine;
return {
getMine: function(){ return mine; },
setMine: function(a){ mine = a; }
}
})();
var Utils = (function () {
return function(module){
module.getMine();
}
})();
var Events = (function () {
})();
})();
IIFE does not give anything to put into the MainModule variable so I'm assuming something else will be calling something within Utils. get/set is the safest way to give access to private variables. Like the comments said, IIFE should only be used when needed, otherwise it's just pointless abstraction.
Here I have a p5 object that I am exporting to be bundled by browserify:
var p5 = require('p5')
var colorPicker = require('./color_picker.js')
module.exports = new p5(function () {
this.setup = function setup () {
this.createCanvas(700, 400)
this.background(205)
this.loadImage('/uploads/uploaded_image', function (img) {
image(img, 0, 0)
})
this.updatePixels()
}
this.clearCanvas = function redraw () {
this.background('black')
}
this.mouseDragged = function mouseDragged () {
var rgb = colorPicker.getRGB()
this.stroke(rgb.r, rgb.g, rgb.b)
this.strokeWeight(10)
this.line(this.pmouseX, this.pmouseY, this.mouseX, this.mouseY)
}
})
All of this works fine and I can access all built in p5 functions in other bundled scripts but not the clearCanvas function that I have defined. I also tried attaching it to the window object based on another SO post, like this:
window.this.clearCanvas = function redraw(){
//code
}
Everything so far has yielded Uncaught TypeError: Cannot set property 'clearCanvas' of undefined
Any idea what I'm doing wrong?
The modules build by browserify have their own scope, so nothing is exposed to the window object per default. You explicitly need to append your stuff to the window object to access it from a browser.
var p5 = require('p5')
var colorPicker = require('./color_picker.js')
module.exports = new p5(function () {
// ...
this.clearCanvas = function redraw () {
this.background('black')
}
// ...
window.clearCanvas = this.clearCanvas.bind(this);
});
First, for the section:
window.this.clearCanvas = function redraw(){
//code
}
To attach something to the window object do it directly,changing it to this:
window.clearCanvas = function redraw(){
//code
}
Worked, however I wanted to attach to the window object as infrequently as possible. For p5.js this section in the documentation is important:
By default, all p5.js functions are in the global namespace (i.e. bound to the window object), meaning you can call them simply ellipse(), fill(), etc. However, this might be inconvenient if you are mixing with other JS libraries or writing long programs of your own. To solve this problem, there is something we call "instance mode", where all p5 functions are bound up in a single variable instead of polluting your global namespace.
https://github.com/processing/p5.js/wiki/p5.js-overview
Running p5.js in instance mode allowed me to use the clearCanvas function without binding it to the window object.
Kind of basic I guess. I'm writing on a modular application and am often running into having to do something like this:
var foo = {};
foo.do_something = function () {
//...
};
foo.do_something_else = function () {
// ...
};
foo.do_all = function () {
var x = foo.do_something();
// ...
};
I prefer to stick with functional programming like this.
Question:
Is it safe to reference methods declared on foo inside the declaration of other methods? Any better idea on how to do this?
Thanks!
That is fine.
You can also use this keyword, that refers to the specific instance. Be careful with this, because during execution the scope can change (because for example you invoke a method of an other object...).
To avoid it a good practice is to set in the first row of the method the assignment var self=this and then you can always use self for referencing the object instance.
You could use the module approach to accomplish the same separation of functionality, but without having to worry about constantly referencing a global name.
var foo = (function() {
var do_something = function () {
//...
};
var do_something_else = function () {
// ...
};
var do_all = function () {
// Note: can refer to `do_something` directly.
var x = do_something();
// ...
};
return {
do_something: do_something,
do_something_else: do_something_else,
do_all: do_all
};
})();
In general, you can use an IIFE (immediately-invoked function expression) to create a local, private scope in which you can define whatever you need to without having to worry about polluting global scope, and export functions or objects from within as needed.
I'm currently trying to implement some common JS concepts
in little projects to understand better how to use them.
I've been working on a simple game, trying to
understand and use the module pattern and closures.
I'm using the module pattern from Stoyan Stefanov's 'patterns'
book.
I'm struggling to understand how best to mix modules and
closures.
I'd like to know if I'm organising the following code in a
sensible way? If so, my question is: what's the best way
to modify the code so that in the $(function(){}) I have
access to the update() function?
MYAPP.utilities = (function() {
return {
fn1: function(lives) {
//do stuff
}
}
})();
MYAPP.game = (function() {
//dependencies
utils = MYAPP.utilities
return {
startGame: function() {
//initialisation code
//game state, stored in closure
var lives = 3;
var victoryPoints = 0;
function update(){
utils.fn1(lives);
//do other stuff
}
}
}
})();
$(function(){
MYAPP.game.startGame();
//Want to do this, but it won't work
//because I don't have access to update
$('#button').on('click',MYAPP.game.update)
});
I've come up with a couple of options which would work, but
I'd like to know if they're good practice, and what the best
option is.
Options:
(1) Bind $('#button').on('click', ...) as part of the
startGame initialisation code.
(2) Assign the update() function to a variable, and
return this variable from the startGame function, So in
$(function(){}) we could have
updatefn = MYAPP.game.startGame(); and then
$('#button').on('click',MYAPP.game.update)
(3)? Is there a better way?
Thank you very much for any help,
Robin
First off, to access the update function in that fashion it will have to exposed in the returned object.
return {
update: function() {
[...]
},
startGame: function() {
[...]
this.update();
}
}
Calling obj.method() automatically sets the this reference inside this method call to obj. That is, calling MYAPP.game.startGame() sets this to MYAPP.game inside this startGame method call. More details about this behavior here.
You will also want to move the lives variable to a common scope which is accessible by both startGame and update methods, which is exactly what the closure is for:
MYAPP.game = (function() {
[...]
var lives; //private/privileged var inside the closure, only accessible by
//the returned object's function properties
return {
update: function() {
utils.fn1(lives);
},
startGame: function() {
[...]
lives = 3; //sets the closure scope's lives variable
[...]
this.update();
}
}
})();
Fiddle
In this case you will need some method to set the lives variable when you want to change it. Another way would be to make the lives variable public as well by making it a property of the returned object and accessing it through this.lives inside of the methods.
NOTE: If you simply pass a reference to the function object stored as property of the returned object as in:
$('#button').on('click', MYAPP.game.update);
The this reference inside the click handler will not point to MYAPP.game as the function reference that has been passed will be called directly from the jQuery core instead of as an object's member function call - in this case, this would point to the #button element as jQuery event handlers set the this reference to the element that triggered the handler, as you can see here.
To remedy that you can use Function.bind():
$('#button').on('click', MYAPP.game.update.bind(MYAPP.game));
Or the old function wrapper trick:
$('#button').on('click', function() {
MYAPP.game.update(); //called as method of an obj, sets `this` to MYAPP.game
});
This is important when the this keyword is used inside the update method.
There are a few issues in your code. First, update() function is not visible outside the object your creating on the fly. To make it part of game object it has to be on the same level as startGame.
Also, if you declare var lives = 3 it will be a local variable and it won't be visible outside startGame() function, as well as victoryPoints. These two variable have to be visible in some way (via closure or as object fields).
Finally, attaching MYAPP.game.update as an event listener will attach just that function, preventing you from using all other object methods/functions. Depending on what you want to do you might prefer to pass a closure like function() { MYAPP.game.update() } instead.
Your code should look something like:
MYAPP.utilities = (function() {
return {
fn1: function(lives) {
console.log(lives);
}
}
})();
MYAPP.game = (function() {
//dependencies
utils = MYAPP.utilities
var lives;
var victoryPoints;
return {
startGame: function() {
//initialisation code
//game state, stored in closure
lives = 3;
victoryPoints = 0;
},
update: function() {
utils.fn1(lives);
//do other stuff
}
}
})();
$(function(){
MYAPP.game.startGame();
//Want to do this, but it won't work
//because I don't have access to update
$('#button').on('click', MYAPP.game.update)
});
(DEMO on jsfiddle)