Due to performance and other issues, I want to split my code into seperate functions as before it was just one big ".ready" function.
I am new to javaScript/jquery but I thought I would give it a go myself. I have done exactly the way I thought it was done but my console is telling me things are undefined so I am guessing I have got things out of scope. I have read up on it in more detail, but still have not got anywhere.
My code works OK at the moment but I want to get into the habbit of clean coding. Can someone point out where I am going wrong so I can carry on myself?
Here is an example of what I have so far
//Global variables
var randomWord = [];
var listOfWords = [];
var populationNumber = [];
var attemptNumber = [];
var completionNumber = [];
var gridSize = [];
generateGrid();
startMusic();
randomizeReward();
//Click event to start the game
$(".start-btn-wrapper").click(function () {
startplay();
});
//Click event to restart the game
$(".restart-btn").click(function () {
restartplay();
});
Thanks
Fiddle: http://jsfiddle.net/QYaGP/
Fiddle with HTML: http://jsfiddle.net/QYaGP/1/
You need to start passing some information into the functions you're defining. If your functions all have no arguments, then you will have to use globally defined variables, hardcoded references to jquery selections etc in order to get anything done.
So as an example, you have a function
function replaySound() {
$("#hintSound").attr('src', listOfWords[randomWord].hintSound);
hintSound.play();
}
This is actually going to play the sound detailed in listOfWords[randomWord] via the element #hintSound. You could do that via:
function playSound(selector, wordlistEntry) {
$(selector).attr('src', wordlistEntry.hintSound);
$(selector)[0].play();
}
And then instead of calling replaySound(), you'd call:
playSound('#hintSound', listOfWords[randomWord]);
This way the behaviour that you want is wrapped up in the function, but the specifics, i.e. the data you need for it, are passed in via the arguments. That allows you to reuse the function to play any sound using any selector, not just #hintSound.
You'll find as you do that that you need to start choosing what a function will act on in the code that calls it, rather than in the function. That's good, because the context of what you're trying to achieve is there in the calling code, not in the function. This is known as 'separation of concerns'; you try to keep logic about a given thing confined to one area, rather than spreading it about in lots of functions. But you still want functions to allow you to encapsulate behaviour. This allows you to change behaviour cleanly and easily, without having to rewrite everything every time some part of the logic changes.
The result should be that you find several functions actually did the same thing, but with different specifics, so you can just have one function instead and reuse it. That is the Don't Repeat Yourself principle, which is also important.
If you are concerned about performance, I would look into using an framework such as AngularJS. You can inject modularized code. Even better, with MVC your view is bound to your model so by changing the model the view automatically updates itself.
Also, stop using class selectors. Use ID selectors. They are much faster. You also want to preload selectors (even with class selectors). That way you are only searching the DOM once:
var ele = $('#elementId');
$(ele).doSomething();
This way, you have a reference to the DOM element. You can use a datastructure to store all of your references outside of the global scope:
var elementReferences = {}; //declaration
elementReferences.mainPage = {}; //use
elementReferences.mainPage.root = $('#mainPage'); //load the root div of a page segment
elementReferences.mainPage.title = $(elementReferences.mainPage.root).children('#title'); //load the title
elementReference.mainPage.form = $(elementReferences.mainPage.root).children('#form'); //load the form
Now you can do this:
$(elementReference.mainPage.form).whatever();
and it doesn't have to search the DOM for the element. This is especially useful for larger apps.
If you put a function within document.ready, as per your fiddle, you are only able to access that function within the scope of the document.ready call. You really want to be able to load/unload functions as needed dynamically within the scope that they are required in, which is where angularjs comes into play.
You also, for the most part, want to remove your functions and variables from the global scope and put them into containers that are sorted by their dependencies and use. This is Object Oriented Programming 101. Instead of having a bunch of arrays sitting within the global scope where they could be overwritten by mistake by another developer, you want to put them within a container:
var arrays = {}; //create the object
arrays.whatever1 = [];
arrays.whatever2 = [];
Obviously, you will probably want a more descriptive name than "arrays". Functions work the same manner:
var ajax = {}; //ajax object
var ajax.get = function(){
};
var ajax.post = function(){
};
var ajax.delete = function(){
};
This generally promotes cleaner code that is more reusable and easier to maintain. You want to spend a good portion of your time writing a spec that fully documents the overall architecture before actually beginning development. NEVER jump the gun if you can help it. Spend time thoroughly researching and planning out the big picture and how everything fits together rather than trying to wing it and figure it out as you go. You spend less time having to reinvent the wheel when you do it this way.
It's developed by google, so it should be around for quite a while. I'm not sure if you are the guy in charge of your system's architecture, but if performance/reusability is an issue at your company it is definitely worth taking a look at. I'd be more than happy to give you a walkthrough regarding most of what I know in terms of software architecture and engineering. Just MSG me if you are interested. Always happy to help!
Related
Is it worth using mini functions in JavaScript? Example:
function setDisplay(id, status) {
document.getElementById(id).style.display = status;
}
And after that, each time you want to manipulate the display attribute of any given object, you could just call this function:
setDisplay("object1", "block");
setDisplay("object2", "none");
Would there be any benefit of coding this way?
It would reduce file size and consequently page load time if that particular attribute is changed multiple times. Or would calling that function each time put extra load on processing, which would make page loading even slower?
Is it worth using mini functions in JavaScript?
Yes. If done well, it will improve on:
Separation of concern. For instance, a set of such functions can create an abstraction layer for what concerns presentation (display). Code that has just a huge series of instructions combining detailed DOM manipulation with business logic is not best practice.
Coding it only once. Once you have a case where you can call such a function more than once, you have already gained. Code maintenance becomes easier when you don't have (a lot of) code repetition.
Readability. By giving such functions well chosen names, you make code more self-explaining.
Concerning your example:
function setDisplay(id, status) {
document.getElementById(id).style.display = status;
}
[...]
setDisplay("object1", "block");
setDisplay("object2", "none");
This example could be improved. It is a pity that the second argument is closely tied to implementation aspects. When it comes to "separation of concern" it would be better to replace that argument with a boolean, as essentially you are only interested in an on/off functionality. Secondly, the function should probably be fault-tolerant. So you could do:
function setDisplay(id, visible) {
let elem = document.getElementById(id);
if (elem) elem.style.display = visible ? "block" : "none";
}
setDisplay("object1", true);
setDisplay("object2", false);
Would there be any benefit of coding this way?
Absolutely.
would [it] reduce file size...
If you have multiple calls of the same function: yes. But this is not a metric that you should be much concerned with. If we specifically speak of "one-liner" functions, then the size gain will hardly ever be noticeable on modern day infrastructures.
...and consequently page load time
It will have no noticeable effect on page load time.
would calling that function each time put extra load on processing, which would make page loading even slower?
No, it wouldn't. The overhead of function calling is very small. Even if you have hundreds of such functions, you'll not see any downgraded performance.
Other considerations
When you define such functions, it is worth to put extra effort in making those functions robust, so they can deal gracefully with unusual arguments, and you can rely on them without ever having to come back to them.
Group them into modules, where each module deals with one layer of your application: presentation, control, business logic, persistence, ...
It would reduce file size and consequently page load time if that particular attribute is changed multiple times. Or would calling that function each time put extra load on processing, which would make page loading even slower?
It won't make the page's loading slower. Adding a few lines here and there with vanilla js won't effect the page loading time, after all, the page's loading time is the time to load the html js and css files, so a few lines in js won't effect it. A performance issue is unlikely to happen unless you're doing something really intended that brings you into a massive calculation or huge recursion.
Is it worth using mini functions in JavaScript?
In my opinion - Yes. You don't want to overuse that when unnecessary, after all, you don't want to write each line of code in a function right? In many cases, creating mini functions can improve the code's cleanness, readability and they can made it easier and faster to code.
With es6 you can use a single line functions with is very nice and easy:
const setDisplay = (id, status) => document.getElementById(id).style.display = status;
It doesn't really affect the performance unless you execute that function 10,000 or more times.
Jquery use mini functions, like $('#id').hide(); $('#id').show(); and $("#id").css("display", "none"); $("#id").css("display", "block");
It creates more readable code but it doesn't do that much to the performance.
var divs = document.querySelectorAll("div");
function display(div, state) {
div.style.display = state;
}
console.time("style");
for(var i = 0; i < divs.length; i++) {
divs[i].style.display = "none";
}
console.timeEnd("style");
console.time("function");
for(var j = 0; j < divs.length; j++) {
display(divs[j], "block");
}
console.timeEnd("function");
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
In terms of runtime performance I’d say it doesn’t make any difference. The JavaScript engines are incredibly optimized and are able to recognize that this function is being called often and to replace it by native code, and maybe even able to inline the function call so that the cost of calling an extra function is eliminated completely.
If your intent is to reduce the JavaScript files: you save a few tens of bytes for each place in the code where the function can be used, but even if you call this mini function 100 times, you will be saving 1 or 2 kb of uncompressed JavaScript. If you compare the final gzipped JavaScript (which should be how you serve your resources if you care about performance) I doubt there will be any noticeable difference.
You also need to think about how this type of code will be understood by your colleagues, or whoever may have to read this code in the future. How likely it is that a new programmer in your team knows the standard DOM API? Your non-standard functions might confuse your future teammates, and require them to look into the function body to know what they do. If you have one function like this it probably doesn’t make much of a difference, but as you start adding more and more mini functions for each possible style attribute you end up with hundreds of functions that people need to get used to.
The real benefit is ability to update on several places. Let say that you realize that you want to use classes instead of using styles. Instead of having to change on all places, you can just change that one method.
This is even more apparent when you read from a common source. Perhaps you have an API that you call with a single method.
let user = getData('user');
...but instead doing a API call, you want to use localforage to store the user data and read from the disk instead.
let user = getData('user');
... but you realized that it takes about 70-200 ms to read from localforage, so instead you store everything in memory, but reading from localforage if it's not in memory, and doing an API call if it's not in localforage. Getting user information is still the same:
let user = getData('user');
Just imagine if you wrote the whole call in every single place.
I usually create mini functions like these if I 1) read/write data, 2) if the code gets shorter OR more readable (i.e. the method name replaces comments), 3) or if I have repeatable code that I can sum up in a dynamic way.
Also, to quote from the book Clean Code: each function should do only one thing. Thinking in terms of mini functions helps with that.
I'm not fully sure of what a mini function is but if you're asking whether it's better to define functions for code that needs to be called multiple times instead of typing out/ copy-pasting those same lines of code over and over then it's better to go with option 1 for readability and file-size.
It would reduce file size and consequently page load time if that particular attribute is changed multiple times. Or would calling that function each time put extra load on processing, which would make page loading even slower?
It's not in typical scenarios possible for the usage of such functions to put (any significant) extra load on processing that would slow down the document parsing, it's likeliness to do that would however depend on when and how you call your code to be executed but it's not like you'll be looping O(N3) and calling such functions thousands of times to impact page loading (right?) - if anything, predefined functions like that meant to take dynamic inputs would make your code neater, more uniform looking and reduce file-size... with a very tiny obvious overhead when compared to directly accessing properties without having to go through another function. So you decide whether you want to take up that bargain depending on whether the frequency of you calling functions like that would be significant enough to be considered.
I'm sure most engines optimize repetitive code so it's (or nearly) equivalent to the same thing anyway so I'd choose readability and improve how easy it is for others to understand and follow through code instead.
JavaScript is prototype language on top of that going on a leaf to say everything in JavaScript is an object so its object oriented as object oriented gets to be although at a very different level.
So the question is like we create classes in object oriented, would we develop script framework as functional programming or prototype and what will be performance hit.
so lets consider this very fine example.
function abc(name, age) {
this.name = name;
this.age = age;
}
person1 = new abc('jhon', 25);
person2 = new abc('mark', 46);
console.log(person1.age);console.log(person2.age);
person1.father = 'mark';
console.log(person1.father);
we have created a function and have used the power of this in the scope of function thus every new insurance will carry this information. and no two instances will be alike.
Then down the a line we have used prototype to add father information on instance. This where prototyping becomes awesome. Thus whole this scoping + instance + prototyping is fast.
function abc(name, age) {
name = name;
age = age;
return name; // its either name or age no two values
}
person1 = abc('jhon', 25);
person2 = abc('mark', 46);
console.log(person1);
console.log(person2);
abc.father = 'mark';
console.log(person1.father);
console.log(abc.father);
we tried the same on functional sense the whole thing fell apart. no instance + no this scoping + no new prototyping delegating down the line. Thus in long run this approach will reduce performance as one would have to re-fetch things over and over again on top of that with t=out this scoping we are only returning single factor where as with this scooping we are packing a lot more into object. this one point.
function abc(name, age) {
this.name = name;
this.age = age;
}
person1 = new abc('jhon', 25);
person1.fatherName = 'Mark';
person1.fatherUpdate = function (d){this.fatherAge = this.fatherName+' '+d;};
person1.fatherUpdate(56);
console.log(person1.fatherAge);
Now have added more complexity we have carried this down the line hence the scope and have added functions on top of it.
This will literally make your hair fall out if things were done in pure functional way.
Always keep in mind any given function can and may compute and execute as many things you want but it will always give single return with 99% of the time single property.
If you need more prototype way with scoping as above.
jquery way of doing things.
I had made my framework ActiveQ which uses the above its almost 30 Kb unminified and does everything jquery does and much more with template engine.
anyways lets build example - it is an example so please be kind ;
function $(a){
var x;
if(x = document.querySelector(a)) return x;
return;}
console.log($('div'));
console.log($('p'));
<div>abc</div>
Now this almost 50% of your jquery selector library in just few lines.
now lets extended this
function $(a){
this.x = document.querySelector(a);
}
$.prototype.changeText = function (a){
this.x.innerHTML = a;
}
$.prototype.changeColor = function (a){
this.x.style.color = a;
}
console.log(document.querySelector('div').innerText);
app = new $('div');
app.changeText('hello');
console.log(document.querySelector('div').innerText);
app.changeColor('red');
<div>lets see</div>
what was the point of the whole above exercise you wont have to search through DOM over and over again as long as long one remains in scope of the function with this
Obviously with pure functional programming one would have to search though all over again and again.
even jquery at time forgets about what it had searched and one would have to research because this context is forwarded.
lets do some jquery style chaining full mode way - please be kind its jusrt an example.
function $(a){
return document.querySelector(a);
}
Element.prototype.changeText = function (a){
this.innerHTML = a;
return this;
}
Element.prototype.changeColor = function (a){
this.style.color = a;
return this;
}
console.log($('div').innerText);
//now we have full DOm acces on simple function reurning single context
//lets chain it
console.log($('div').changeText('hello').changeColor('red').innerText);
<div>lets see</div>
See the difference less line of code is way faster in performance as its working with the browser rather then pure functional way rather then creating a functional call load and search load all repeatedly
so you need to perform bunch of tasks with single output stick to functions as in conventional way and if you need to perform bunch of tasks on context as in last example where context is to manipulate properties of the element as compared to $ function which only searches and returns the element.
In a fairly large application made in Cordova, we keep going back and forth taking a set of variables from the user's LocalStorage. I mean, this set is quite big (in can go from some kbytes to fairly 10mb). I'm pretty sure that keeping variables loaded in memory may not be a pretty good idea (especially in low power, low memory devices), but it turns out that getting them from the LocalStorage looks like it's taking too long. At this moment, our practice is something like this:
function doSomething() {
var ourData = JSON.parse(localStorage.getItem('ourData'));
ourData = processingBackAndForth(ourData);
localStorage.setItem('ourData', JSON.stringify(ourData));
syncWithServer(ourData);
}
Where processingBackAndForth(ourData) is pretty intensive and the whole function is used almost once per 10 seconds. Now, I was thinking, should this be faster?
ourData = JSON.parse(localStorage.getItem('ourData'));
function doSomething() {
ourData = processingBackAndForth(ourData);
localStorage.setItem('ourData', JSON.stringify(ourData));
syncWithServer(ourData);
}
After reading a little on this, well, it basically tells that it's better to keep them in the local scope (which I know is better for encapsulation), but isn't this process a little too much? Just picture it, retrieving from LocalStorage, saving it back again, and letting GC to clean a variable with will be used afterwards once again.
I know one of the points that states the link is that if something needs to be used across the app, and only in really special cases, then make it global, but anyway things like keeping a synced version between the server and LocalStorage still concern a lot to me, so actually I only remove the weight of retriving and garbage collecting.
Would a JavaScript code use a big global variable instead of big local variables to gain some micro-optimizations?
You say that your data can be really large (up to 10MB). Now the question is: would it be better to retrieve your data only one time and save it globally?
In terms of performance, if you get or set your data many times this will really slow down your program, hence your goal is to request and set the data as few times as possible, so that you will not have to wait for the localStorage to retrieve it every time.
Now, given your function:
ourData = JSON.parse(localStorage.getItem('ourData'));
function doSomething() {
ourData = processingBackAndForth(ourData);
localStorage.setItem('ourData', JSON.stringify(ourData));
syncWithServer(ourData);
}
The above code would be the best solution if you call the doSomething() function multiple times, because the data is only retrieved once, then can be elaborated as many times as you want, and saved again. When you have finished using your data you should just delete it (delete ourData;).
Otherwise, if you call the doSomething() function only one time, then the best solution would be to get the data inside the function itself, since that it will only live as long as the function runs, and it will be auto-removed from the memory once the function ends, so the best way to do it would be:
function doSomething() {
var ourData = JSON.parse(localStorage.getItem('ourData'));
ourData = processingBackAndForth(ourData);
localStorage.setItem('ourData', JSON.stringify(ourData));
syncWithServer(ourData);
}
TL;DR you've got two options:
If you use the doSomething() function several times, it's better to save the data globally and then delete it when finished.
If you use the doSomething() function only one time, then it's better to retrieve the data inside the function, using the var keyword.
It's hard to get the exact specific information on OOP you're searching for.
I tried to keep it as short as possible:
I'm currently developing a jump n run in HTML5.
I have actually no real experience with developing games.
But I know how the basics are supposed to work.
I just want to know if I'm doing it right.
I have a game, player and level object.
What I'm currently doing is the following:
Game.player = new Player();
Game.level = new Level();
Game.level.load(Game.currentLevel);
...
Game.run();
Is that the best way or should I call them all on their own, e.g.:
var Player = new Player();
var Level = new Level();
Level.load(Game.currentLevel);
...
Game.run();
They way I'm doing it right now (the first one) seems more logic to me.
But.. in the level objects functions I have to check for various variables from the game object or call a function of its self. Thus I have to write Game.level.funcName inside the Level objects functions. But since Game.level doesnt actually exist at the level objects declaration point it feels kinda wrong and dirty. Here is another example:
Level.prototype.reset = function() {
this.load(Game.currentLevel);
};
The Game.currentLevel is hardcoded, isn't there any better way to detect which variable currently handles the game object, or is it totally ok the way I'm doing it ?
So the basic question is, whats the best way to let objects interact with each other ?
And one last question which is kinda offtopic, but what does ()(); do?
I sometimes see it beeing used like this:
(function() {
// Content
});
I hope you understand my concerns, thanks for your time and answers. :)
I would recommend the first approach, because it's more modular.
Your problem can be solved by simply passing a reference of the Game instance to the other components, so that they are aware of the game.
It's not uncommon for objects to have a cyclic structure in javascript:
Game.level = new Level();
Game.level._game = Game;
//...
Level.prototype.reset = function() {
this.load(this._game.currentLevel);
};
Of course that you can do it a bit more elegant by passing reference at initialization, but I think you got my point.
I think the way you're doing things look pretty good. About the last part of your question, that's called an immediate function. It's a function that's called right after it's declared. You can see more info about here: http://javascriptmountain.com/2011/06/functions/immediate-functions-in-javascript-the-basics/
I have answer to last question
Question: what does ()(); do? I sometimes see it beeing used like this:
(function() {
// Content
});
It is the self executing closure. I will provide simplest explanation here. When we write java script function they need to be called to execute them.
For example,
function alertMe(){
alert('alerted');
}
alertMe(); // We need to call intentionally for execution of function.
Self executing closure doesn't require calling separately.
For example,
(function(){
alert('alerted');
})();
Above javascript executes automatically, when script is loaded. Same Question is answered on SO here.
Start with the user interaction and work backwards. It's possible to get too much involved in the design process and come up with convoluted designs if that design is too flexible or is solving too many problems.
Based on my limited knowledge of games, and even lesser of game programming, and what you've shown us, I believe there are two user interactions that you're dealing with here.
User picks a particular game level to play
User resets that game level
Storing the current level as a property of the game object is perfectly fine. I can think of two methods that would be needed to handle these interactions both of which would make sense on a Game object.
function setLevel(levelNumber) {
this.currentLevelNumber = levelNumber;
this.level = new Level(levelNumber);
}
function resetCurrentLevel() {
this.setLevel(this.currentLevelNumber);
}
I would break the connection from a Level object to the Game object, and load a level independently of the game as much as possible. So instead of doing,
game.level = new Level();
game.level.load(game.currentLevel);
I'd push the burden of initializing a level upon the Level constructor itself as in,
game.level = new Level(8);
or even better, make it a method call on the Game object as in my example above - setLevel(n). The method will be responsible for ensuring that the game object is consistent when the level changes.
game.setLevel(8);
Since the reset function resetCurrentLevel internally uses this method, handling of level changes will be unified whether it's loading a new level, or resetting a current level.
I'm new to object oriented programming and am slowly learning how to apply it to javascript. So please bear with me. :)
I have two basic objects:
"record" which contains methods for editing a single record from a recordset. (create, save, load, etc.)
"recordList" which contains methods for outputting a paginated list of record titles.
I would like for these objects to be able to work together. For example, if record.save() is called, recordList.refresh() is also called, so that the paginated list reflects the updated data.
To accomplish this, I have created a third object "control" which contains instances of both "record" and "recordList". I am using "control" in the following fashion:
control = {}
control.record = object.create("record");
control.recordList = object.create("recordList");
control.save = function() {
this.record.save();
this.recordList.refresh();
};
This works. But I am wondering, is it proper? (I want to be sure I am not violating any of the rules of OO design in doing this.) Is there a better way?
Thanks in advance for your help.
Speaking from an OOP perspective, I don't think a record would save itself. A record in a database is simply data, and the database itself is what does things with that data, whether it's saving or loading or etc. That being said I'd make record be simply an object that holds data and would create a recordset object for interacting with the data. Within that recordset object you could put your recordList and update it accordingly. Something like:
var recordset = function() {
var me = this;
var currentRecord = object.create("record");
var recordList = object.create("recordList");
me.save = function() {
//Insert record.save code here
recordList.refresh();
};
};
Something to note about that code. In that setup currentRecord and recordList can't be accessed from outside the function and therefore you have encapsulation, one of the hallmarks of OOP. This is because the recordset function is a closure that "closes over" all variables within, meaning that every function within has access to the variables within the scope of recordset.
You could let the outside world get access through get or set functions:
me.getRecordList = function() {
return recordList.getArray(); //Not generally a good idea to simply return your internal object
};
Your solution is fine. Two minor suggestions for improvement
Use a more specific name than control (even 'recordControl' is ok). You may end up with lots of controls for different feature sets.
Use an object literal to create the entire object. Keeps your code tidier and more readable (and saves a few bytes)
(apologies for lack of spacing - editor not doing what I want it to do!)
recordControl = {
record : object.create("record"),
recordList : object.create("recordList"),
save : function() {
this.record.save();
this.recordList.refresh();
}
}
If it's one thing I've learned over time, it is that following any paradigm to the letter will result in more frustration and difficulty than taking the concept as far as you can go and using common sense to dictate your deviations.
That said, your solution will work fine and it's normal to create a container class for multiple objects of varying types that are coupled. If you want a different way to handle it, check out Client Event Pooling. The only thing that I can say about what you've done is to be sure you're using object.create the way it was intended.
Using this method you can create an event, which when triggered will perform a series of other commands that are associated with your event. I have used this with great success in all sorts of applications, from the intended event hooking to simplifying inline javascript injections after a postback.
Good luck.
why don't you provide your recordList into record?
var record = object.create("record");
record.recordList = object.create('recordList');
record.save = function(){
/*
do something
*/
this.recordList.refresh();
}
(I'm using prototype.js here, but I imagine the same holds true across other libraries as well)
I often find myself writing code like this:
var search_box;
Event.observe(window, 'load', function() {
search_box = $('search_box');
});
function doSomething(msg) {
search_box.innerHTML = msg;
}
Rather then writing it simply like this:
function doSomething(msg) {
$('search_box').innerHTML = msg;
}
My intention is to avoid having to traverse the entire DOM searching for the "search_box" element everything I need access to it. So I search for it once on page load and then stick the reference in a global variable. However, I don't recall ever seeing anyone else do this? Am I needlessly making my code more complex?
This is called premature optimization.
You are introducing a global variable to optimize something you have not profiled.
Your assumption that the $ "traverses the DOM" is incorrect. This function is implemented using document.getElementById which is the fastest way to access an element in the DOM.
I suggest coding your javascript using basic programming best practices such as avoiding global variables, and not optimizing without profiling. Once your application is working as expected, then you should profile it (using firebug) and address the area(s) where it is slow.
I usually do the same thing, the reason you don't see it often is probably because you don't see well written code often that's optimized ( nevermind the whole preoptimization is evil thing ) - I say if you can optimize it without headaches then why not?
Realistically speaking though that's a very very trivial DOM lookup, you should only begin to worry if you're iterating through dozens of elements and being vague in the selectors.. so I wouldn't worry too much about it unless you can really notice certain parts of your web page loading rather slowly, in which case you should store the multiple elements you access in the outer scope's variable.
Good:
(function() {
var els = $$('.foo span'); // also better to specify a context but I'm not sure how that's done in Prototype, it's the second param in jQuery.
function foo() {
els.something();
}
els.somethingElse();
})();
Bad:
(function() {
var els = $$('.foo span'); // also better to specify a context but I'm not sure how that's done in Prototype, it's the second param in jQuery.
function foo() {
$$('.foo span').something();
}
$$('.foo span').somethingElse();
})();
I decided to spend a bit of time doing some testing to get some hard data. The answer is that preloading the elements into global variables is twice as efficient as accessing them using the DOM getElementById method. (At least under FF 3.6).
Subsequent accesses to the objects is also more efficient using the global variable method, but only marginally so.