I am new to JavaScript and have a question to ask. When creating a new programme is it best to create all of my functions within a global variable/object or to create them all separate.
For example this:
var register = {
members: [],
Member: function(){
},
reset: function(){
},
printMembers: function(){
},
process: function(){
}
};
function init(){
register.process();
};
window.onload = init();
Or instead like this:
var members = [];
function Member(){
};
function reset(){
};
function printMembers(){
};
function process(){
};
function init(){
process();
};
window.onload = init();
This may be a very stupid question to ask...
When creating a new programme is it best to create all of my functions within a global variable/object or to create them all separate.
This is kind of opinion based, but I'll give you a good reason for creating all your properties inside a global object: you won't pollute the namespace. That means, you won't be creating a lot of global objects, that would be properties of the global object (window, in browsers).
If you use someone else's library, and if both you and him would create global objects, the script that was included later in the HTML would override properties with the same name declared in the other script.
Finishing, I would suggest you to write your code like:
var app = {};
app.members = [];
app.Member = function(){};
app.reset = function(){};
app.printMembers = function(){};
app.process = function(){};
function init() {
app.process();
};
window.onload = init;
Also, note that you should do window.onload = init, and not window.onload = init().
The difference:
with window.onload = init, you are setting the onload property in window to be the init function. Later, when the page finishes loading, someone will ask the window to execute its onload property, and window will then do something like this.onload().
with window.onload = init(), you are executing init right there and setting the onload property of window to be the return of the execution of the init function. Since every Javascript function returns something (by default, undefined), you'll be setting window.onload = undefined. And this is not what you want.
It's a matter of taste style--kindof like writing an essay. Ultimately, what's important is that anyone (or you, six months from now) can read through your code and understand what's going on. Just focus on functionality and you will naturally start making decisions about what's logical where.
(Eloquent Javascript)[http://eloquentjavascript.net//] is also a great book on the subject.
Related
I'm new to JS and especially to prototypes.
I have this class and I cannot figure out how to access the properties.
var Lobby = function (preloader, serverConn) {
// Hold a reference to EventBus
this.serverConn = serverConn;
this.preloader = preloader;
this.scheduleItemService = new ScheduledItemService(this.preloader);
this.stage = new createjs.Stage("lobbyCanvas");
};
Lobby.prototype.start = function(me, signedRequest) {
sendMessage(data, function() {
// inside this scope this.stage is undefined!
renderLobbyImages(this.stage, this.scheduleItemService);
});
};
function renderLobbyImages(stage, scheduleItemService) {
stage.update();
};
Calling code:
var lobby = new Lobby(preloader, serverConn);
lobby.start(me, status.authResponse.signedRequest);
What am I doing wrong accessing 'renderLobbyImages' ??
Thank you :-)
In javascript, this is not resolved based on where it is declared/used. It is resolved when it gets called. (see: How does the "this" keyword in Javascript act within an object literal?).
Therefore, in the code above, since this is called in the callback to sendMessage(), and since sendMessage is asynchronous (meaning the callback will be called long after the call to start() have returned), this is therefore referring to the global object (which is window in web browsers, something unnamed in node.js).
So effectively, your code is doing this (no pun intended):
sendMessage(data, function() {
renderLobbyImages(stage, scheduleItemService);
});
Since there are no global variables called stage or scheduleItemService both are effectively undefined!
Fortunately, there is a workaround for this. You can capture the correct object in a closure:
var foo = this;
sendMessage(data, function() {
renderLobbyImages(foo.stage, foo.scheduleItemService);
});
Alternatively, you can pass the correct object (this) into an IIFE:
(function(x){
sendMessage(data, function() {
renderLobbyImages(x.stage, x.scheduleItemService);
});
})(this); // <-------- this is how we pass this
or:
sendMessage(data, (function(a){
return function(){
renderLobbyImages(a.stage, a.scheduleItemService);
}
})(this));
Or in this case, since stage and scheduleItemService are not functions, you can even pass them directly:
sendMessage(data, (function(a,b){
return function(){
renderLobbyImages(a,b);
}
})(this.stage, this.scheduleItemService));
There are lots of solutions to this problem. Just use the one you're most comfortable with.
Two problems.
this is missing in your constructor function on scheduleItemService.
Some functions you call to assign values seem to be not returning anything.
new createjs.Stage("lobbyCanvas");
new ScheduledItemService
Your calling method is alright.
this always refers to the calling object. When you say...
varlobby = new Lobby();
lobby.start();
... your calling object is lobby which has all the fields the start() function needs. But there initialization seems to be not working properly.
Please read this MDN starter guide.
Also we are having a some discussion about classical and prototype based OOP in this question. Please see the answer of Paul S for more about the tutorial I mentioned. Please see my answer if you need to see the tutorial in classical OOP light.
I have a simple thing like this:
function init() {
var $something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
I thought this would work, but it doesn't, console says that $something is not defined. What's the issue?
Many thanks!
$something is defined within the scope of the function 'init' so you will only ever be able to access it from within that function as it is. If you wanted to get a value back, you could return it, like so:
function init() {
var $something = 'something';
return $something;
}
jQuery(document).ready(function($) {
var $something = init();
alert($something);
}
Notice that both those variables have the same name (normally a bad idea). They are each defined within their own scope, and thus they are totally different variables.
An alternate pattern might be to wrap the entire thing and use that scope, like so:
(function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
})();
That way you have a single variable, but you avoid polluting the global namespace.
Edit:
In response to your comment, the above could be written like:
var newscope = function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
}
newscope();
But I have defined the function AND called it at basically the same time without having to give it a name.
This is a scoping issue.
$something is defined within the scope of the init() function, and therefore, it will be disposed of when the init() function completes.
Vars declared with var will be local to the closure in which they are declared. As you've found, this means, therefore, that outside of that closure they are not reachable.
There's many ways round this and each means a different design pattern. Here's one pattern you could use:
({
init: function() {
this.something = 'hello';
jQuery(function() { this.dom_ready(); }.bind(this));
},
dom_ready: function() {
//DOM code here
alert(this.something); //hello
}
}).init();
Here I declare several methods of an object, or namespace. Since they belong to, and are called in the context of, this one object, they communicate between one another with properties rather than variables.
Variables are thus demoted to being useful only within (but never outside of) the closure in which they are declared.
One advantage of this pattern is that you can separate any code that needs to wait for the DOM to be ready from any code that doesn't. This is achieved by having a dedicated dom_ready method.
I was writing some code with a namespace to reduce clutter when I thought that maybe I could stop polluting the namespace all together by creating an anonymous namespace. I just want to make sure that this is valid and there's no hidden gotcha's that I'm not thinking of.
Basically, the code goes like this:
new function() {
// bunch of private helper functions and variables
// ...
this.loadEventHandler = function()
{
// do load stuff
};
this.resizeEventHandler = function()
{
// do resize stuff
};
window.onload = this.loadEventHandler;
window.onresize = this.resizeEventHandler;
};
Is there anything that I'm not taking in to account? This wouldn't be taken out by a garbage collector or something, right?
What you have will work, but the more idiomatic, clean way would be to use an IIFE —Immediately Invoked Function Expression.
Your code above creates a function on the fly, and invokes it with new, which results in a new object being created. The object has a loadEventHandler and resizeEventHandler added to it, which you then add to the global object. The function then exists, releasing said object for future garbage collection.
An IIFE lets you get in, and add what you want to the global object, without cluttering it up with all your private helpers and such, and without any useless objects being created in the process.
(function() {
// bunch of private helper functions and variables
// ...
function loadEventHandler()
{
// do load stuff
};
function resizeEventHandler()
{
// do resize stuff
};
window.onload = loadEventHandler;
window.onresize = resizeEventHandler;
})();
WARNING!! I AM A NOVICE THROUGH AND THROUGH
Alright, so I know there have been a lot questions about Global variables, and I think that's what I'm looking for, but, not exactly. Lately I've been needing to call upon the same lines of code several times. document.getElementById("example").style or similar to little things like that but I need to continuously repeat.
My question is how do I make it so that I make one variable, outside of the function, to save time writing these lines?
What I've been seeing is to simply write it outside like this var inferno = document.getElementById("inferno"); but this is far from working.
This is my code right now, it's simple because I was just using it as a test, but can anyone help me?
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
You have the right idea. Note, though, that the variable doesn't have to be global. It just has to be where all of the code that wants to use it can use it.
For example, this creates a global:
<script>
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
function somethingElse () {
inferno.style.color="green";
}
</script>
(Note that this needs to be after the markup creating the inferno element.)
The problem with globals is that they can conflict with each other, and in fact the global "namespace" is really, really crowded already.
You can avoid that by wrapping up the code that needs inferno in a scoping function, like this:
<script>
(function() {
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
function somethingElse () {
inferno.style.color="green";
}
})();
</script>
That code creates an anonymous function and then calls it immediately, running the code inside.
Now inferno is "global" to the functions that need it, but isn't actually a global.
Let's take a further example:
<script>
(function() {
var outer = 42;
function doSomethingCool() {
var inner = 67;
document.getElementById("someElement").onclick = function() {
alert("inner = " + inner + ", outer = " + outer);
};
}
// Can't use `inner` here, but can use `outer`
alert("outer = " + outer);
doSomethingCool();
})();
</script>
That code wraps everything in a scoping function, and the outer variable is accessible everywhere within that scoping function. It also has a function, doSomethingCool, which has a variable called inner. inner is only accessible within doSomethingCool. Look at what doSomethingCool does: It hooks up an event handler for when someElement is clicked. It doesn't call the function, it just hooks it up.
The really cool thing is that later, when someone clicks the element, that function has access to that inner variable.
And in fact, that's true for arguments you pass into the function as well. One last example:
<input type="button" id="element1" value="One">
<input type="button" id="element2" value="Two">
<script>
(function() {
function hookItUp(id, msg) {
document.getElementById(id).onclick = function() {
alert(msg);
};
}
hookItUp("element1", "This message is for element1");
hookItUp("element2", "And this one is for element2");
})();
</script>
There, we have this function that accepts a couple of arguments, and we call it twice: Once to hook up click on element1, and again to hook up click on element2.
The really cool thing here is that even though the clicks happen much later, after the calls to hookItUp have long-since returned, the functions created when we called hookItUp still have access to the arguments we passed to it — when we click element1, we get "This message is for element1", and when we click element2, we get "And this one is for element2."
These are called closures. You can read more about them on my blog: Closures are not complicated
That'll work, but only if the declaration appears after the point in the DOM where the element actually appears. Try moving your <script> to the very end of the <body>.
Another thing you can do is use the window "load" event to make sure the whole DOM has been seen before your code runs.
for example
var myGlobalVars = {"inferno":null,"othervar":null}; // globals in their own scope
function clickMe(varName,color) { // generic function
myGlobalVars[varName].style.backgroundColor=color;
}
window.onload=function() {
// initialise after the objects are available
for (var o in myGlobalVars) myGlobalVars[o]=document.getElementById(o);
// execute
clickMe("inferno","red");
}
.
.
T.J. Crowder gave a beautiful answer about scoping; just to add on that you can also use an immediately-invoked function expression to create a module with your UI elements, i.e.
var UI = (function() {
...
return {
inferno: document.getElementById("inferno");
};
})();
...
UI.inferno.style = ...;
I have inherited a code base that I need to update for my job. I'm slowly learning what they are trying to accomplish with the closure that is in place, but I am getting stuck when trying to update a part of the site that uses this functionality. I'll give a basic outline of what the code is trying to accomplish and see if anyone can help.
var TheObject = (function (){
var veryLargeDependantData = {
var1: {},
var2: {},
var3: [],
//Set these variables via functions
function1: function f1(data){...},
function2: function f2(data){...},
initialize: function initialize() { //set values for var1... var3}
};
return {initialize: veryLargeDependentData.initialize};
})().initialize();
Since I obviously cannot show production code on the site this will have to do. But basically the veryLargeDependentData variable is the entrance to the function. When the page loads it calls the initialize function and everything is happy. But now I need to add this to an onclick event for and older page and the firebug console says that the variable is undefined. In the other pages I am able to use it with no problem.
My question is what magic is going on that causes the closure not to be part of a callable namespace such as this. I'm a bit of a javascript nOOb so I apologize if the question sounds misguided.
onclick='TheObject.initialize();'
I assume what you mean is that you want to run the initialize function in a click event handler, and you're currently attempting to do so like this:
TheObject.initialize();
If that's the case, the problem is that TheObject actually refers to the return value of initialize, since you called initialize on the return value of the immediately-invoked function expression. And the chances are that initialize is returning undefined (most likely, it has no explicit return statement).
To solve this, you probably want to remove the immediate call to initalize, which will allow you to use the line shown above both on page load and anywhere else.
In this code, the value of TheObject will be whatever the veryLargeDependentData.initialize() method returns. If the initialize method returns nothing, TheObject will be undefined.
A simplified example:
var TheObject = (function () {
return {
initialize: function () {
// stuff happens here, but importantly, there's nothing returned
}
}
})().initialize();
You can break this down into the following order of execution:
// the value of step_one is a function that will return an object when it is run
var step_one = (function () {
return {
initialize: function () {
// ...
}
}
});
// after running the function step_one, step_two will be an object
// containing one key - initialize - which is a function
var step_two = step_one();
// since initialize doesn't return anything, TheObject is set to undefined.
var TheObject = step_two.initialize();
You can get around this by setting TheObject to be the object containing the initialize method then run that method again whenever you need it.
var TheObject = (function () {
return {
initialize: function () {
}
}
})();
// initialize
TheObject.initialize();
// and again
TheObject.initialize();
PLEASE NOTE!
The original author may have intended for the initialize method to only be run once. Running it more than once might introduce bugs into your system!
Seems unnecessarily complicated, I'm not sure what is gained by using the anonymous function and closure in this case. Is there any reason you can't simple do the following?
var TheObject = {
var1: {},
var2: {},
var3: [],
//Set these variables via functions
function1: function(data){...},
function2: function(data){...},
initialize: function(){alert("initialize");}
};
var initButton = document.getElementById("initButtonName");
initButton.addEventListener("click", TheObject.initialize);
Note that you'd want to remove the inline event.