Simple Confusing Loops and Variable working - javascript

In this question,I'm asking how the following snippets work, as it involves weird use of variable:
while (+(+i--)!=0)
{
i-=i++;
}
console.log(i);

Interesting problem... you've tagged it Java, JavaScript and C -- note that while these languages have the same syntax, this question involves very subtle semantics that may (I'm not sure) differ across languages.
Let's break it down:
+(+i--)
The -- postfix decrement operator is most tightly bound. So this is equivalent to +(+(i--)). That is therefore equivalent to the value of +(+i) (that is, i), but it also decrements i after taking the value. It compares the value with 0 to see if the loop should continue. Thus, while (+(+i--)!=0) is equivalent to the following:
while (i-- != 0)
Note that it also performs i-- at the end of the loop.
Inside the loop, I believe you have some undefined behaviour, at least in C, because you are referencing i on the right, and also updating i on the left -- I believe that C doesn't define which order to do that in. So your results will probably vary from compiler to compiler. Java, at least, is consistent, so I'll give the Java answer. i-=i++ is equivalent i = i - i++, which is equivalent to to reading all the values out of the expression, computing the result of the expression, applying the post-increment, and then assigning the result back. That is:
int t = i - i; // Calculate the result of the expression "i - i++"
i++; // Post-increment i
i = t; // Store the result back
Clearly, this is the same as writing i = 0. So the body of the loop sets i to 0.
Thus, the loop executes just one time, setting i to 0. Then, it decrements i one more time on the next while loop, but fails the check because i (before decrementing) == 0.
Hence, the final answer is -1, no matter what the initial value for i is.
To put this all together and write an equivalent program:
while (i-- != 0)
{
int t = i - i;
i++;
i = t;
}
console.log(i);
When I tried it in Java and JavaScript, that's what I got. For GCC (C compiler), it gives -1 only when i starts out as 0. If i starts out as anything else, it goes into an infinite loop.
That's because in GCC (not necessarily all C compilers), i-=i++ has a different meaning: it does the store back to i first, then does the post-increment. Therefore, it is equivalent to this:
int t = i - i; // Calculate the result of the expression "i - i++"
i = t; // Store the result back
i++; // Post-increment i
That's equivalent to writing i = 1. Therefore, on the first iteration, it sets i to 1, and then on the loop, it checks whether i == 0, and it isn't, so it goes around again, always setting i to 1. This will never terminate, but for the special case where i starts out as 0; then it will always terminate the loop and decrement i (so you get -1).
Another C compiler may choose to act like Java instead. This shows that you should never write code which both assigns and postincrements the same variable: you never know what will happen!
Edit: I tried to be too clever using that for loop; that wasn't equivalent. Turned back into a while loop.

That's soooo wierd! (I love it)
first, you can forget about the +(+...) part, it's just like saying 1 + (+1) = 2.
i-- means i = i - 1. In your while condition, you test if i-- is different from 0. Note: the test is made on i != 0 and then i's value is changed. If you wanted to change its value before the test, you should have used --i instead.
As for the i -= i++, it's a very dumb way to say i = 0. You must read it from right to left: i = i + 1 and then i = i - i1 (whatever value of i you have, you'll end up with 0.
Simplier quivalent snippet:
while (i-- != 0) {
i = 0;
}
console.log(i);
1 a -= b means a = a - b.

i -= i++ would mean a similar thing to i = i-i; i++ (if you make the -= explicit).
In a similar fashion, you can pull the side effect of i-- out of the loop condition by
transforming while (foo(i--)) { ... } to while (foo(i)) { i--; ...} i--; (you need to put it both in the loop body and after the loop because, at the end, the condition will be false and the loop body will not be executed but execution continues after the while loop).

The while condition evaluation happens based on operator precedence. I have used explicit braces to help understand the evaluation:
(+(+(i--)) != 0) which is equivalent to using (i-- != 0) as the '+' are just unary operators.
The expression i -=i++; is equivalent to i = i - i++; where the LHS expression gets evaluated from left to right.
i = 4;
while (+(+i--)!=0) //here i would be decremented once to 3.
{
i-=i++; // here i = 3 - 3 = 0; even though i has been incremented by 1 its value is set to 0 with this assignment
}

This is very simple. And I think the only reason to code like this is a concept called "code obfucasion" or "code confusing". This way makes the code harder to read and debug, so that can prevent from reverse engineer your code :-)

Related

basic Javascript While loop and boolean value questions

I would ask my instructor, but whenever I do, he gives me an even more vague answer to my questions, so I'm asking yall for help. We "learned" (i.e. watched videos) about for and while loops and I get it, I feel like I do, but whenever it comes to doing the assignments given, I feel like they don't make sense. Like back in math class in high school, they'd teach you about the problems, but then when it came time to do your homework, the problems were completely different from what you just learned about. For instance, it says the basic while loop structure is:
while(condition is true) {
//do something
}
But then in this assignment, it gives me:
// Another way to write a while loop is to have a boolean variable
// where the condition goes and then test every time if you need to
// change the boolean to false.
// Below we have a variable lessThan5 and it is set to true.
// Create a loop that tests if our variable 'j' is less than 5.
// If it is less than 5 then Increment it by 1. If it is not
// less than 5 then set our lessThan5 variable to be false.
let lessThan5 = true;
let j = 0;
while(lessThan5) {
}
We didn't learn anything about using boolean values in while loops and I feel like I'm meant to infer what to do, and what structure to use and I just have no idea. Aside from the fact I feel like the instructions to many of these questions are poorly worded, which only confuses me more!
So then there's this third one:
// Example of what the number game would look like:
// Couple things to note:
// Math is a built in object in javascript.
// Math.round() will round a decimal number to a whole number.
// Math.random() returns a decimal number between 0 to 1.
// (But not including 1)
function guessNumberGame(guess) {
let guessing = true;
let number = Math.round(Math.random() * 100);
while(guessing) {
if(guess === number) {
guessing = false;
} else {
guess = Number(prompt("That number didn't work. Try again: "));
}
}
}
// Problem 3
// We will give you a number through the 'num' parameter
// Create a while loop that will loop 'num' amount of times.
// For example if num is 3 then your while loop should loop 3 times
// If num is 20 then the loop should loop 20 times.
// Increment k every loop.
let k = 0;
function keepLooping(num) {
}
If this Problem 3 is meant to be related somehow to the number game example, I can't see it. I don't even know what it is I need to be asking. Does this make any sense to anyone? And nobody else is publicly asking questions about any of this, and it's making me feel stupid and like I am the only one too dumb to get what's going on. I was doing really well and ahead of schedule with all this until this point, but just none of this is making any sense to me.
Welcome to programming, JavaScript (JS), and StackOverflow (SO)!
Let's dive into this a little deeper. First, a quick JavaScript primer: in JavaScript, everything can be classified as either an expression or a statement. At a super high and not-technical level:
expression: something that produces a value
statement: an instruction to the computer
(For a much longer explanation, see here)
Often, statements have slots that can take expressions. Loops are a great example of that.
For example, 1 + 1 is an expression, since it produces the value 2. Even more simply, 1 on its own is also an expression, since it produces the value 1. while(/*some expression here*/) is a statement that has a slot for an expression. for(s1, e2, e3) is also a statement that has slots for statements and slots.
So, the while loop acts on an expression, and will continue to loop as long as the value returned by that expression is truthy. truthy and falsey is an interesting concept in JavaScript and can be a whole essay on it's own, but the tl;dr of it is that anything that == true is truthy, and anything that == false is falsey
So for your first question, 0 < 5 == true, while 5 < 5 == false. Thus, if you make the value of j be greater than or equal to 5, the loop will break.
let lessThan5 = true;
let j = 0;
while(lessThan5) {
// For each cycle of the loop, check if `j` is less than 5
if (j < 5) {
// If `j` is less than 5, increment it
j++; // This is equivalent to saying j = j + 1, or j += 1
} else {
// If `j` is not less than 5, set `lessThan5` to `false`
// Not when the loop goes to iterate again, `false == false`, and it stops
lessThan5 = false;
}
}
I think given the above you should be able to solve the third problem. Please let us know if you have trouble with it, show us what you try, and we'll be happy to help some more :)
Let's take a deep breath and relax. I'm a very senior developer and can't tell -- from your examples -- what's going on here. Maybe that's because your instructor is terrible, maybe it's because you've missed some context in your class, and so it's omitted from the question.
I can answer the two questions you've been given. Hopefully it'll be helpful.
First:
I do not know why your materials claim that a while loop might be written this way. I've completed the assignment, but it seems very odd. But if they want you to complete it, here's a solution.
// Another way to write a while loop is to have a boolean variable
// where the condition goes and then test every time if you need to
// change the boolean to false.
// Below we have a variable lessThan5 and it is set to true.
// Create a loop that tests if our variable 'j' is less than 5.
// If it is less than 5 then Increment it by 1. If it is not
// less than 5 then set our lessThan5 variable to be false.
let lessThan5 = true;
let j = 0;
while(lessThan5) {
if (j >= 5) {
lessThan5 = false;
} else {
j++;
}
}
Moving on to the second snippet, the second snippet does not, to me, appear to be related to guessNumberGame in any way.
And the solution to "Problem 3" seems useless to me. A loop that doesn't do anything is not useful in real life.
That said, the solution to "Problem 3" is as follows:
// Problem 3
// We will give you a number through the 'num' parameter
// Create a while loop that will loop 'num' amount of times.
// For example if num is 3 then your while loop should loop 3 times
// If num is 20 then the loop should loop 20 times.
// Increment k every loop.
let k = 0;
function keepLooping(num) {
while(k < num) {
k++;
}
}

Why does i + 2 result in an infinite loop where i += 2 doesn't? [duplicate]

I am at a loss how best to approach for loops in JavaScript. Hopefully an understanding of for loops will help shed light on the other types of loops.
Sample code
for (var i=0; i < 10; i=i+1) {
document.write("This is number " + i);
}
My understanding is that when i has been initialized, it starts with the value of 0 which then evaluated against the condition < 10. If it is less than 10, it the executes the statement document.write("This is number + i); Once it has executed the preceding statement, only then does it increment the next value by 1.
Guides I have consulted:
http://www.functionx.com/javascript/Lesson11.htm
http://www.cs.brown.edu/courses/bridge/1998/res/javascript/javascript-tutorial.html#10.1
http://www.tizag.com/javascriptT/javascriptfor.php
Now the guide at http://www.functionx.com/javascript/Lesson11.htm seems to indicate otherwise i.e.
To execute this loop, the Start condition is checked. This is usually
the initial value where the counting should start. Next, the Condition
is tested; this test determines whether the loop should continue. If
the test renders a true result, then the Expression is used to modify
the loop and the Statement is executed. After the Statement has been
executed, the loop restarts.
The line that throws me is "If the test renders a true result, then the Expression is used to modify the loop and the Statement is executed". It seems to imply that because 0 is less than 10, increment expression is modified which would be 0 + 1 and THEN the statement, e.g. document.write is executed.
My problem
What is the correct way to interpret for loops? Is my own comprehension correct? Is the same comprehension applicable to other programming languages e.g. PHP, Perl, Python, etc?
Think of a for loop as the following
for(initializers; condition; postexec) {
execution
}
When the loop is first started the code var i = 0 is run. This initializes the variable that you will be testing for inside the loop
Next the loop evaluates the i < 10 expression. This returns a boolean value which will be true for the first 10 times it is run. While this expression keeps evaluating to true the code inside the loop is run.
document.write("This is number " + i);
Each time after this code is run the last part of the loop i++ is executed. This code in this example adds 1 to i after each execution.
After that code is executed the condition of the loop is check and steps 2 and 3 repeat until finally the condition is false in which case the loop is exited.
This the way loops work in the languages you mentioned.
Lets have a look at the corresponding section in the ECMAScript specification:
The production
IterationStatement : for ( var VariableDeclarationListNoIn ; Expressionopt ; Expressionopt) Statement
is evaluated as follows:
1. Evaluate VariableDeclarationListNoIn.
2. Let V = empty.
3. Repeat
a. If the first Expression is present, then
i. Let testExprRef be the result of evaluating the first Expression.
ii. If ToBoolean(GetValue(testExprRef)) is false,
return (normal, V, empty).
b. Let stmt be the result of evaluating Statement.
...
f. If the second Expression is present, then
i. Let incExprRef be the result of evaluating the second Expression.
ii. Call GetValue(incExprRef). (This value is not used.)
As you can see, in step 1, the variable assignment is evaluated. In step 3a, the condition is tested. In step 3b, the loop body is evaluated, and after that the third expression is evaluated in step 3f.
Therefore your understanding of the for loop is correct.
It is to assume that it works the same way in other languages, since the for loop is such a common statement in programming languages (note that Python does not have such a statement). But if you want to be absolutely certain, you better consult their specification as well.
Your quoted source is wrong, and we can prove it...
The basis of the for loop has four separate blocks which may be executed:
for(initialise; condition; finishediteration) { iteration }
Fortunately we can execute a function in each of these blocks. Therefore we can create four functions which log to the console when they execute like so:
var initialise = function () { console.log("initialising"); i=0; }
var condition = function () { console.log("conditioning"); return i<5; }
var finishediteration = function () { console.log("finished an iteration"); i++; }
var doingiteration = function () { console.log("doing iteration when `i` is equal", i); }
Then we can run the following, which places the above functions into each block:
for (initialise(); condition(); finishediteration()) {
doingiteration();
}
Kaboom. Works.
If you viewing this page using Safari on the Mac then you can AppleAlt + I and copy the above two snippets, in order, into the console and see the result.
EDIT, extra info....
Also... the finished iteration block is optional. For example:
for (var i=0; i<10;) {
console.log(i); i++;
};
does work.
The second reference is wrong. Your explanation is correct.
Another way to think about it, if this helps you:
var i = 0;
while (i < 10) {
document.write("This is number " + i);
i++;
}
This is for statement syntax:
for(initalize, condition, increment) {
Do_some_things();
}
initalize Will executed only one time when for begin then it execute Do_some_things(); statement, and while condition still true it will execute increment and then Do_some_things();. if co condition false, for would exit.
for (var i=0; i < 10; i=i+1) {
document.write("This is number " + i);
}
var i=0 will execute one time (initalize).
i < 10 condition was always checked after a loop.
i=i+1 will execute after check i < 10 and result is true.
Value of i is: 0, 1, 3, 4, 5, 6, 7, 8, 9 (10 times loop)

Ternary Operator use to increase variable

Is it a good practice to use the ternary operator for this:
answersCounter = answer.length != 0 ? ++answersCounter : answersCounter;
This is a question that I always asked myself as it happens quite often. Or, is it better to use a normal if statement? For me, this looks much cleaner in one line.
This is just opinion, but I think that writing the increment like you have it is somewhat poor style.
Assigning a variable to a pre-incremented version of itself is a little bit confusing. To me, the best code is the clearest (excepting nods to optimization where necessary), and sometimes brevity leads to clarity and sometimes it does not (see anything written in Perl... I kid, sorta).
Have you ever had the programming trick question of:
int i = 5;
i += i++ + i;
Or something similar? And you think to yourself who would ever need to know how that works out since when would you ever assign a variable to the pre/post increment version of itself? I mean, you would never ever see that in real code, right?
Well, you just provided an example. And while it is parseable, it is not idiomatic and not clearer than a straight forward if.
E.g.
if (answer.length != 0) answersCounter++;
Of course, some people don't like if statements with out braces, and don't like braces without newlines, which is probably how you ended up with the ternary. Something with the coding style needs to be re-evaluated though if it is resulting in (subjectively) worse code to avoid a few carriage returns.
Again, this is opinion only, and certainly not a rule.
For Javascript
As it's unclear whether OP is asking about Java, JavaScript or genuinely both.
Also know this is an old question but I've been playing with this and ended up here so thought it worth sharing.
The following does nothing, as incrementers within ternary operators don't work as expected.
let i = 0;
const test = true;
i = test ? i++ : i--;
console.log(i) // 0
Switching ++ to +1 and -- to -1 does work.
However it conceptually is a little strange. We are creating an increment of the variable, then assigning that incremented variable back to the original. Rather than incrementing the variable directly.
let i = 0;
const test = true;
i = test ? i+1 : i-1;
console.log(i) // 1
You can also use the logical operators && and ||.
However I personally find this harder to read and know for sure what will be output without testing it.
let i = 0;
const test = true;
i = test && i+1 || i-1;
console.log(i) // 1
But at the end of the day as commented above, an if else statement seems to be the clearest representation.
This increments the variable directly, and if brevity is the aim then it can still all go on one line.
let i = 0;
const test = true;
if (test) { i++ } else { i-- }
console.log(i) // 1

JavaScript: What will break, and what will break it? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Potentially dangerous code below, line 2
Repeat: POTENTIALLY DANGEROUS CODE BELOW
var history = "";
for (start = 3; start = 1000; start += 1){
if(start % 5 == 0 || start % 3 == 0)
history += start + " "; }
Okay, this is the tenth time I've put JavaScript code in that's frozen my browser. It's putting my computer in shock. Are these panic attacks going to destroy her heart? Where can I learn about all the crap that might break my computer as I continue to learn and practice JavaScript? I'm looking for an exhaustive list, only.
Your loop: for (start = 3; start = 1000; start += 1){
The second part of a for( ; ; ) loop is the condition test. The loop will continue until the second part evaluates to false. To not create an infinite loop, change your code to:
for (var start = 3; start < 1000; start += 1){
Note: start+=1 is equal to start++. If you want a compact code, you can replace +=1 by ++.
An overview of the three-part for-loop, for(initialise; condition; increment):
initialise - Create variables (allowed to be empty)
condition - The loop will stop once this expression evaluates to false
increment - This expression is executed at the end of the loop
Always check against infinite loops: Make sure that the condition is able to evaluate to false.
Commonly made mistakes:
A negative incremental expression in conjunction with a is-lower-than comparison:
i-- decreases the counter, so i<100 will always be true (unless the variable i is initialized at 100)
A positive incremental expression in conjunction with a is-higher-than comparison.
A non-incrementing expression: for(var i=0,j=0; i<100; j++) (i doesn't increase)
A condition which is always true (such as in your case)
You just have to learn and read about it properly. Your loop condition start = 1000 will always evaluate to true, that's why the loop never terminates (an assignment returns the value which was assigned and any other number than 0 is evaluates to true).
The MDN JavaScript Guide is a great resource for learning JavaScript. Particular for this situation:
A for loop repeats until a specified condition evaluates to false. The JavaScript for loop is similar to the Java and C for loop. A for statement looks as follows:
for ([initialExpression]; [condition]; [incrementExpression])
statement
When a for loop executes, the following occurs:
The initializing expression initialExpression, if any, is executed. This expression usually initializes one or more loop counters, but the syntax allows an expression of any degree of complexity. This expression can also declare variables.
The condition expression is evaluated. If the value of condition is true, the loop statements execute. If the value of condition is false, the for loop terminates. If the condition expression is omitted entirely, the condition is assumed to be true.
The statement executes. To execute multiple statements, use a block statement ({ ... }) to group those statements.
The update expression incrementExpression, if there is one, executes, and control returns to step 2.
As the others said, it mostly comes down to try and error.... that is a good way of learning anyway.
your conditional statement start=1000 will always return true. You cant found such fool proof's list for this, you have to learn from these mistakes on your own.
Uh - what did you wanted here ?
for (start = 3; start = 1000; start += 1)
do you wanted this ? ( from 3 to 1000 )
for (start = 3; start <= 1000; start += 1)
in first case you will stuck on 1000

JavaScript Infinitely Looping slideshow with delays?

How do I make an infinite loop in JavaScript? I'm trying to make a slideshow, which I have working, but I can't get it to loop. I can't even get it to loop twice.
The code I'm using right now is
window.onload = function start() {
slide();
}
function slide() {
var num = 0;
for (num=0;num<=10;num++) {
setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
}
}
Without the for thing in there, it does go through once. When I put in a for, it either makes Firefox lock up, or just loops once. I'm sure this is a really simple thing to do, and even if it has to be loop 1,000,000 times or something instead of infinite, that'd work fine for me.
Also, I don't want to use jQuery or something that someone else created. I'm learning JavaScript, and this is partially to help me learn, and partially because I'm trying to make as many HTML5-based systems as I can.
EDIT: I think the reason it's freezing is because it executes the code all at once, and then just stores it in a cache or something. What I want it to do is go through this once, then start at the top again, which is what I've always thought loops where for. In "batch" (command prompt) scripting, this could be done with a "GOTO" command. I don't know if there's an equivalent in JS or not, but that's really my goal.
The correct approach is to use a single timer. Using setInterval, you can achieve what you want as follows:
window.onload = function start() {
slide();
}
function slide() {
var num = 0, style = document.getElementById('container').style;
window.setInterval(function () {
// increase by num 1, reset to 0 at 4
num = (num + 1) % 4;
// -600 * 1 = -600, -600 * 2 = -1200, etc
style.marginLeft = (-600 * num) + "px";
}, 3000); // repeat forever, polling every 3 seconds
}
You don't want while(true), that will lock up your system.
What you want instead is a timeout that sets a timeout on itself, something like this:
function start() {
// your code here
setTimeout(start, 3000);
}
// boot up the first call
start();
Here's a nice, tidy solution for you: (also see the live demo ->)
window.onload = function start() {
slide();
}
function slide() {
var currMarg = 0,
contStyle = document.getElementById('container').style;
setInterval(function() {
currMarg = currMarg == 1800 ? 0 : currMarg + 600;
contStyle.marginLeft = '-' + currMarg + 'px';
}, 3000);
}
Since you are trying to learn, allow me to explain how this works.
First we declare two variables: currMarg and contStyle. currMarg is an integer that we will use to track/update what left margin the container should have. We declare it outside the actual update function (in a closure), so that it can be continuously updated/access without losing its value. contStyle is simply a convenience variable that gives us access to the containers styles without having to locate the element on each interval.
Next, we will use setInterval to establish a function which should be called every 3 seconds, until we tell it to stop (there's your infinite loop, without freezing the browser). It works exactly like setTimeout, except it happens infinitely until cancelled, instead of just happening once.
We pass an anonymous function to setInterval, which will do our work for us. The first line is:
currMarg = currMarg == 1800 ? 0 : currMarg + 600;
This is a ternary operator. It will assign currMarg the value of 0 if currMarg is equal to 1800, otherwise it will increment currMarg by 600.
With the second line, we simply assign our chosen value to containers marginLeft, and we're done!
Note: For the demo, I changed the negative values to positive, so the effect would be visible.
Perhps this is what you are looking for.
var pos = 0;
window.onload = function start() {
setTimeout(slide, 3000);
}
function slide() {
pos -= 600;
if (pos === -2400)
pos = 0;
document.getElementById('container').style.marginLeft= pos + "px";
setTimeout(slide, 3000);
}
You are calling setTimeout() ten times in a row, so they all expire almost at the same time. What you actually want is this:
window.onload = function start() {
slide(10);
}
function slide(repeats) {
if (repeats > 0) {
document.getElementById('container').style.marginLeft='-600px';
document.getElementById('container').style.marginLeft='-1200px';
document.getElementById('container').style.marginLeft='-1800px';
document.getElementById('container').style.marginLeft='0px';
window.setTimeout(
function(){
slide(repeats - 1)
},
3000
);
}
}
This will call slide(10), which will then set the 3-second timeout to call slide(9), which will set timeout to call slide(8), etc. When slide(0) is called, no more timeouts will be set up.
You can infinitely loop easily enough via recursion.
function it_keeps_going_and_going_and_going() {
it_keeps_going_and_going_and_going();
}
it_keeps_going_and_going_and_going()
The key is not to schedule all pics at once, but to schedule a next pic each time you have a pic shown.
var current = 0;
var num_slides = 10;
function slide() {
// here display the current slide, then:
current = (current + 1) % num_slides;
setTimeout(slide, 3000);
}
The alternative is to use setInterval, which sets the function to repeat regularly (as opposed to setTimeout, which schedules the next appearance only.
Expanding on Ender's answer, let's explore our options with the improvements from ES2015.
First off, the problem in the asker's code is the fact that setTimeout is asynchronous while loops are synchronous. So the logical flaw is that they wrote multiple calls to an asynchronous function from a synchronous loop, expecting them to execute synchronously.
function slide() {
var num = 0;
for (num=0;num<=10;num++) {
setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
}
}
What happens in reality, though, is that...
The loop "simultaneously" creates 44 async timeouts set to execute 3, 6, 9 and 12 seconds in the future. Asker expected the 44 calls to execute one-after-the-other, but instead, they all execute simultaneously.
3 seconds after the loop finishes, container's marginLeft is set to "-600px" 11 times.
3 seconds after that, marginLeft is set to "-1200px" 11 times.
3 seconds later, "-1800px", 11 times.
And so on.
You could solve this by changing it to:
function setMargin(margin){
return function(){
document.querySelector("#container").style.marginLeft = margin;
};
}
function slide() {
for (let num = 0; num <= 10; ++num) {
setTimeout(setMargin("-600px"), + (3000 * (num + 1)));
setTimeout(setMargin("-1200px"), + (6000 * (num + 1)));
setTimeout(setMargin("-1800px"), + (9000 * (num + 1)));
setTimeout(setMargin("0px"), + (12000 * (num + 1)));
}
}
But that is just a lazy solution that doesn't address the other issues with this implementation. There's a lot of hardcoding and general sloppiness here that ought to be fixed.
Lessons learnt from a decade of experience
As mentioned at the top of this answer, Ender already proposed a solution, but I would like to add on to it, to factor in good practice and modern innovations in the ECMAScript specification.
function format(str, ...args){
return str.split(/(%)/).map(part => (part == "%") ? (args.shift()) : (part)).join("");
}
function slideLoop(margin, selector){
const multiplier = -600;
let contStyle = document.querySelector(selector).style;
return function(){
margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);
}
}
function slide() {
return setInterval(slideLoop(0, "#container"), 3000);
}
Let's go over how this works for the total beginners (note that not all of this is directly related to the question):
format
function format
It's immensely useful to have a printf-like string formatter function in any language. I don't understand why JavaScript doesn't seem to have one.
format(str, ...args)
... is a snazzy feature added in ES6 that lets you do lots of stuff. I believe it's called the spread operator. Syntax: ...identifier or ...array. In a function header, you can use it to specify variable arguments, and it will take every argument at and past the position of said variable argument, and stuff them into an array. You can also call a function with an array like so: args = [1, 2, 3]; i_take_3_args(...args), or you can take an array-like object and transform it into an array: ...document.querySelectorAll("div.someclass").forEach(...). This would not be possible without the spread operator, because querySelectorAll returns an "element list", which isn't a true array.
str.split(/(%)/)
I'm not good at explaining how regex works. JavaScript has two syntaxes for regex. There's the OO way (new RegExp("regex", "gi")) and there's the literal way (/insert regex here/gi). I have a profound hatred for regex because the terse syntax it encourages often does more harm than good (and also because they're extremely non-portable), but there are some instances where regex is helpful, like this one. Normally, if you called split with "%" or /%/, the resulting array would exclude the "%" delimiters from the array. But for the algorithm used here, we need them included. /(%)/ was the first thing I tried and it worked. Lucky guess, I suppose.
.map(...)
map is a functional idiom. You use map to apply a function to a list. Syntax: array.map(function). Function: must return a value and take 1-2 arguments. The first argument will be used to hold each value in the array, while the second will be used to hold the current index in the array. Example: [1,2,3,4,5].map(x => x * x); // returns [1,4,9,16,25]. See also: filter, find, reduce, forEach.
part => ...
This is an alternative form of function. Syntax: argument-list => return-value, e.g. (x, y) => (y * width + x), which is equivalent to function(x, y){return (y * width + x);}.
(part == "%") ? (args.shift()) : (part)
The ?: operator pair is a 3-operand operator called the ternary conditional operator. Syntax: condition ? if-true : if-false, although most people call it the "ternary" operator, since in every language it appears in, it's the only 3-operand operator, every other operator is binary (+, &&, |, =) or unary (++, ..., &, *). Fun fact: some languages (and vendor extensions of languages, like GNU C) implement a two-operand version of the ?: operator with syntax value ?: fallback, which is equivalent to value ? value : fallback, and will use fallback if value evaluates to false. They call it the Elvis Operator.
I should also mention the difference between an expression and an expression-statement, as I realize this may not be intuitive to all programmers. An expression represents a value, and can be assigned to an l-value. An expression can be stuffed inside parentheses and not be considered a syntax error. An expression can itself be an l-value, although most statements are r-values, as the only l-value expressions are those formed from an identifier or (e.g. in C) from a reference/pointer. Functions can return l-values, but don't count on it. Expressions can also be compounded from other, smaller expressions. (1, 2, 3) is an expression formed from three r-value expressions joined by two comma operators. The value of the expression is 3. expression-statements, on the other hand, are statements formed from a single expression. ++somevar is an expression, as it can be used as the r-value in the assignment expression-statement newvar = ++somevar; (the value of the expression newvar = ++somevar, for example, is the value that gets assigned to newvar). ++somevar; is also an expression-statement.
If ternary operators confuse you at all, apply what I just said to the ternary operator: expression ? expression : expression. Ternary operator can form an expression or an expression-statement, so both of these things:
smallest = (a < b) ? (a) : (b);
(valueA < valueB) ? (backup_database()) : (nuke_atlantic_ocean());
are valid uses of the operator. Please don't do the latter, though. That's what if is for. There are cases for this sort of thing in e.g. C preprocessor macros, but we're talking about JavaScript here.
args.shift()
Array.prototype.shift. It's the mirror version of pop, ostensibly inherited from shell languages where you can call shift to move onto the next argument. shift "pops" the first argument out of the array and returns it, mutating the array in the process. The inverse is unshift. Full list:
array.shift()
[1,2,3] -> [2,3], returns 1
array.unshift(new-element)
[element, ...] -> [new-element, element, ...]
array.pop()
[1,2,3] -> [1,2], returns 3
array.push(new-element)
[..., element] -> [..., element, new-element]
See also: slice, splice
.join("")
Array.prototype.join(string). This function turns an array into a string. Example: [1,2,3].join(", ") -> "1, 2, 3"
slide
return setInterval(slideLoop(0, "#container"), 3000);
First off, we return setInterval's return value so that it may be used later in a call to clearInterval. This is important, because JavaScript won't clean that up by itself. I strongly advise against using setTimeout to make a loop. That is not what setTimeout is designed for, and by doing that, you're reverting to GOTO. Read Dijkstra's 1968 paper, Go To Statement Considered Harmful, to understand why GOTO loops are bad practice.
Second off, you'll notice I did some things differently. The repeating interval is the obvious one. This will run forever until the interval is cleared, and at a delay of 3000ms. The value for the callback is the return value of another function, which I have fed the arguments 0 and "#container". This creates a closure, and you will understand how this works shortly.
slideLoop
function slideLoop(margin, selector)
We take margin (0) and selector ("#container") as arguments. The margin is the initial margin value and the selector is the CSS selector used to find the element we're modifying. Pretty straightforward.
const multiplier = -600;
let contStyle = document.querySelector(selector).style;
I've moved some of the hard coded elements up. Since the margins are in multiples of -600, we have a clearly labeled constant multiplier with that base value.
I've also created a reference to the element's style property via the CSS selector. Since style is an object, this is safe to do, as it will be treated as a reference rather than a copy (read up on Pass By Sharing to understand these semantics).
return function(){
margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);
}
Now that we have the scope defined, we return a function that uses said scope. This is called a closure. You should read up on those, too. Understanding JavaScript's admittedly bizarre scoping rules will make the language a lot less painful in the long run.
margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);
Here, we simply increment margin and modulus it by 4. The sequence of values this will produce is 1->2->3->0->1->..., which mimics exactly the behavior from the question without any complicated or hard-coded logic.
Afterwards, we use the format function defined earlier to painlessly set the marginLeft CSS property of the container. It's set to the currnent margin value multiplied by the multiplier, which as you recall was set to -600. -600 -> -1200 -> -1800 -> 0 -> -600 -> ...
There are some important differences between my version and Ender's, which I mentioned in a comment on their answer. I'm gonna go over the reasoning now:
Use document.querySelector(css_selector) instead of document.getElementById(id)
querySelector was added in ES6, if I'm not mistaken. querySelector (returns first found element) and querySelectorAll (returns a list of all found elements) are part of the prototype chain of all DOM elements (not just document), and take a CSS selector, so there are other ways to find an element than just by its ID. You can search by ID (#idname), class (.classname), relationships (div.container div div span, p:nth-child(even)), and attributes (div[name], a[href=https://google.com]), among other things.
Always track setInterval(fn, interval)'s return value so it can later be closed with clearInterval(interval_id)
It's not good design to leave an interval running forever. It's also not good design to write a function that calls itself via setTimeout. That is no different from a GOTO loop. The return value of setInterval should be stored and used to clear the interval when it's no longer needed. Think of it as a form of memory management.
Put the interval's callback into its own formal function for readability and maintainability
Constructs like this
setInterval(function(){
...
}, 1000);
Can get clunky pretty easily, especially if you are storing the return value of setInterval. I strongly recommend putting the function outside of the call and giving it a name so that it's clear and self-documenting. This also makes it possible to call a function that returns an anonymous function, in case you're doing stuff with closures (a special type of object that contains the local state surrounding a function).
Array.prototype.forEach is fine.
If state is kept with the callback, the callback should be returned from another function (e.g. slideLoop) to form a closure
You don't want to mush state and callbacks together the way Ender did. This is mess-prone and can become hard to maintain. The state should be in the same function that the anonymous function comes from, so as to clearly separate it from the rest of the world. A better name for slideLoop could be makeSlideLoop, just to make it extra clear.
Use proper whitespace. Logical blocks that do different things should be separated by one empty line
This:
print(some_string);
if(foo && bar)
baz();
while((some_number = some_fn()) !== SOME_SENTINEL && ++counter < limit)
;
quux();
is much easier to read than this:
print(some_string);
if(foo&&bar)baz();
while((some_number=some_fn())!==SOME_SENTINEL&&++counter<limit);
quux();
A lot of beginners do this. Including little 14-year-old me from 2009, and I didn't unlearn that bad habit until probably 2013. Stop trying to crush your code down so small.
Avoid "string" + value + "string" + .... Make a format function or use String.prototype.replace(string/regex, new_string)
Again, this is a matter of readability. This:
format("Hello %! You've visited % times today. Your score is %/% (%%).",
name, visits, score, maxScore, score/maxScore * 100, "%"
);
is much easier to read than this horrific monstrosity:
"Hello " + name + "! You've visited " + visits + "% times today. " +
"Your score is " + score + "/" + maxScore + " (" + (score/maxScore * 100) +
"%).",
edit: I'm pleased to point out that I made in error in the above snippet, which in my opinion is a great demonstration of how error-prone this method of string building is.
visits + "% times today"
^ whoops
It's a good demonstration because the entire reason I made that error, and didn't notice it for as long as I did(n't), is because the code is bloody hard to read.
Always surround the arguments of your ternary expressions with parens. It aids readability and prevents bugs.
I borrow this rule from the best practices surrounding C preprocessor macros. But I don't really need to explain this one; see for yourself:
let myValue = someValue < maxValue ? someValue * 2 : 0;
let myValue = (someValue < maxValue) ? (someValue * 2) : (0);
I don't care how well you think you understand your language's syntax, the latter will ALWAYS be easier to read than the former, and readability is the the only argument that is necessary. You read thousands of times more code than you write. Don't be a jerk to your future self long-term just so you can pat yourself on the back for being clever in the short term.
Here:
window.onload = function start() {
slide();
}
function slide() {
var num = 0;
for (num=0;num==10;) {
setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
}
}
That makes it keep looping alright! That's why it isn't runnable here.
try this:
window.onload = function start() {
slide();
}
function slide() {
setInterval("document.getElementById('container').style.marginLeft='-600px'",3000);
setInterval("document.getElementById('container').style.marginLeft='-1200px'",6000);
setInterval("document.getElementById('container').style.marginLeft='-1800px'",9000);
setInterval("document.getElementById('container').style.marginLeft='0px'",12000);
}
setInterval is basically an 'infinite loop' and it wont black up the browser. it waits the required time, then goes again
you can use requestAnimationFrame() function like in the below,
function unlimited () {
requestAnimationFrame(unlimited);
console.log("arian")
}
unlimited();

Categories

Resources