Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I've started to learn HTML/CSS/JS after several years of no coding at all, previously some simple C++/Python/Pascal stuff.
In one of the tutorials, there is a very simple game to do, comparing a user input with a generated random number and displaying a message if both match.
The question is what is a general practice in web-development:
<p>How many fingers am i holding up?</p>
<input id=userInput type=text>
<input id=guessButton type="button" value=Guess!>
<script type="text/javascript">
document.getElementById("guessButton").onclick=function(){
if (document.getElementById("userInput").value == Math.floor(Math.random()*5)+1)
alert ("You've won");
}
</script>
vs
<script type="text/javascript">
document.getElementById("guessButton").onclick=function(){
var numberGuessed=document.getElementById("userInput").value;
var randomNumber=(Math.random()*5);
randomNumber=Math.floor(randomNumber)+1;
if (numberGuessed==randomNumber)
alert ("You've won");
}
</script>
I get that this example is VERY simple, but when it comes to more complicated logic or nested functions is it better to strive for step-by-step clarity or to avoid the creation of unnecessary variables?
Which is faster?
The answer to this one is it doesn't really matter a whole lot. If it's extremely necessary, for the best speed (both downloading off of the internet and running), if you're only using the values once, you'll want no variables. If you're using the values more than once, you'll probably want to define them in a variable. When downloading, the less data, the better. If you're only using it once, defining it as a variable would technically require more data to be downloaded (i.e. an extra var, an extra semicolon maybe), but it's so insignificant it should not be taken into account most of the time. Your Javascript took less than 1/10th of a millisecond to execute, so this should not be a concern most of the time, either.
Which is preferred?
This is more of an opinion-based question, and therefore, not exactly allowed on Stack Overflow, but I think I can safely say that most developers would say what is important is readability. If this means defining the variables, then so be it. If code cannot be read easily, then it's going to be extremely difficult to expand and modify it, even for yourself, down the road. Readability is especially important when working in a team. Ultimately, though, you should do whatever you prefer.
For speed, as people have stated, the difference is negligible. As for best practice, the second is, in my opinion, demonstrably better.
If someone else is using your code, proper variable naming removes the question of "What are they trying to do?" from the list of problems another developer (or you in a few months) has to solve. Expressive variable names make it so that, If I'm trying to debug a function, I can focus my energy on what's "going wrong", not on what's "going on".
In your second code example, without even looking at how your variables are declared, I can instantly understand what you're trying to do, whether it's working as intended or not. I can then put a debugger before the conditional and check that all the values of the variables you've declared make sense based on what you've named them. For example, I can pretty quickly narrow down what might be wrong if one them is a string, null, NaN, or undefined.
The second example, however, forces me to evaluate what you're doing before I can analyze how you're doing it.
It's a similar philosophy to code comments, really, the smaller the ratio of time spent understanding vs debugging, the more maintainable the code will be.
This is one of those style questions for which you'll likely get a gazillion answers. I would prefer the in-line version unless I was going to use the values more than once. There is probably a tiny difference in performance, since you're having to add names to the local namespace and then (eventually) gc them, but I doubt that difference could be measured in any reasonable use-case.
Yes, you can break down a computation into intermediate variables, and this has the advantage not only of readability, but also of debuggability, since that lets you set breakpoints on each line where something is calculated, and examine the values of the variables.
However, you can also pre-calculate variables. In your case, the DOM element will never change, and so it can be pre-calculated outside the event handler:
const userInputElement = document.getElementById("userInput");
document.getElementById("guessButton").onclick=function() {
var numberGuessed = userInputElement.value;
var randomNumber=(Math.random()*5);
randomNumber=Math.floor(randomNumber)+1;
if (numberGuessed==randomNumber)
alert ("You've won");
};
userInputElement needs to not be executed until the DOM is loaded. So this JS should be either at the end of your body, or wrapped in an event handler such as DOMContentLoaded.
Breaking the calculation of the random number down into two steps is probably going overboard. Most programmers would write this in one line:
var randomNumber = Math.floor(Math.random() * 5) + 1;
In this case, another option is to write this as a little separate function. This has several advantages. First, you would be able to re-use that function in other places in your code. Second, you could test it more easily in isolation. Third, the code where you are using it is simplified.
function getRandomNumber(n) {
return Math.floor(Math.random() * n) + 1;
}
Now you can simplify your event handler to
document.getElementById("guessButton").onclick=function() {
var numberGuessed = userInputElement.value;
var randomNumber = getRandomNumber(5);
if (numberGuessed == randomNumber)
alert ("You've won");
};
However, now that you have both pre-calculated the DOM element, and moved the random number calculation into a function, there is less need to calculate variables one-by-one in your event handler, and you can simplify it to:
document.getElementById("guessButton").onclick = function() {
if (userInputElement.value == getRandomNumber(5))
alert ("You've won");
};
Now you have a very simple, readable event handler, which anyone can look at and easily see what is happening. You also have a separate, re-usable random number generator.
Although not directly related to your question, I would strongly recommend that you indent your code properly, and also adhere to style guidelines for things like the use of spaces.
It is also recommended to use addEventListener instead of assigning to the element's onlick attribute:
document.getElementById("guessButton").addEventListener('click', function() {
if (userInputElement.value == getRandomNumber(5))
alert ("You've won");
});
Related
I am learning JS and came across this exercise:
Write a loop which prompts for a number greater than 100.
If the visitor enters another number – ask them to input again.
The loop must ask for a number until either the visitor enters a number greater than 100 or
cancels the input/enters an empty line.
Here we can assume that the visitor only inputs numbers.
There’s no need to implement a special handling for a non-numeric input in this task.
My solution works and was as follows:
let value;
while (true) {
value = prompt('Enter a number greater than 100', 0);
if (value > 100 || value === '');
console.log(value);
break;
}
The MDN solution was this, and though it is shorter and more simple, it seems to accomplish the same task.
let num;
do {
num = prompt("Enter a number greater than 100?", 0);
} while (num <= 100 && num);
Is my solution still valid? Is the MDN one more proper?
I just want to make sure I am understanding things correctly as I go.
If your solution worked before, I think you probably typed in your solution incorrectly here. I am going to assume you meant to write:
if (value > 100 || value === '') {
console.log(value);
break;
}
Since you're just starting out with JS, you will eventually learn that there will be multiple ways you can handle any given coding problem. It will not always be a good answer/wrong answer type of scenario. Sometimes there are multiple ways to accomplish the same thing.
In this example, the MSN solution is better in terms of readability and possibly safety.
The MSN solution creates a while loop with the exit condition identified in the while statement. This loop will exit when that condition is met.
In your solution, the loop will never exit on it's own, the while() statement will always evaluate to 'true'. This loop needs an explicit exit statement, which you provide with the if() condition.
Your method, although it works, is a little bit less safe in terms of code readability and overall maintenance profile. For example, a future developer could by mistake change the if() condition and inadvertently create a never ending loop.
Or, if the loop contained several dozen lines of code, a developer may miss the if condition, and may add some important code after the if condition (such code would not execute when the exit condition is met.)
Yes, this specific sample exercise is trivial, so the code complexity and readability may not matter. But in large enterprise applications with hundreds of lines of code, such code choices carry serious risks with costly implications.
That said, I'll reiterate - as you learn more about JS, you will often find that there are multiple ways of solving any given problem. Sometimes you do want to create an explicit exit condition through an if() statement, on rare occasions you will want to create a never ending loop.
As you explore more complex problems, you will find needs for such solutiosn. So keep learning, keep trying different solutions, and keep asking questions.
I am currently learning JavaScript and I am wondering what is the role of the variables (var).
In the example bellow, on the last two lines we first define a variable "monCompte" in which we call "john.demandeCaissier(1234)". Then we use console.log(monCompte) to print the result on the screen. What I don't understand is why do we first need to define the variable "monCompte" to call "john.demandeCaissier(1234)". Why can't we just do something such as:
console.log(john.demandeCaissier(1234));
Example
function Personne(prenom,nom,age) {
this.prenom = prenom;
this.nom = nom;
this.age = age;
var compteEnBanque = 7500;
this.demandeCaissier = function(mdp) {
if (mdp == 1234) {
return compteEnBanque;
}
else {
return "Mot de passe faux.";
}
};
}
var john = new Personne('John','Smith',30);
var monCompte = john.demandeCaissier(1234);
console.log(monCompte);
Thank you for you answers.
Yes, you can inline your function call and avoid the need for a variable. However, if an error occurs on that line, it becomes harder to debug:
var monCompte = john.demandeCaissier(1234);
console.log(monCompte);
vs
console.log(john.demandeCaissier(1234));
in the second example, there are several different modes of failure that would not be apparent in a debugging session. When split over two lines, some of those failures become easier to track down.
Second, if you wanted to reuse the value returned by john.demandeCaissier(1234) (the author might have shown this), then a variable becomes very useful indeed.
In my opinion, it's a worthy pursuit to perform only a single operation per line. Fluent-style advocates might disagree here, but it really does make debugging considerably easier.
You could definitely do that, but in more complex programs you will need to store variables for several reasons:
Shortening Long Expressions
Imagine if you saw this code somewhere:
console.log((parseInt(parseFloat(lev_c + end_lev_c)) - parseInt(parseFloat(lev_c + start_lev_c)) + 1));
BTW I got that from here
Wouldn't it be so much simpler just to split that expression up into different variables?
Storing Data
Let's say that you take some input from the user. How would you refer to it later? You cannot use a literal value because you don't know what the user entered, so do you just call the input function again? No, because then it would take the input a second time. What you do is you store the input from the user in a variable and refer to it later on in the code. That way, you can retrieve the value at any time in the program.
If you are a beginner, you might not see any use for variables, but when you start writing larger programs you will start to use variables literally in almost every line of code.
Variables exist to store data. They're useful because instead of invoking an operation over and over again, which is criminally inefficient, they allow you to invoke an operation once, and then use that result where necessary.
And that's for all languages, not just JavaScript.
Variables are structures that store some value (or values). They're only that and you could probably do all your code (or the majority of it) without them.
They help you organize and add some readability to your code. Example:
alert(sumNumbers(askNumber()+askNumber()));
takes a lot more effort to read/understand then this:
var firstNumber = askNumber();
var secondNumber = askNumber();
var total = sumNumbers(firstNumber + secondNumber);
alert(total);
Sure it's longer but it's more readable. Of course you don't have to use var for everything, in this case I could just hide the total.
Another common use for variables is "caching" a value.
If you had a function that sums like 1 million values, if you keep calling it for everything, your code would always have to do all that hard work.
On the other hand, if you store it on a variable the first time you call it, every other time you need that value again, you could just use the variable, since its a "copy" of that calculation and the result is already there.
I was doing this test case to see how much using the this selector speeds up a process. While doing it, I decided to try out pre-saved element variables as well, assuming they would be even faster. Using an element variable saved before the test appears to be the slowest, quite to my confusion. I though only having to "find" the element once would immensely speed up the process. Why is this not the case?
Here are my tests from fastest to slowest, in case anyone can't load it:
1
$("#bar").click(function(){
$(this).width($(this).width()+100);
});
$("#bar").trigger( "click" );
2
$("#bar").click(function(){
$("#bar").width($("#bar").width()+100);
});
$("#bar").trigger( "click" );
3
var bar = $("#bar");
bar.click(function(){
bar.width(bar.width()+100);
});
bar.trigger( "click" );
4
par.click(function(){
par.width(par.width()+100);
});
par.trigger( "click" );
I'd have assumed the order would go 4, 3, 1, 2 in order of which one has to use the selector to "find" the variable more often.
UPDATE: I have a theory, though I'd like someone to verify this if possible. I'm guessing that on click, it has to reference the variable, instead of just the element, which slows it down.
Fixed test case: http://jsperf.com/this-vs-thatjames/10
TL;DR: Number of click handlers executed in each test grows because the element is not reset between tests.
The biggest problem with testing for micro-optimizations is that you have to be very very careful with what you're testing. There are many cases where the testing code interferes with what you're testing. Here is an example from Vyacheslav Egorov of a test that "proves" multiplication is almost instantaneous in JavaScript because the testing loop is removed entirely by the JavaScript compiler:
// I am using Benchmark.js API as if I would run it in the d8.
Benchmark.prototype.setup = function() {
function multiply(x,y) {
return x*y;
}
};
var suite = new Benchmark.Suite;
suite.add('multiply', function() {
var a = Math.round(Math.random()*100),
b = Math.round(Math.random()*100);
for(var i = 0; i < 10000; i++) {
multiply(a,b);
}
})
Since you're already aware there is something counter-intuitive going on, you should pay extra care.
First of all, you're not testing selectors there. Your testing code is doing: zero or more selectors, depending on the test, a function creation (which in some cases is a closure, others it is not), assignment as the click handler and triggering of the jQuery event system.
Also, the element you're testing on is changing between tests. It's obvious that the width in one test is more than the width in the test before. That isn't the biggest problem though. The problem is that the element in one test has X click handlers associated. The element in the next test has X+1 click handlers.
So when you trigger the click handlers for the last test, you also trigger the click handlers associated in all the tests before, making it much slower than tests made earlier.
I fixed the jsPerf, but keep in mind that it still doesn't test just the selector performance. Still, the most important factor that skewes the results is eliminated.
Note: There are some slides and a video about doing good performance testing with jsPerf, focused on common pitfalls that you should avoid. Main ideas:
don't define functions in the tests, do it in the setup/preparation phase
keep the test code as simple as possible
compare things that do the same thing or be upfront about it
test what you intend to test, not the setup code
isolate the tests, reset the state after/before each test
no randomness. mock it if you need it
be aware of browser optimizations (dead code removal, etc)
You don't really test the performance between the different techniques.
If you look at the output of the console for this modified test:
http://jsperf.com/this-vs-thatjames/8
You will see how many event listeners are attached to the #bar object.
And you will see that they are not removed at the beginning for each test.
So the following tests will always become slower as the previous ones because the trigger function has to call all the previous callbacks.
Some of this increase in slowness is because the object reference is already found in memory, so the compiler doesn't have to go looking in memory for the variable
$("#bar").click(function(){
$(this).width($(this).width()+100); // Only has to check the function call
}); // each time, not search the whole memory
as opposed to
var bar = $("#bar");
...
bar.click(function(){
bar.width(bar.width()+100); // Has to search the memory to find it
}); // each time it is used
As zerkms said, dereferencing (having to look up the memory reference as I describe above) has some but little effect on the performance
Thus the main source of slowness in difference for the tests you have performed is the fact that the DOM is not reset between each function call. In actuality, a saved selector performs just about as fast as this
Looks like the performance results you're getting has nothing to do with the code. If you look at these edited tests, you can see that having the same code in two of the tests (first and last) yield totally different results.
I don't know, but if I had to guess I would say it is due to concurrency and multithreading.
When you do $(...) you call the jQuery constructor and create a new object that gets stored in the memory. However, when you reference to an existing variable you do not create a new object (duh).
Although I have no source to quote I believe that every javascript event gets called in its own thread so events don't interfere with eachother. By this logic the compiler would have to get a lock on the variable in order to use it, which might take time.
Once again, I am not sure. Very interesting test btw!
I've just finished writing a script for parsing csv data. Having recently installed JShint, it's been badgering me about the re-use of variables. I've been using JS a fair bit lately, but I come from a python background where it's normal to reuse variables. I'm wondering what issues there are with reusing variables in the following two examples:
Loop with a Switch
The following loop steps through the rows on a csv file, and when it passes a certain value in a row, it switches variable "currentSwitch" from false to true. After currentSwitch is tripped, the loop starts to push stuff to an array.
for (f=0; f < data.length; f++){
if (data[f][0] === code){
if (currentSwitch === true){
dataListByCode.push(data[f]);
}
}
else if ((data[f][0]).slice(0,4) === "UNIN"){
var currentSwitch = true;
}
}
Processing Data with Broken Out Functions
I've got a few functions for processing data that it makes sense to keep separate. In the following code, I process with one function, then I process with another.
var dataListByCode = addDivideData(dataListByCode);
var dataListByCode = addBeforeEntriesArray(dataListByCode, invNumber, matterNumber, client, workType);
Can anyone tell me if this is not in line with best practice? Is there anything that could go wrong with either of these (or scenarios like them)?
You don't need to redeclare currentSwtich
var currentSwitch = true;
In fact it really doesn't make any sense to redeclare this variable in the middle of the loop and in most cases it's almost certainly not what you actually want.
Just initialize/declare it once at the beginning of your loop
var currentSwtich;
// or
var currentSwitch = false;
and drop the var when you set it to true:
currentSwitch = true;
Basically what you are doing is creating a brand new variable with the same name as the old one, and throwing away the old one. This isn't really what you want normally.
There is no analogous concept in python because python doesn't require you to declare variables.
The major problem with reusing variables is that:
a.) in bigger code blocks it can get very confusing, especially if you added/removed code ~20 times, and kept reusing same ~5 variables for multiple things
b.) any programmer that knows nothing about code(read: you after couple months/years) will have a much more difficult time grasping the code.
The lower function snippet can be expressed as:
var dataListByCode = addBeforeEntriesArray(addDivideData(dataListByCode), invNumber, matterNumber, client, workType);
which is not that problematic. The breaking up of functions is useless in this case, and if you have many inline function chains that is usually sign that you need to rethink the object/function design.
Here's an implementation details question for JavaScript gurus.
I have a UI with a number of fields in which the values of the fields depend in a complicated fashion on the values of seven bits of inputs. Exactly what should be displayed for any one of the possible 128 values that is changing regularly as users see more of the application?
Right now, I've for this being implemented as a decision tree through an if-then-else comb, but it's brittle under the requirements changes and sort of hard to get right.
One implementation approach I've thought about is to make an array of values from 0x0 to 0x7F and then store a closure at each location --
var tbl; // initialize it with the values
...
tbl[0x42] = function (){ doAThing(); doAnotherThing(); }
and then invoke them with
tbl[bitsIn]();
This, at least makes the decision logic into a bunch of assignments.
Question: is there a better way?
(Update: holy crap, how'd that line about 'ajax iphone tags' get in there? No wonder it was a little puzzling.)
Update
So what happened? Basically I took a fourth option, although similar to the one I've checked. The logic was sufficiently complex that I finally built a Python program to generate a truth table in the server (generating Groovy code, in fact, the host is a Grails application) and move the decision logic into the server completely. Now the JavaScript side simply interprets a JSON object that contains the values for the various fields.
Eventually, this will probably go through one more iteration, and become data in a database table, indexed by the vector of bits.
The table driven part certainly came out to be the way to go; there have already been a half dozen new changes in the specific requirements for display.
I see two options...
Common to both solutions are the following named functions:
function aThing() {}
function anotherThing() {}
function aThirdThing() {}
The switch way
function exec(bits) {
switch(bits) {
case 0x00: aThing(); anotherThing(); break;
case 0x01: aThing(); anotherThing(); aThirdThing(); break;
case 0x02: aThing(); aThirdThing(); break;
case 0x03: anotherThing(); aThirdThing(); break;
...
case 0x42: aThirdThing(); break;
...
case 0x7f: ... break;
default: throw 'There is only 128 options :P';
}
}
The map way
function exec(bits) {
var actions = map[bits];
for(var i=0, action; action=actions[i]; i++)
action();
}
var map = {
0x00: [aThing, anotherThing],
0x01: [aThing, anotherThing, aThirdThing],
0x02: [aThing, aThirdThing],
0x03: [anotherThing, aThirdThing],
...
0x42: [aThirdThing],
...
};
in both cases you'd call
exec(0x42);
Since the situation (as you have described) is so irregular, there doesn't seem to be a better way. Although, I can suggest an improvement to your jump table. You mentioned that you have errors and duplicates. So instead of explicitly assigning them to a closure, you can assign them to named functions so that you don't have to duplicate the explicit closure.
var doAThingAndAnother = function (){ doAThing(); doAnotherThing(); }
var tbl; // initialize it with the values
...
tbl[0x42] = doAThingAndAnother;
tbl[0x43] = doAThingAndAnother;
Not that much of an improvement, but it's the only thing I could think of! You seem to have covered most of the other issues. Since it looks like the requirements change so much, I think you might have to forgo elegance and have a design that is not as elegant, but is still easy to change.
Have you considered generating your decision tree on the server rather than writing it by hand? Use whatever representation is clean, easy to work with, and modify and then compile that to ugly yet efficient javascript for the client side.
A decision tree is fairly easy to represent as data and it is easy to understand and work with as a traditional tree data structure. You can store said tree in whatever form makes sense for you. Validating and modifying it as data should also be straight forward.
Then, when you need to use the decision tree, just compile/serialize it to JavaScript as a big if-the-else, switch, or hash mess. This should also be fairly straight forward and probably a lot easier than trying to maintain a switch with a couple hundred elements.
I've got a rough example of a JavaScript decision tree tool if you want to take a look:
http://jsfiddle.net/danw/h8CFe/