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.
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'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.
I'm looking to encapsulate my javascript inside a namespace like this:
MySpace = {
SomeGlobal : 1,
A: function () { ... },
B: function () { ....; MySpace.A(); .... },
C: function () { MySpace.SomeGlobal = 2;.... }
}
Now imagine that instead of a few lines of code, I have about 12K lines of javascript with hundreds of functions and about 60 globals. I already know how to convert my code into a namespace but I'm wondering if there's a quicker way of doing it than going down 12K lines of code and adding MySpace. all over the place.
Please let me know if there's a faster way of doing this.
Thanks for your suggestions.
I like to wrap up the namespace like so. The flexibility is huge, and we can even separate different modules of the MySpace namespace in separate wrappers if we wanted too. You will still have to add some sort of _self. reference infront of everything, but at least this way you can change the entire name of the namespace very quickly if need be.
You can see how with this method you can even call _self.anotherFunc() from the 1st module, and you'll get to the second one.
(function (MySpace, $, undefined) {
var _self = MySpace; // create a self-reference
_self.test = function () {
alert('we got here!');
_self.anotherFunc(); // testing to see if we can get the 2nd module
};
_self = MySpace; // reassign everything just incase
}(window.MySpace = window.MySpace || {}, jQuery));
$(function () {
MySpace.test(); // call module 1
MySpace.callOtherModule(); // call module 2
});
// Here we will create a seperate Module to the MySpace namespace
(function (MySpace, $, undefined) {
var _self = MySpace; // create a self-reference
_self.callOtherModule = function () {
alert('we called the 2nd module!');
};
_self.anotherFunc = function () {
alert('We got to anotherFunc from the first module, even by using _self.anotherFunc()!');
};
_self = MySpace; // reassign everything just incase
}(window.MySpace = window.MySpace || {}, jQuery));
jsFiddle DEMO
Wrap a function body around your existing code to use as scope, hiding everything from global - this will allow you to do internal calls without pasting Namespace. prefix everywhere, neatly hide things you don't want everyone else to see, and will require minimal changes as well.
After that, decide what functions you want to "export" for everyone and assign them to properties of object you want to use as "namespace".
I'm still kinda new to Javascript and noticed in lot of places in my code I am using JQuery to get a reference to my canvas element like this:
$('#myCanvas')[0].getContext('2d');
I have my suspicions that it is slowing my site down because I have it in a lot of places that may be called quite often. Ideally, I'd just do this once and access the context from any javascript page.
I tried to make a global variable but it didn't see to work (probably because it runs before the page can load) so instead I put this function in global scope in my first referenced javascript file:
var drawingCanvasContext;
function getDrawingCanvas() {
if (drawingCanvasContext == null) {
drawingCanvasContext = $('#myCanvas')[0].getContext('2d');
}
return drawingCanvasContext;
}
So then whenever I need the canvas in my code I just call that method.. But it just seems rather.. messy. I doubt this is an uncommon desire so I'm curious of the proper solution. I'd prefer it just to be a variable instead of a function and to be accessed globally without all these null checks. Does anyone know how to accomplish this? Thanks.
You can simply do this
var drawingCanvasContext = $('#myCanvas')[0].getContext('2d');
instead of defining a function for that.
If you're careful about global scope pollution, you should wrap it up in a function or use namespacing. E.g.
var myNamespace = myNamespace || {};
myNamespace.drawingCanvasContext = $('#myCanvas')[0].getContext('2d');
I've got only two little improvements on your code:
(function(){
// move the var out of the global context by wrapping everything in a closure
var /* static */ drawingCanvasContext;
function getDrawingCanvas() {
if (!drawingCanvasContext) // no need to check for null, defaultvalue is undefined
drawingCanvasContext = $('#myCanvas')[0].getContext('2d');
return drawingCanvasContext;
}
window.getDrawingCanvas = getDrawingCanvas;
})();
Of course you could/should put that method in a namespace object, I guess you have more than one of such.
Ultimately all you can do is store the context around to be retrieved afterwards. The problem I can see here is probably an order of operations, the context is being requested before the page is ready, and possibly you're losing scope with your VAR declaration (i think).
;(function($, window, document) {
/* private */ var canvas = null;
/* private */ var context = null;
$(document).ready(function() {
canvas = $("#myCanvas")[0];
context = canvas.getContext('2d');
$('body').trigger('CanvasReady', [canvas, context]); // You can fire an event to bind to with jquery for when the canvas is ready.
});
/* public */ window.getCanvas = function() { return canvas; };
/* public */ window.getCanvasContext = function() { return context; };
// You can also use a namespace here too if you want
// window.Canvas = {};
// window.Canvas.GetContext = ... etc ...
})(jQuery, window, document);
This will aquire the canvas when it's ready, fire an event you can subscribe to for when the canvas has been found and is ready for manipulation.
It will expose 2 functions globally:
getCanvas which will get you the Canvas Instance
getCanvasContext which will get you the Canvas Context
You can adapt this to use a registry pattern if you want so it can aquire & store multiple canvas elements/contexts but this is just a simple pattern to show you.
Note: It returns null if the methods are called before the canvas/document is ready, you should be doing all of your setup code after domReady anyway.
You can define a getter on your global variable that will execute each time you read from that variable (see here for tips on making this backward compatible):
var _drawingCanvasContext;//behind the scenes variable that keeps the value
Object.defineProperty(this, "drawingCanvasContext", {
get: function() {
return _drawingCanvasContext || (_drawingCanvasContext = $('#myCanvas')[0].getContext('2d'));
}
});
Here's a fiddle where I set it to 'foo' to show you how it works.
I trying to create a sandbox module that can take a object and prevent that object's code reference to window.
here is how it work in concept.
var sand = function(window) {
var module = {
say: function() {
console.log(window.location);
}
};
return module;
}
sand({}).say(); // window.location is undefine
This doesn't work if the object is pass-in
var $sand = (function(){
return function(obj, context) {
return (function(obj, window) {
window.module = {};
// doesn't work even copy object
for (p in obj) {
window.module[p] = obj[p];
}
console.log(window.location); // undefine
return window.module;
}(obj, context));
};
}());
var module = {
say: function() {
console.log(window.location);
}
};
$sand(module, {}).say(); // still reference to window.location
How can i make this pattern work?
As long as you don't have a variable shadowing window in the scope of your function, the function will be able to access window. Even if you had a variable called window, the code will still be able to access the properties by simply omitting window..
(function(window) {
console.log(window.location); //undefined
console.log(location); //this will still work
})({ });
In other words, sandboxing JavaScript in a browser environment is not possible like this.
In your first example, the only reason window is undefined is because you are passing in an empty object and calling the argument window, so it is hiding the real window.
Also, you can always get access to the window object by hoisting the this variable inside a closure, like so:
console.log ( ( function () { return this; } )() );
So even if you somehow manage to block window, it's trivial to get it back again.
If you define the function outside your sandbox, the context will be the current one, and you can't really do otherwise.
If you really want to do some sandboxing, then you should use iframes to achieve that. Take a look at https://github.com/substack/vm-browserify it is a browser version of the vm module of node, you should be able to extract some good pieces of work, and avoiding eval which is not really clean for what you want to do.