Let me expose my question:
I have a main script, let's say that it creates an instance of a "game" object, which, depending on the actions of the user, loads one of many javascript files, let's call them "levels" :D These files contains different objects, for example, "level1.js" which contains the object for level1, then "level2.js", etc.
Each time a level script is loaded, for example level1.js, the instance of "game" creates an instance of the object level1 and stores it in a local variable.
The only way I've found to do it, is to write, at the end of all the "level" scripts, a global variable, which has always the same name, and that points to the definition of the current level. Then in game, when any level script is loaded, I use this global variable to create the instance of the current level. I would like to know if there is a way to do it without using a global variable.
Here is a simplified example:
In game.js:
function Game() {
var levelCurrent = null;
var scriptCour = document.createElement("script");
scriptCur.type = "text/javascript";
scriptCur.onload = function() {
levelCurrent = new level();
}
}
And in each "level" script (level1.js, level2.js):
function level1() {
//definition of the level
//...
}
level = level1;
Or, similarly:
level = function() {
//definition of the level
//...
}
I don't know if I explained well enough my question, but if anyone has an answer... Thank you! Note that the instance of game is created using a self-executing function, and is therefore a local variable, like:
(function() {
var game = new Game();
})();
Thank you!
Instead of assigning the new level to a global variable, you should call a global function with the level information, e.g.
window.addLevel(function level1() {
//definition of the level
//...
});
Why is your Game instance a local variable, can't it be accessed? I guess it's a singleton (only one instance), so it would be perfectly valid to have it as a global variable. It then would be a good namespace for the addLevel function (window.game.addLevel()).
You could have the main game code expose a single global function, like "startNewLevel" or something, and then instead of declaring a global variable you'd just have the new level pass itself to that function.
Related
I have two javascript files that look like:
// module1.js
var globalFunction = function(val1, val2, callback){
var localVariable1 = val1;
var localVariable2 = val2;
someAsyncFunction(function(){
callback(localVariable1, localVariable2 );
});
}
module.exports = function(val1, val2, callback){
var localVariable1 = val1;
var localVariabl2 = val2;
anotherAsyncFunction( function(){
globalFunction(localVariable1, localVariable2, callback);
});
}
and this:
function MyClass(val1, val2){
this._val1 = val1;
this._val2 = val2;
this._boundFunc = require("./module1.js").bind(this);
}
MyClass.prototype.doSomething = function(callback){
this._boundFunc(callback);
}
Now this incredibly contrieved example binds the module.exports to the class MyClass. What is happening under the hood here? Does each MyClass instance have its own copy of the module.exports function and also will it have it's own copy of the globalFunction as this is referenced in the module.exports?
My concern is that if multiple "doSomething" functions are called synchronously on two difference instances of MyClass then they can interfere and overwrite each others local variables whilst waiting on the asyncFunctions to callback. If the bind copied both the globalFunction and module.exports then I dont think Id have a problem. Thanks
this will help you understand
Does each MyClass instance have its own copy of the module.exports function
no it doesn't. The code in module1.js is run only once and it's module.export is "cached" so whenever you require('./module1') you are getting the same instance of that object
they can interfere and overwrite each others local variables
no they can't. These variables are re-created every time you call the function so every call to the function has its own local variables and don't interfere with one another
My concern is that if multiple functions are called they can interfere and overwrite each others local variables
No, that cannot happen ever. The local variables (vars, parameters) in each function are created independently for every function call. Even if you call the same function it twice, it generates two unrelated sets of local variables. Assigning one of the variables from the first call will not change the variable of the same name in the second call.
What can of course happen is that the calls explicitly share a mutable value, i.e. an object or an instance of your class. If you change a property of it, and both function calls work on the same object, they can interfere indeed. In your example, the underscored properties of your MyClass instances are not used anywhere though.
Hello i have the following issue i am not quite sure how to search for it:
function(){
var sites;
var controller = {
list: function(){
sites = "some value";
}
}
}
So the question is how to access the sites variable from the top defined as
var sites
EDIT:
Here is a more complete part. i am Using marionette.js. i don't want to define the variable attached to the Module (code below) variable but keep it private to the Module, hope that makes sense. Here is the code that works:
Admin.module("Site", function(Module, App, Backbone, Marionette, $, _ ) {
Module.sites = null;
Module.Controller = {
list: function (id) {
Module.sites = App.request("site:entities");
}
};
});
and i would like instead of
Module.sites=null;
to do
var sites;
That sort of thing does make a difference right? Because in the first case i would be defining an accessible variable from outside where as the second case it would be a private one. i am a bit new to javascript so please try to make it simple.
if you are looking for global access, just declare the variable outside the function first, make your changes to the variable inside the function, then you can get the value whenever you need it.
I have found some info on this: sadly what i am trying to do doesn't seem possible.
Can I access a private variable of a Marionette module in a second definition of that module?
So i guess i have to do _variable to make developers know its private.
Disclaimer: I have no experience using Marionette, however, what you're describing sounds very doable.
One of the most powerful (in my opinion) features of JavaScript is closures. What this means is that any function declared from within another function has access to the variables declared in the outer function.
For example:
var func;
function foo() {
var answer = 42;
func = function () {
// I have access to variable answer from in here.
return answer++;
};
}
// By calling foo(), I will assign the function func that has access "answer"
foo();
// Now I can call the func() function and it has access to the "answer"
// variable even though it was in a scope that doesn't exist anymore.
// Outputs:
// 42
// 43
console.log(func());
console.log(func());
What this means is that if you declare var sites from within your module definition function as you described, you should have access to it from within any of your inner anonymous functions. The only exception is if Marionette is re-writing your functions (by using the Function function and toString()), which seems unlikely but possible.
Your original example should would as described, my suspicion is that there is something else going wrong with the code that is unrelated to your scope.
At my current place of work we have a set way of going about JavaScript and was wondering if someone can help shed any light on the below approach to storing variables and cached jQuery objects.
(function($) {
var APP = {};
$(function() {
APP.header = $("#header");
APP.footer = $("#footer");
});
})(jQuery);
Firstly, what is the point of setting what appears to be a global variable and then appending jQuery objects to the variable? Does it keep things cleaner, does it make your app perform any faster or is it an easier way to set variables without having to go:
var header = $("#header");
var footer = $("#footer");
I understand everything else going on, but always wondered why this was common place in all our project JS files? I presume there is a name for what is going on here and I'd like to understand it a little better, especially if there are caveats that I should bring up with my manager about doing this.
The name of these constructs are closure and namespace. A closure is a function that maintains references to variables outside of its scope.
Note that you can now access the APP variable later and it will be correctly updated:
(function($) {
var APP = {};
$(function() {
APP.header = $("#header");
APP.footer = $("#footer");
});
// "APP.header" and "APP.footer" are defined later in the code
})(jQuery);
If you used local variables, you could not get his effect:
(function($) {
$(function() {
var header = $("#header");
var footer = $("#footer");
});
// "header" and "footer" are long gone local variables
})(jQuery);
Note that you could use global variables (technically properties of window) here:
(function($) {
$(function() {
header = $("#header");
footer = $("#footer");
});
// "header" and "footer" are accessible global variables
})(jQuery);
But that's bad practice for a number of reasons (notably, possible collisions and unexpectedly defined variables). So you set up what's called a namespace -- a variable like APP to hold your data that needs to be "global-like".
As to the specific utility of this closure in your case, note that it's surrounded by $(). This is a jQuery way of saying "run this functoin when the DOM has finished loading". The DOM has to be finished loading before trying to access $('#header') and $('#footer') to make sure these items actually exist in the DOM. But since this is a callback, it will be execute later, that's why it's need a closure to maintain a reference to APP when it does eventually get called.
It's called namespacing.
This is so that you can keep the DOM clean, and other plugins/scripts won't interfere. If you were to create a global var called header or footer, another plugin may want to use that global var, and thus breaking your code.
APP appears to be the namespace your company uses to have an applicationwide consistent notation without cluttering the global namespace.
Also using variables to store the access to Dom-Elements is faster then accessing them each time needed via the jQuery Construct $("...")
Other than my comment, some "caveats" to "Global Vars"
Local variables are freed from memory whereas global vars sit with the lifetime of the app open
If code is inside a function or in another scope and references that variable, the engine has to slow down to step back until it reaches your global scope, thus causing "lag"
Little known factoid, global scope is shared with the window object!!! This means it takes even LONGER for the engine to find them, thus another negative slow down point
Say I have some context where variables are set and a λ-function is called which uses them directly:
function outerContext(){
...
var data = ...; // some data the script uses
...
someObject.method = function(){
data; // the variable is used here
};
...
}
I know that the dynamically created function has a snapshot of the context it was created in, so data variable is accessible there.
What are the dangers I may face with such an approach when I use this dynamically created method? Should I always give this data as an argument or is it ok?
The inner function does not have access to a "snapshot", it has full access to the data variable.
function outer() {
var data = 1;
...
someObject.method = function () {
data = 42;
};
someObject.method();
// data == 42
}
(The real explanation being that when using data in the inner function, Javascript will try to figure out which scope data is in. It will traverse up the scope chain to find the place where the variable was created, and that's the variable that will be used.)
There's no "danger", this is one of the core competencies of Javascript. It's like an object method modifying an object's properties. Of course you need to take care what you want to do, do you really want to modify the variable or do you just want to use it locally?
For the "snapshot", you need to use a closure:
function outer() {
var data = 1;
...
someObject.method = (function (data) {
return function () {
data = 42;
}
})(data);
someObject.method();
// data == 1
}
I can't really think of any "dangers" besides the possibility of causing a circular reference and thus a memory leak in case of DOM objects or such.
It works much like a private variable in a class.
I am trying to localize everything to a namespace in javascript. So I have objects that follow a naming convention like:
myapp.utilities.file.spinner
etc...
My question is, is there a way to avoid repeating that big string everytime I want to augment the object with a property or a method. Currently my code looks like this...
myapp.utilities.file.spinner.method1 = function() { };
myapp.utilities.file.spinner.method2 = function() { };
etc.
Something like this...
spinnerPath.method1 = function()
...where spinnerPath stands for myapp.utilities.file.spinner, would be nicer. But from my understanding I cannot just say
spinnerPath = myapp.utilities.file.spinner
as that will create another object in the global space.
Thanks
The code you're using won't actually create a new object, merely a new global variable referring to the existing object. It will pollute the global namespace however, so if you're looking to avoid that, you have several options:
You can use with, but don't because it will probably cause you more heartache than it's worth.
You can make a shorthand pointer variable inside each function outside of the global namespace: var s = myapp.utilities.file.spinner;, but this is annoying.
(Probably the best option) create a "private namespace" using an immediate-call function:
(function (S)
{
S.method1 = function(){/*whatever*/};
S.method2 = function(){/*whatever*/};
})(myapp.utilities.file.spinner)
Try this:
(function(){
var spinner = myapp.utilities.file.spinner;
spinner.method1 = function(){};
})();
myapp.utilities.file.spinner.method1 = function() { };
myapp.utilities.file.spinner.method2 = function() { };
...
// Somewhere else in your code, create a temp local called "spinner"
// that references your longer path object.
var spinner = myapp.utilities.file.spinner;
spinner.method1();
You can just make a temporary local variable wrapped in an anonymous function:
(function(){
var spinnerPath = myapp.utilities.file.spinner;
spinnerPath.method1 = function() { };
spinnerPath.method2 = function() { };
spinnerPath.method1();
})();
Here, spinnerPath is in fact a local reference to the global myapp.utilities.file.spinner object, not a copy. Objects in JavaScript are references, so if you create a local variable that points to it, you will not create a copy or pollute the global namespace.