Sites: Javascript: Dictionary - 2 |
1 | * Operator precedence |
2 | ^ Description |
3 | a) if an expression has more than one operator, the execution order is defined by their precedence |
4 | b) or, in other words, the default priority order of operators |
5 | c) from school, we all know that the multiplication in the expression 1 + 2 * 2 should be calculated before the addition |
6 | `that's exactly the precedence thing |
7 | d) the multiplication is said to have a higher precedence than the addition |
8 | e) parentheses override any precedence, so if we aren't satisfied with the default order, we can use them to change it |
9 | `(1 + 2) * 2 |
10 | f) there are many operators in js |
11 | g) every operator has a corresponding precedence number |
12 | h) the one with the larger number executes first |
13 | ^ Description (part 2) |
14 | a) if the precedence is the same, the execution order is from left to right |
15 | b) here's an extract from the precedence table |
16 | `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence |
17 | `we don't need to remember this, but note that unary operators are higher than corresponding binary ones |
18 | c) example |
19 | `unary plus + and unary negation are 17 precedence |
20 | `exponentiation ** is 16 precedence |
21 | `multiplication and division are 15 precedence |
22 | `addition and subtraction are 13 precedence |
23 | `assignment is 3 precedence |
24 | d) as we can see, the 'unary plus' has a priority of 17 which is higher than the 13 of 'addition' (binary plus) |
25 | e) that's why, in the expression "+apples + +oranges", unary pluses work before the addition |
26 | * Assignment |
27 | ^ Introduction |
28 | a) Let's note that an assignment = is also operator |
29 | b) it's listed in the precedence table with the very low priority of 3 |
30 | c) that's why, when we assign variable, like x = 2 * 2 + 1, the calculations are done first and then the = is evaluated, storing the result in x |
31 | `let x = 2 * 2 + 1; |
32 | `alert(x); // 5 |
33 | ^ Assignment = returns a value |
34 | a) the fact of = being an operator, not a 'magical' language construct has an interesting implication |
35 | b) all operators in js return a value |
36 | c) that's obvious for + and -, but also true for = |
37 | d) the cal x = value writes the value into x and then returns it |
38 | e) here's a demo that uses an assignment as part of a more complex expression |
39 | `let a = 1; |
40 | `let b = 2; |
41 | `let c = 3 - (a = b + 1); |
42 | `alert(a); // 3 |
43 | `alert(c); // 0 |
44 | f) in the example above, the result of expression (a = b + 1) is the value which was assigned to a (that is 3) |
45 | g) it is then used for further evaluations |
46 | h) funny code, isn't it? |
47 | ^ Assignment = returns a value (part 2) |
48 | a) we should understand how it works, because sometimes we see it in js libraries |
49 | b) although, please don't write the code like that |
50 | c) such tricks definitely don't make code clearer or readable |
51 | ^ Chaining Assignments |
52 | a) another interesting feature is the ability to chain assignments |
53 | `let a, b, c; |
54 | `a = b = c = 2 + 2; |
55 | `alert(a); // 4 |
56 | `alert(b); // 4 |
57 | `alert(c); // 4 |
58 | b) chained assignments evaluate from right to left |
59 | c) first, the rightmost expression 2 + 2 is evaluated and then assigned to the variables on the left: c, b and a |
60 | d) at the end, all the variables share a single value |
61 | e) once again, for the purposes of readability it's better to split such code into few lines |
62 | `c = 2 + 2; |
63 | `b = c; |
64 | `a = c; |
65 | f) that's easier to read, especially when eye-scanning the code fast |
66 | ^ Modify-in-place |
67 | a) we often need to apply an operator to a variable and store the new result in that same variable |
68 | b) example |
69 | `let n = 2; |
70 | `n = n + 5; |
71 | `n = n * 2; |
72 | c) this notation can be shortened using the operators += an *= |
73 | `let n = 2; |
74 | `n += 5; // now n = 7 (same as n = n + 5) |
75 | `n *= 2; // now n = 14 (same as n = n * 2) |
76 | d) short 'modify-and-assign' operator exist for all arithmetical and bitwise operators |
77 | `/= |
78 | `-= |
79 | e) such operators have the same precedence as a normal assignment, so they run after most other calculations |
80 | `let n = 2; |
81 | `n *= 3 + 5; |
82 | `alert(n); // 16 right part evaluated first, same as n *= 8 |
83 | * Increment/Decrement |
84 | ^ Introduction |
85 | a) increasing or decreasing a number by one is among the most common numerical operations |
86 | b) there are special operators for it |
87 | c) Increment '++' increases a variable by 1 |
88 | `let counter = 2; |
89 | `counter++; // works the same as counter = counter + 1, but is shorter |
90 | `alert(counter); // 3 |
91 | d) Decrement '--' decreases a variable by 1 |
92 | `let counter = 2; |
93 | `counter--; // works the same as counter = counter - 1, but is shorter |
94 | `alert(counter); // 1 |
95 | ^ Important |
96 | a) increment/decrement can only be applied to variables |
97 | b) trying to use it on values like 5++ will give an error. |
98 | c) the operators ++ and -- can be placed either before or after a variable |
99 | `when the operators goes after the variable, it is in 'postfix form' |
100 | ,counter++ |
101 | `the 'prefix form' is when the operator goes before the variable |
102 | ,++counter |
103 | d) both of these statements do the same thing: increase counter by 1 |
104 | e) is there any difference? yes, but we can only see it if we use the returned value of ++/-- |
105 | f) let's clarify. as we know, all operators return a value |
106 | g) increment/decrement is no exception |
107 | h) the prefix form returns the new value while the postfix form returns the old value |
108 | `prior to increment/decrement |
109 | ^ Important (part 2) |
110 | a) to see the difference, here's an example |
111 | `let counter = 1; |
112 | `let a = ++counter; // (*) |
113 | `alert(a); // 2 |
114 | b) in the line (*), the prefix form ++counter increments counter and returns the new value, 2. |
115 | `so, the alert shows 2 |
116 | c) now, let's use the postfix form |
117 | `let counter = 1; |
118 | `let a = counter++; |
119 | `alert(a); // 1 |
120 | d) in the line (*), the postfix form counter++ also increments counter but returns the old value (prior to increment) |
121 | `so, the alert shows 1 |
122 | ^ Summarize |
123 | a) if the result of increment/ decrement is not used, there's no difference in which form to use |
124 | `let counter = 0; |
125 | `counter++; |
126 | `++counter; |
127 | `alert(counter); // 2 the lines above did the same |
128 | b) if we'd like to increase a value and immediately use the result of the operator, we need the prefix form |
129 | `let counter = 0; |
130 | `alert(++counter); // 1 |
131 | c) if we'd like to increment a value but use its previous value, we need the postfix form |
132 | `let counter = 0; |
133 | `alert(counter++); // 0 |
134 | ^ Increment/Decrement among other operators |
135 | a) the operators ++/-- can be used inside expressions as well |
136 | b) their precedence is higher than most other arithmetical operations |
137 | `let counter = 1; |
138 | `alert(2 * ++counter); // 4 |
139 | c) compare with |
140 | `let counter = 1; |
141 | `alert(2 * counter++); // 2, because counter++ returns the old value |
142 | d) though technically okay, such notation usually makes code less readable |
143 | e) one line does multiple things - not good |
144 | f) while reading code, a fast 'vertical' eye-scan can easily miss sth like counter++ and it won't be obvious that the variable increased |
145 | g) we advise a style of 'one line - one action' |
146 | `let counter = 1; |
147 | `alert(2 * counter); |
148 | `counter++; |
149 | ^ Bitwise operators |
150 | a) bitwise operators treat arguments as 32-bit integer numbers and work on the level of their binary representation |
151 | b) these operators are not js-specific |
152 | c) they're supported in most programme languages |
153 | d) the list of operators |
154 | `AND (&) |
155 | `OR (|) |
156 | `XOR (^) |
157 | `NOT (~) |
158 | `LEFT SHIFT (<<) |
159 | `RIGHT SHIFT (>>) |
160 | `ZERO-FILL RIGHT SHIFT (>>>) |
161 | e) these operators are used very rarely, when we need to fiddle on the very lowest (bitwise) level. |
162 | f) we won't need these operators any time soon, as web development has little use of them, but in some special areas, such as cryptography, they're useful |
163 | * Comma |
164 | ^ Introduction |
165 | a) the comma operator ',' is one of the rarest and most unusual operators |
166 | b) sometimes, it's used to write shorter code, so we need to know it in order to understand what's going on |
167 | `let a = (1 + 2, 3 + 4); |
168 | `alert(a); // 7 (the result of 3 + 4) |
169 | c) here the first expression 1 + 2 is evaluated and its result is thrown away |
170 | d) then, 3 + 4 is evaluated and returned as the result |
171 | ^ Comma has a very low precedence |
172 | a) please note that the comma operator has very low precedence |
173 | `lower than = |
174 | b) so parentheses are important in the example above |
175 | c) without them a = 1 + 2, 3 + 4 evaluates + first, summing the numbers into a = 3, 7 |
176 | `then, the assignment operator = assigns a = 3, and the rest is ignored |
177 | d) it's like (a = 1 + 2), 3 + 4 |
178 | e) why do we need an operator that throws away everything except the last expression? |
179 | f) sometimes, people use it in more complex constructs to put several actions in one line |
180 | `// three operations in one line |
181 | `for (a = 1, b = 3, c = a * b; a < 10; a++) { |
182 | `... |
183 | `} |
184 | g) such tricks are used in many js frameworks |
185 | h) that's why we're mentioning them |
186 | `but usually they don't improve code readability so we should think well before using them |
187 | * Tasks Basic operators, maths |
188 | ^ The postfix and prefix forms |
189 | a) what are the final values of all variables a, b, c and d after the code below |
190 | `let a = 1, b = 1; |
191 | `let c = ++a; // 2 prefix form returns the new value |
192 | `let d = b++; // 1 postfix form returns the old value |
193 | ^ Assignment result |
194 | a) what are the values of a and x after the code below |
195 | `let a = 2; |
196 | `let x = 1 + (a *= 2); // 5 calculated as 1 + 4 |
197 | ^ Type Conversions |
198 | a) what are results of these expressions? |
199 | `"" + 1 + 0; // '10' the addition "" + 1 converts to '1', then '1' + 0 = '10' |
200 | `"" - 1 + 0; // -1 the subtraction (like most math operations) only works with numbers, it converts an empty string "" to 0 |
201 | `true + false; // 1 |
202 | `6 / '3'; // 2 |
203 | `"2" * "3"; // 6 |
204 | `4 + 5 + 'px'; // '9px' |
205 | `"$" + 4 + 5; // '$45' |
206 | `"4px" - 2; // NaN |
207 | `"4" - 2; // 2 |
208 | `" -9 " + 5; // -9 5 |
209 | `" -9 " - 5; // -14 the subtraction always converts to numbers, so it makes ' -9 ' a number -9 (ignoring spaces around it) |
210 | `null + 1; // 1 null becomes 0 after the numeric conversion |
211 | `undefined + 1; // NaN (undefined becomes NaN after the numeric conversion |
212 | `" \t " - 2; // space characters, are trimmed off string start and end when a string is converted to a number. |
213 | ,here the whole string consists of space characters, such as \t, and a 'regular' space between them |
214 | ,so, similarly to an empty string, it becomes 0 |
215 | ^ Fix the addition |
216 | a) here's a code that asks the user for two numbers and shows their sum |
217 | b) it works incorrectly. The output in the example below is 12 (for default prompt values) |
218 | c) why? Fix it. The result should be 3 |
219 | `let a = prompt('First number?', 1); // fix let a = +prompt... |
220 | `let b = prompt('Second number?', 2); // fix let b = +prompt... |
221 | `alert(a + b); // or alert(+a + +b); or alet(Number(a) + Number(b)); |
222 | c) explanation |
223 | `the reason is that prompt returns user input as a string |
224 | `so variables have values '1' and '2' respectively |
225 | `what we should do is to convert strings to numbers before + |
226 | ,for example, using Number() or prepending them with + |
227 | * from Comment Basic Operators, maths |
228 | ^ difference between postfix and prefix |
229 | a) the difference between ++z and z++ is only when one of these expressions is included in an other expression |
230 | `z = 0; |
231 | `z++; |
232 | `alert(z); // 1 no difference between z++ and ++z |
233 | b) but if we include now theses expressions in another one (alert for example) then we can see the difference |
234 | `let z = 0; |
235 | `alert(z++); // 0 difference as alert(++z) is 1 |
236 | * Comparisons |
237 | ^ Introduction |
238 | a) we know many comparisons from maths |
239 | b) in js they're written like this |
240 | `greater/less than: a > b, a < b |
241 | `greater/less than or equals: a >= b, a <= b |
242 | `equals: a == b |
243 | ,please note the double equality sign == means the equality test |
244 | ,while a single one = means an assignment |
245 | `not equals: in maths the notation is crossed out sign of equality |
246 | `but in js it's written a != b |
247 | c) in this article we'll learn more about different types of comparisons, how js makes them, including important |
248 | d) at the end you'll find a good recipe to avoid 'js quirks' - related issues |
249 | ^ Boolean is the result |
250 | a) all comparison operators return a boolean value |
251 | `true - means 'yes', 'correct' or 'the truth' |
252 | `false - means 'no', 'wrong' or 'not the truth' |
253 | b) example |
254 | `alert(2 > 1); // true (correct) |
255 | `alert(2 == 1); // false (wrong) |
256 | `alert(2 != 1); // true (correct) |
257 | c) a comparison result can be assigned to a variable, just like any value |
258 | `let result = 5 > 4; // assign the result of the comparison |
259 | `alert(result); // true |
260 | ^ String comparison |
261 | a) to see whether a string is greater than another, js uses the so-called 'dictionary' or 'lexicographical' order |
262 | `in other words, strings are compared |
263 | b) example |
264 | `alert('Z' > 'A'); // true |
265 | `alert('Glow' > 'Glee'); // true |
266 | `alert('Bee' > 'Be'); // true |
267 | c) the algorithm to compare two strings is simple |
268 | `compare the first character of both strings |
269 | `if the first character from the first string is greater (or less) than the other string's |
270 | ,the first string is greater (or less) than the second |
271 | `otherwise, if both string's first characters are the same, compare the second characters the same way |
272 | `repeat until the end of either string |
273 | `if both strings end at the same length, then they're equal |
274 | ,otherwise, the longer string is greater |
275 | d) in the first example above, the comparison 'Z' > 'A' gets to a result at the first step |
276 | e) the second comparison 'Glow' and 'Glee' needs more steps as strings are compared character-by-character |
277 | `G is the same as G |
278 | `l is the same as l |
279 | `o is greater than e. |
280 | ,stop here, the first string is greater |
281 | * Not a real dictionary, but Unicode order |
282 | a) the comparison algorithm given above is roughly equivalent to the one used in dictionaries or phone books, but it's not exactly the same |
283 | b) for instance, case matters. |
284 | `a capital letter 'A' is not equal to the lowercase 'a' |
285 | ,because the lowercase character has a greater index in the internal encoding table js uses (Unicode) |
286 | * Comparison of different types |
287 | a) when comparing values of different types, js converts the values to numbers |
288 | `alert('2' > 1); // true, string '2' becomes a number 2 |
289 | `alert('01' == 1); // true, string '01' becomes a number 1 |
290 | b) for boolean values, true becomes 1 and false becomes 0 |
291 | `alert(true == 1); // true |
292 | `alert(false == 0); // true |
293 | ^ A funny consequence |
294 | a) it's possible that at the same time |
295 | `two values are equal |
296 | `one of them is true as a boolean and the other one is false as a boolean |
297 | b) example |
298 | `let a = 0; |
299 | `alert(Boolean(a)); // false |
300 | `let b = '0'; |
301 | `alert(Boolean(b)); // true |
302 | `alert(a == b); // true |
303 | c) from js's standpoint, this result is quite normal |
304 | d) an equality check converts values using the numeric conversion |
305 | `hence '0' becomes 0, while the explicit Boolean conversion uses another set of rules |
306 | * Strict equality |
307 | a) a regular equality check == has a problem. |
308 | `it can't differentiate 0 from false |
309 | b) the same thing happens with an empty string |
310 | `alert("" == false); // true |
311 | c) this happens because operands of different types are converted to numbers by the equality operator ==. |
312 | d) an empty string, just like false, becomes a zero. |
313 | e) what to do if we'd like to differentiate o from false? |
314 | f) a strict equality operator === checks the equality without type conversion |
315 | `in other words, if a and b are of different types, then a === b immediately returns false without an attempt to convert them |
316 | g) let's try it |
317 | `alert(0 === false); // false, because the types are different |
318 | h) there's also a strict non-equality operator !== analogous to != |
319 | * Comparison with null and undefined |
320 | ^ Introduction |
321 | a) there's a non-intuitive behaviour when null or undefined are compared to other values |
322 | b) for a strict equality check === |
323 | `these values are different, because each of them is a different type |
324 | ,alert(null === undefined); // false |
325 | c) for a non-strict check == |
326 | `there's a special rule |
327 | `these two are 'sweet couple', but not any other value |
328 | ,alert(null == undefined);; // true |
329 | d) for maths and other comparisons < > <= >= |
330 | `null/undefined are converted to numbers |
331 | ,null becomes 0 |
332 | ,undefined becomes NaN |
333 | * Strange result: Null vs 0 |
334 | a) Let's compare null with a zero |
335 | `alert(null > 0); // false |
336 | `alert(null == 0); // false |
337 | `alert(null >= 0); // true |
338 | b) Mathematically, that's strange |
339 | `the last result states 'null is greater than or equal to zero' |
340 | `so in one of the comparisons above it must be true, but they're both false |
341 | c) The reason is that an equality check == and comparisons > < >= <= work differently |
342 | d) Comparisons convert null to a number, treating it as 0 |
343 | e) That's why null >= 0 is true and null > 0 is false |
344 | f) On the other hand, the equality check == for undefined and null is defined such that, without any conversions |
345 | `they equal each other and don't equal anything else |
346 | `that's why null == 0 is false |
347 | * An incomparable undefined |
348 | a) The value undefined shouldn't be compared to other values |
349 | `alert(undefined > 0); // false (1) |
350 | `alert(undefined < 0); // false (2) |
351 | `alert(undefined == 0); // false (3) |
352 | b) Why does it dislike zero so much? Always false! |
353 | c) We get these results because |
354 | `Comparisons (1) and (2) return false because undefined gets converted to NaN and NaN is a special numeric value which returns false for all comparisons |
355 | `The equality check (3) returns false because undefined only equals |
356 | ,null |
357 | ,undefined |
358 | ^ Avoid problems |
359 | a) Why did we go over these examples? |
360 | `Should we remember these peculiarities all the time? |
361 | `Well, not really |
362 | `Actually, these tricky things will gradually become familiar over time, but there's a solid way to avoid problems with them |
363 | b) Avoid problems |
364 | `Treat any comparison with undefined/null except the strict equality === with exceptional care |
365 | `Don't use comparisons >= > < <= with a variable which may be null/undefined, unless you're really sure of what you're doing |
366 | ,If a variable can have these values, check for them separately |
367 | * Summary |
368 | a) Comparison operators return a boolean value |
369 | b) Strings are compared letter-by-letter in the 'dictionary' order |
370 | c) When values of different types are compared, they get converted to numbers |
371 | `with the exclusion of a strict equality check |
372 | d) The values null and undefined equal == each other and don't equal any other value |
373 | e) Be careful when using comparisons like > or < with variables that can occasionally be null/undefined |
374 | `Checking for null/undefined separately is a good idea |
375 | * Tasks (Comparisons) |
376 | a) What will be the result for these expressions? |
377 | `5 > 4; // true |
378 | `"apple" > "pineapple"; // false (Dictionary comparison where 'a' is smaller than 'p' |
379 | `"2" > "12"; // true (Dictionary comparison where '2' is greater than '1') |
380 | `undefined == null; // true (values null and undefined equal each other) |
381 | `undefined === null; // false (Strict equality is strict. Different types from both sides lead to false) |
382 | `null == " 0 "; // false (null only equals to undefined) |
383 | `null === +" 0 "; // false (strict equality of different types) |
384 | * Conditional branching: if, '?' |
385 | ^ Introduction |
386 | a) Sometimes, we need to perform different actions based on different conditions |
387 | b) To do that, we can use the 'if' statement and the conditional operator '?', that's also called a 'question mark' operator |
388 | ^ The 'if' statement |
389 | a) The if(...) statement evaluates a condition in parentheses and, if the result is true, executes a block of code |
390 | `let year = prompt('In which year was ECMAScript-2015 specification published?', ""); |
391 | `if (year == 20156) alert('You are right!'); |
392 | b) In the example above, the condition is a simple equality check (year == 2015), but it can be much more complex |
393 | c) If we want to execute more than one statement, we have to wrap our code block inside curly braces |
394 | `if (year == 2015) { |
395 | `alert('That's correct!'); |
396 | `alert('You're so smart!'); |
397 | `} |
398 | d) We recommend wrapping your code block with curly braces {} every time you use an 'if' statement |
399 | `even if there's only one statement to execute |
400 | `doing so improves readability |
401 | * Boolean conversion |
402 | ^ Introduction |
403 | a) The if(...) statement evaluates the expression in its parentheses and converts the result to a boolean |
404 | b) Let's recall the conversion rules from the chapter Type Conversions |
405 | `A number 0, an empty string "", null, undefined and NaN all become false |
406 | ,Because of that they're called 'falsy' values |
407 | `Other values become true |
408 | ,so they're called 'truthy' |
409 | c) So, the code under this condition would never execute |
410 | `if (0) { // 0 is falsy |
411 | `... |
412 | `} |
413 | d) And inside this condition - it always will |
414 | `if (1) { // 1 is truthy |
415 | `... |
416 | `} |
417 | e) We can also pass a pre-evaluated boolean value to 'if', like this |
418 | `let cond = (year == 2015); // equality evaluates to true or false |
419 | `if (cond) { |
420 | `... |
421 | `} |
422 | * The 'else' clause |
423 | ^ Introduction |
424 | a) The if statement may contain optional 'else' block. |
425 | `it executes when the condition is falsy. |
426 | b) Example |
427 | `let year = prompt('In which year was the ECMAScript-2015 specification published?', ""); |
428 | `if (year == 2015) { |
429 | `alert('You guessed it right!); |
430 | `} else { |
431 | `alert('How can you be so wrong?'); // any value except 2015 |
432 | `} |
433 | * Several conditions: 'else if' |
434 | ^ Introduction |
435 | a) Sometimes, we'd like to test several variants of a condition |
436 | b) The 'else if' clause lets us do that |
437 | `let year = prompt('In which year was the ECMAScript-2015 published?', ""); |
438 | `if (year < 2015) { |
439 | `alert('Too early!'); |
440 | `} else if (year > 2015) { |
441 | `alert('Too late!'); |
442 | `} else { |
443 | `alert('Exactly!'); |
444 | `} |
445 | c) In the code above JavaScript first checks year < 2015 |
446 | `If that is falsy, it goes to the next condition year > 2015 |
447 | `If that is also falsy, it shows the last alert |
448 | d) There can be more else if blocks. |
449 | `The final else is optional |
450 | * Conditional operator '?' |
451 | a) Sometimes, we need to assign a variable depending on a condition |
452 | `let accessAllowed; |
453 | `let age = prompt('How old are you, ""); |
454 | `if (age > 18) { |
455 | `accessAllowed = true; |
456 | `} else { |
457 | `accessAllowed =false; |
458 | `} |
459 | `alert('accessAllower'); |
460 | b) The so-called 'conditional' or 'question mark' operator lets us do that in a shorter and simpler way |
461 | c) The operator is represented by a question mark '?' |
462 | `Sometimes, it's called 'ternary', because the operator has three operands |
463 | `It's actually the one and only operator in js which has that many |
464 | d) The syntax is |
465 | `let result = condition ? value1 : value2; |
466 | e) The condition is evaluated |
467 | `If it's truthy then value1 is returned, otherwise - value2 |
468 | f) Example |
469 | `let accessAllowed = (age > 18) ? true : false; |
470 | `Technically, we can omit the parentheses around age > 18 |
471 | `The question mark operator has a low precedence |
472 | ,so it executes after the comparison > |
473 | ^ Conditional operator '?' (part 2) |
474 | a) This example will do the same thing as the previous one |
475 | `// the comparison operator 'age > 18' executes first anyway |
476 | `// no need to wrap it into parentheses |
477 | `let accessAllowed = age > 18 ? true : false; |
478 | b) But parentheses make the code more readable, so we recommend using them |
479 | ^ Please note |
480 | a) In the example above, you can avoid using the question mark operator because the comparison itself returns true/false |
481 | `// the same |
482 | `let accessAllowed = age > 18; |
483 | * Multiple '?' |
484 | a) A sequence of question mark operators '?' can return a value that depends on more than one condition |
485 | `let age = prompt('age?', 18); |
486 | `let message = (age < 3) ? 'Hi, baby!' : |
487 | `(age < 18) ? 'Hello!' : |
488 | `(age < 100) ? 'Greetings!' : |
489 | `'What an unusual age!'; |
490 | `alert(message); |
491 | b) It may be difficult at first to grasp what's going on |
492 | `but after a closer look, we can see that it's just an ordinary sequence of tests |
493 | c) Explanation |
494 | `The first question mark checks whether age < 3 |
495 | `If true - it returns 'Hi, baby!'. |
496 | ,otherwise, it continues to the expression after the colon ':', checking age < 18 |
497 | `If that's true - it returns 'Hello!' |
498 | ,otherwise, it continues to the expression after the colon ':', checking age < 18 |
499 | `It that's true - it returns 'Greetings!' |
500 | ,otherwise, it continues to the expression after the colon ':', returning 'What an unusual age!' |
501 | d) Here's how this looks using if...else |
502 | `if (age < 3) { |
503 | `message = 'Hi, baby!'; |
504 | `} else if (age < 18) { |
505 | `message = 'Hello!'; |
506 | `} else if (age < 100) { |
507 | `message = 'Greetings!'; |
508 | `} else { |
509 | `message = 'What an unusual age!'; |
510 | `} |
511 | * Non-traditional use of '?' |
512 | a) Sometimes the question mark '?' is used as a replacement for 'if' |
513 | `let company = prompt('Which company created js?', ""); |
514 | `(company == 'Netscape') ? |
515 | `alert('Right!') : alert('Wrong!'); |
516 | b) Depending on the condition company == 'Netscape', either the first or the second expression after the '?' gets executed and shows an alert |
517 | c) We don't assign a result to a variable here |
518 | `Instead, we execute different code depending on the condition |
519 | d) It's not recommended to use the question mark operator in this way |
520 | e) The notation is shorter than the equivalent 'if' statement, which appeals to some programmers |
521 | `But it is less readable |
522 | f) Here's the same code using of comparison |
523 | `let company = prompt('Which company created js?', ""); |
524 | `if (company == 'Netscape') { |
525 | `alert('Right'); |
526 | `} else { |
527 | `alert('Wrong'); |
528 | `} |
529 | g) Our eyes scan the code vertically |
530 | `Code blocks which span several lines are easier to understand than a long, horizontal instruction set |
531 | h) The purpose of the question mark operator '?' is to return one value or another depending on its condition |
532 | `please use it for exactly that |
533 | `use 'if' when you need to execute different branches of code |
534 | * Tasks (Conditional branches 'if', '?') |
535 | ^ if (a string with zero) |
536 | a) Will alert be shown? |
537 | `if ('0') { |
538 | `alert('Hello'); |
539 | `} |
540 | b) '0' is not empty, so it will be true |
541 | ^ The name of js |
542 | a) Using the if... else construct, write the code which asks |
543 | `"What is the official name of JavaScript?" |
544 | `If the visitor enters 'ECMAScript', then output 'Right' |
545 | ,otherwise - output: "You don't know? ECMAScript!" |
546 | ^ Show the sign |
547 | a) Using if...else, write the code which gets a number via prompt and then shows in alert |
548 | `1, if the value is greater than zero |
549 | `-1, if less than zero |
550 | `0, if equals zero |
551 | b) In this task we assume that the input is always a number |
552 | ^ Rewrite 'if' into '?' |
553 | a) Rewrite this 'if' using a conditional operator '?' |
554 | `let result; |
555 | `if (a + b < 4) { |
556 | `result = 'Below'; |
557 | `} else { |
558 | `result = 'Over'; |
559 | `} |
560 | ^ Rewrite 'if...else' into '?' |
561 | a) Rewrite 'if...else' using multiple ternary operators '?' |
562 | b) For readability, it's recommended to split the code into multiple lines |
563 | `let message; |
564 | `if (login == 'Employee') { |
565 | `message = 'Hello'; |
566 | `} else if (login == 'Director') { |
567 | `message = 'Greetings'; |
568 | `} else if (login == "") { |
569 | `message = 'No login'; |
570 | `} else { |
571 | `message = ""; |
572 | `} |
573 | * Logical Operators |
574 | ^ Introduction |
575 | a) There are four logical operators in JS |
576 | `|| OR |
577 | `&& AND |
578 | `! NOT |
579 | `?? NULLISH COALESCING |
580 | b) Here we cover the first three |
581 | c) Although they're called "logical", they can be applied to values of any type, not only boolean. |
582 | , Their result can also be of any type. |
583 | ^ || OR |
584 | a) the OR operator is represented with two vertical line symbols |
585 | ,result = a || b; |
586 | b) In classical programming the logical OR is meant to manipulate boolean values only. |
587 | c) If any of its arguments are true, it returns true, otherwise false |
588 | d) In JS, the operator is a little bit trickier and more powerful. |
589 | `But first, let's see what happens with boolean values. |
590 | e) There are four possible logical combinations |
591 | `alert (true || true); // true |
592 | `alert (true || false); // true |
593 | `alert (false || true); // true |
594 | `alert (false || false); // false |
595 | f) As we can see, the result is always true except for the case when both operands are false |
596 | g) If an operand is not a boolean, it's converted to a boolean for the evaluation |
597 | h) For instance, the number 1 is treated as true, the number 0 as false |
598 | `if (1 || 0) { // works just like if (true || false) |
599 | `alert ( 'truthy!' ); |
600 | `} |
601 | ^ || OR (part 2) |
602 | a) Most of the time, OR || is used in an if statement to test if any of the given conditions is true |
603 | `let hour = 9; |
604 | `if (hour < 10 || hour > 18) { |
605 | `alert ( 'The office is closed.' ); |
606 | `} |
607 | b) We can pass more conditions |
608 | `let hour = 12; |
609 | `let isWeekend = true; |
610 | `if (hour < 10 || hour > 18 || isWeekend) { |
611 | `alert ( 'The office is closed.' ); // It is the weekend |
612 | `} |
613 | ^ OR || finds the first truthy value |
614 | a) The logic described above is somewhat classical |
615 | b) Now, let's bring in the 'extra' features of JS |
616 | c) The extended algorithm works as follows |
617 | d) Given multiple OR'ed values |
618 | `result = value1 || value2 || value3; |
619 | e) The OR operator does the following |
620 | `Evaluates operands from left to right |
621 | `For each operand, converts it to boolean |
622 | `If the result is true, stops and returns the original value of that operand |
623 | `If all operands have been evaluated (i.e. all were false), returns the last operand |
624 | f) A value is returned in its original form, without the conversion |
625 | `In other words, a chain of OR || returns the first truthy value or the last one if no truthy value is found |
626 | g) For instance |
627 | `alert( 1 || 0 ); // 1 is truthy |
628 | `alert( null || 1 ); // 1 is the first truthy value |
629 | `alert( null || 0 || 1 ); // 1 is the first truthy value |
630 | `alert( undefined || null || 0 ); // 0 all falsy, returns the last value |
631 | ^ OR || Finds the first truthy value (part 2) |
632 | a) This leads to some interesting usage compared to a 'pure, classical, boolean-only OR' |
633 | b) Getting the first truthy value from a list of variables or expressions |
634 | `For instance, we have firstName, lastName and nickName variables, all optional (i.e. can be undefined or have falsy values) |
635 | `Let's use OR || to choose the one that has the data and show it (or 'Anonymous' if nothing set) |
636 | ,let firstName = ""; |
637 | ,let lastName = ""; |
638 | ,let nickName = "SuperCoder"; |
639 | ,alert( firstName || lastName || nickName || 'Anonymous'); // SuperCoder |
640 | `If all variables were falsy, 'Anonymous' would show up |
641 | c) Short-circuit evaluation |
642 | `Another feature of OR || operator is the so-called 'short-circuit' evaluation |
643 | `It means that || processes its arguments until the first truthy value is reached, and then the value is returned immediately, without even touching the other argument |
644 | `The importance of this feature becomes obvious if an operand isn't just a value, but an expression with a side effect, such as a variable assignment of a function call |
645 | `In the example below, only the second message is printed |
646 | ,true || alert('not printed'); |
647 | ,false || alert('printed'); |
648 | `In the first line, the OR || operator stops the evaluation immediately upon seeing true, so the alert isn't run. |
649 | `Sometimes, people use this feature to execute commands only if the condition on the left part is falsy |
650 | ^ && (AND) |
651 | a) The ANS operator is represented with 2 ampersands && |
652 | `result = a && b; |
653 | b) In classical programming, AND returns true if both operands are truthy and false otherwise |
654 | c) alert (true && true); // true |
655 | `alert (true && false); // false |
656 | `alert (false && true); // false |
657 | `alert (false && false); // false |
658 | d) An example with if |
659 | `let hour = 12; |
660 | `let minute = 30; |
661 | `if (hour == 12 && minute == 30) { |
662 | `alert('The time is 12:30'); |
663 | `} |
664 | e) Just as with OR, any value is allowed as an operand of AND |
665 | `if (1 && 0) { // evaluated as true && false |
666 | `alert('Won't work, because the result is falsy'); |
667 | `} |
668 | ^ AND '&&' finds the first falsy value |
669 | a) Given multiple AND'ed values |
670 | `result = value1 && value2 && value3; |
671 | b) The AND && operator does the following |
672 | `Evaluates operands from left to right |
673 | `For each operand, converts it to a boolean. |
674 | ,If the result is false, stops and returns the original value of that operand |
675 | ,If all operands have been evaluated (i.e. all were truthy), returns the last operand |
676 | `In other words, AND returns the first falsy value or the last value if none were found |
677 | c) The rules above are similar to OR. |
678 | `The difference is that AND returns the first falsy value while OR returns the first truthy one. |
679 | d) Examples |
680 | `// if the first operand is truthy, AND returns the second operand |
681 | `alert(1 && 0); // 0 |
682 | `alert(1 && 5); // 5 |
683 | `// if the first operand is falsy, AND returns it |
684 | `// The second operand is ignored |
685 | `alert(null && 5); // null |
686 | `alert(0 && 'no matter what'); // 0 |
687 | e) We can also pass several values in a row. |
688 | `See how the first falsy one is returned |
689 | `alert(1 && 2 && null && 3); // null |
690 | f) When all values are truthy, the last value is returned |
691 | `alert(1 && 2 && 3); // 3 |
692 | ^ Precedence of AND && is higher than OR || |
693 | a) So the code a && b || c && d is essentially the same as if the && expressions were in parentheses |
694 | `(a && b) || (c && d) |
695 | ^ Don't replace 'if' with '||' or '&&' |
696 | a) Sometimes, people use the AND && operator as a 'shorter way to write if' |
697 | b) For instance |
698 | `let x = 1; |
699 | `(x > 0) && alert('Greater than zero'); |
700 | c) The action in the right part of && would execute only if the evaluation reaches it. |
701 | `That is, only if (x > 0) is true. |
702 | d) So we basically have an analogue for |
703 | `let x = 1; |
704 | `if (x > 0) alert('Greater than zero'); |
705 | e) Although, the variant with && appears shorter, 'if' is more obvious and tends to be a little bit more readable |
706 | f) So we recommend using every construct for its purpose |
707 | `use 'if' if we want 'if' and use '&&' if we want AND |
708 | * ! (NOT) |
709 | ^ |
710 | a) The boolean NOT operator is represented with an exclamation sign ! |
711 | b) The syntax is pretty simple |
712 | `result = !value; |
713 | c) The operator accepts a single argument and does the following |
714 | `Converts the operand to boolean type: true/false. |
715 | `Returns the inverse value |
716 | d) For instance |
717 | `alert(!true); // false |
718 | `alert(!0); // true |
719 | e) A double NOT !! is sometimes used for converting a value to a boolean type |
720 | `alert(!!'non-empty sting'); // true |
721 | `alert(!!null); // false |
722 | f) That is, the first NOT converts the value to boolean and returns the inverse, and the second NOT inverses it again. |
723 | `In the end, we have a plain value-to-boolean conversion |
724 | g) There's a little more verbose way to do the same thing -- a built-in boolean function |
725 | `alert( Boolean('non-empty string')); // true |
726 | `alert( Boolean(null)); // false |
727 | h) The precedence of NOT is the highest of all logical operators |
728 | `so it always executes first, before && or || |
729 | * Tasks (Logical operators) |
730 | ^ What is the code below going to output? |
731 | a) alert(null || 2 || undefined); // 2 (the first truthy value) |
732 | ^ What's the result of OR'ed alerts? |
733 | a) What will the code below output? |
734 | `alert( alert(1) || 2 || alert (3) ); // first 1, then 2 |
735 | b) The call to alert doesn't return a value |
736 | `In other words, it returns undefined |
737 | c) The first OR || evaluates its left operand alert(1) |
738 | `That shows the first message with 1 |
739 | d) The alert returns undefined |
740 | `so OR goes on to the second operand searching for a truthy value |
741 | e) The second operand 2 is truthy |
742 | `so the execution is halted |
743 | `2 is returned and then shown by the outer alert |
744 | f) There will be no 3, because the evaluation doesn't reach alert(3) |
745 | ^ What is this code going to show? |
746 | a) alert(1 && null && 2); // null, because it's the first falsy value from the list |
747 | ^ What is the result of AND'ed alerts? |
748 | a) alert( alert(1) && alert(2) ); // the answer 1, and then undefined |
749 | `The call to alert returns undefined |
750 | ,it just shows a message, so there's no meaningful return |
751 | `Because of that, && evaluates the left operand (outputs 1), and immediately stops, because undefined is a falsy value |
752 | `And && looks for a falsy value and returns it, so it's done |
753 | ^ The result of OR AND OR |
754 | a) What will the result be? |
755 | `alert(null || 2 && 3 || 4); // the answer 3 |
756 | b) The precedence of AND && if higher than ||, so it executes first |
757 | c) The result of 2 && 3 = 3, so the expression becomes |
758 | null || 3 || 4 |
759 | `Now the result is the first truthy value |
760 | ^ Check the range between |
761 | a) Write an 'if' condition to check that 'age' is between 14 and 90 inclusively |
762 | b) 'Inclusively' means that 'age' can reach the edges 14 or 90 |
763 | c) if (age >= 14 && age <= 90) |
764 | ^ Check the range outside |
765 | a) Write an 'if' condition to check that 'age' is NOT between 14 and 90 inclusively |
766 | b) Create two variants |
767 | `The first one using NOT '!' |
768 | `The second one without it |
769 | c) The first variant |
770 | `if (age < 14 || age > 90); |
771 | d) The second variant |
772 | `if (!(age >= 14 && age <= 90)); |
773 | ^ A question about 'if' |
774 | a) Which of these alerts are going to execute? |
775 | b) What will the results of the expressions be inside if(...)? |
776 | c) Tasks |
777 | `if (-1 || 0) alert('first'); |
778 | `if (-1 && 0) alert('second'); |
779 | `if (null || -1 && 1) alert('third'); |
780 | d) The answer: the first and the third will execute |
781 | e) Details |
782 | `// Runs |
783 | ,// The result of -1 || 0 = -1, truthy |
784 | `// Doesn't run |
785 | ,// -1 && 0 = 0, falsy |
786 | ,if (-1 && 0) alert('second'); |
787 | `// Executes |
788 | ,// Operator && has a higher precedence than || |
789 | ,// so -1 && 1 executes first, giving us the chain |
790 | ,// null || -1 && 1 -> null || 1 -> 1 |
791 | ,if (null || -1 && 1) alert('third'); |
792 | ^ Check the login |
793 | a) Write the code which asks for a login with prompt |
794 | b) If the visitor enters 'Admin', then prompt for a password, if the input is an empty line or Esc - show 'Canceled', if it's another string -- then show 'I don't know you' |
795 | c) The password is checked as follows |
796 | `If it equals 'TheMaster', then show 'Welcome' |
797 | `Another string -- show 'Wrong password' |
798 | `For an empty string or cancelled input, show 'Canceled' |
799 | d) The schema |
800 | `Begin -> Who's there? -> Cancel || Other || Admin |
801 | ,Admin -> Password -> (Cancel -> Canceled) || (Other -> Wrong password) || (TheMaster -> Welcome) |
802 | e) Please, use nested 'if' blocks |
803 | `Mind the overall readability of the code |
804 | f) Hint: passing an empty input to a prompt returns an empty string "". |
805 | `Pressing Esc during a prompt returns null |
806 | g) Decision |
807 | `let currentUserName = prompt("Who's there?", ''); |
808 | `if (currentUserName === 'Admin') { |
809 | `let pass = prompt('Password?', ''); |
810 | `if (pass === 'TheMaster') { |
811 | `alert('Welcome!'); |
812 | `} else if (pass === null || pass === '') { |
813 | `alert('Canceled'); |
814 | `} else { |
815 | `alert('Wrong password'); |
816 | `} |
817 | `} else if (currentUserName === null || currentUserName === '') { |
818 | `alert("Canceled"); |
819 | `} else { |
820 | `alert("I don't know"); |
821 | `} |
822 | h) Note the vertical indents inside the 'if' blocks. |
823 | `They're technically nore required, but make the code more readable |
824 | * Nullish coalescing operator '??' |
825 | ^ Introduction |
826 | a) This is a recent addition to the language |
827 | `Old browsers may need polyfills |
828 | b) The nullish coalescing operator is written as two question marks '??' |
829 | c) As it treats treats null and undefined similarly, we'll use a special term here, in this article |
830 | d) We'll say that an expression is 'defined' when it's neither null nor undefined |
831 | e) The result of a ?? b is |
832 | `if 'a' is defined, then 'a' |
833 | `if 'a' isn't defined, then 'b' |
834 | f) In other words, ?? returns the first argument if it's not null/undefined. |
835 | `Otherwise, the second one |
836 | g) The nullish coalescing operator isn't anything completely new |
837 | `It's just a nice syntax to get the first 'defined' value of the two |
838 | ^ Introduction (part 2) |
839 | a) We can rewrite result = a ?? b using the operators that we already know, like this |
840 | `result = (a !== null && a !== undefined) ? a : b; |
841 | b) Now it should be absolutely clear what ?? does |
842 | `Let's see where it helps |
843 | c) The common use case for ?? is to provide a default value for a potentially undefined variable |
844 | d) For example, here we show user if defined, otherwise Anonymous |
845 | `let user; |
846 | `alert(user ?? 'Anonymous'); // Anonymous (user not defined) |
847 | e) We can also use a sequence of ?? to select the first value from a list that isn't null/undefined |
848 | f) Let's say we have a user's data in variables firstName, lastName or nickName |
849 | `All of them may be not defined, if the user decided not to enter a value |
850 | g) We'd like to display the user name using one of these variables, or show 'Anonymous' if all of them aren't defined |
851 | `Let's use the ?? operator for that |
852 | ^ Introduction (part 3) |
853 | a) let firstName = null; |
854 | `let lastName = null; |
855 | `let nickName = 'Supercoder'; |
856 | `// Shows the first defined value |
857 | `alert(firstName ?? lastName ?? nickName ?? 'Anonymous'); // Supercoder |
858 | ^ Comparison with || |
859 | a) The OR || operator can be used in the same way as ?? |
860 | b) For example, in the code above we could replace ?? with || and still get the same result |
861 | `let firstName = null; |
862 | `let lastName = null; |
863 | `let nickName = 'Supercoder'; |
864 | `// Shows the first truthy value |
865 | `alert(firstName || lastName || nickName || 'Anonymous'); // Supercoder |
866 | c) Historically, the OR || operator was there first |
867 | `It exists since the beginning of JS |
868 | `So developers were using it for such purposes for a long time |
869 | d) On the other hand, the nullish coalescing operator ?? was added to JS only recently |
870 | `And the reason for that was that people weren't quite happy with || |
871 | e) The important difference between them is that |
872 | `|| returns the first truthy value |
873 | `?? returns the first defined value |
874 | f) In other words, || doesn't distinguish between false, 0, an empty string "" and null/undefined |
875 | `They're all the same -- falsy values |
876 | `If any of these is the first argument of ||, then we'll get the second argument as the result |
877 | g) In practice though, we may want to use default value only when the variable is null/undefined |
878 | `That is, when the value is really unknown/not set |
879 | ^ Comparison with || (part 2) |
880 | a) For example, consider this |
881 | `let height = 0; |
882 | `alert(height || 100); // 100 |
883 | `alert(height ?? 100); // 0 |
884 | b) The height || 100 checks height for being a falsy value, and it's 0, falsy indeed |
885 | `So the result of || is the second argument, 100 |
886 | c) The height ?? 100 checks height for being null/undefined, and it's not |
887 | `So the result is height 'as is', that is 0 |
888 | d) In practise, the zero height is often a valid value, that shouldn't be replaced with the default |
889 | `So ?? does just the right thing |
890 | * Precedence (??) |
891 | ^ Introduction |
892 | a) The precedence of the ?? operator is about the same as ||, just a bit lower |
893 | `5 ?? against 6 || |
894 | b) That means that, just like ||, the nullish coalescing operator ?? is evaluated before = and ?, but after most other operations, such as +, * |
895 | c) So if we'd like to choose a value with ?? in an expression with other operators, consider adding parentheses |
896 | `let height = null; |
897 | `let width = null; |
898 | `// important: use parentheses |
899 | `let area = (height ?? 100) * (width ?? 50); |
900 | `alert(area); // 5000 |
901 | d) Otherwise, if we omit parentheses, then as * has the higher precedence than ??, it would execute first, leading to incorrect results |
902 | `// Without parentheses |
903 | `let area = height ?? 100 * width ?? 50; |
904 | `// ... works the same as this (probably not what we want): |
905 | `let area = height ?? (100 * width) ?? 50; |
906 | * Using ?? with && or || |
907 | ^ |
908 | a) Due to safety reasons, JS forbids using ?? together with && and || operators, unless the precedence is explicitly specified with parentheses |
909 | b) The code below triggers a syntax error |
910 | `let x = 1 && 2 ?? 3; // Syntax error |
911 | c) The limitation is surely debatable, it was added to the language specification with the purpose to avoid programming mistakes, |
912 | when people start to switch from || to ?? |
913 | d) Use explicit parentheses to work around it |
914 | `let x = (1 && 2) ?? 3; // works |
915 | `alert(x); // 2 |
916 | * Summary (??) |
917 | ^ |
918 | a) The nullish coalescing operator ?? provides a short way to choose the first 'defined value from a list' |
919 | b) It's used to assign default values to variables |
920 | `// Set height = 100, if height is null or undefined |
921 | `height = height ?? 100; |
922 | c) The operator ?? has a very low precedence, only a bit higher than ? and = |
923 | `So consider adding parentheses when using it in an expression |
924 | d) It's forbidden to use it with || or && without explicit parentheses |
925 | * Loops: while and for |
926 | ^ Introduction |
927 | a) We often need to repeat actions |
928 | b) For example, outputting goods from a list one after another or just running the same code for each number from 1 to 10 |
929 | c) Loops are a way to repeat the same code multiple times |
930 | ^ The 'while' loop |
931 | a) while (condition) { |
932 | `// code |
933 | `// so-called 'loop body' |
934 | `} |
935 | b) While the condition is truthy, the code from the loop body is executed |
936 | d) For instance, the loop below outputs 'i' while i < 3 |
937 | `let i = 0; |
938 | `while (i < 3) { // shows 0, then 1, then 2 |
939 | `alert(i); |
940 | `i++;; |
941 | `} |
942 | e) A single execution of the loop body is called an iteration. |
943 | `The loop in the example above makes three iterations |
944 | f) If i++ was missing from the example above, the loop would repeat (in theory) forever. |
945 | g) In practise, the browser provides ways to stop such loops |
946 | `And in server-side JS, we can kill the process |
947 | ^ The 'while' loop (part 2) |
948 | a) Any expression or variable can be a loop condition, not just comparisons |
949 | `The condition is evaluated and converted to a boolean by while |
950 | b) For instance, a shorter way to write while (i != 0) is while (i) |
951 | `let i = 3; |
952 | `while (i) { // when i becomes 0, the condition becomes falsy, and the loop stops |
953 | `alert (i); |
954 | `i--; |
955 | `} |
956 | ^ Curly braces are not required for a single-line body |
957 | a) If the loop body has a single statement, we can omit the curly braces {...} |
958 | `let i = 3; |
959 | `while (i) alert(i--); |
960 | * The 'do...while' loop |
961 | ^ |
962 | a) The condition check can be moved below the loop body using the do...while syntax |
963 | `do { |
964 | `// loop body |
965 | `} while (condition) |
966 | b) The loop will first execute the body, then check the condition, and, while it's truthy, execute it again and again |
967 | c) For example |
968 | `let i = 0; |
969 | `do { |
970 | `alert(i); |
971 | `i++; |
972 | `} while (i < 3); |
973 | d) This form of syntax should only be used when you want the body of the loop to execute at least once regardless of the condition being truthy |
974 | - Usually, the other form is preferred: |
975 | `while (...) {...} |
976 | * The 'for' loop |
977 | ^ |
978 | a) The 'for' loop is more complex, but it's also the most commonly used loop |
979 | - It looks like this |
980 | `for (begin; condition; step) { |
981 | `// loop body |
982 | `} |
983 | b) Let's learn the meaning of these parts by example |
984 | - The loop below runs alert(i) for i from 0 up to (but not including) 3 |
985 | `for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2 |
986 | `alert(i); |
987 | `} |
988 | c) Let's examine the 'for' statement part-by-part |
989 | - part |
990 | `begin |
991 | ,let i = 0; // Executes once upon entering the loop |
992 | `condition |
993 | ,i < 3; // Checked before every loop iteration. If false, the loop stops |
994 | `body |
995 | ,alert(i); // Runs again and again while the condition is truthy |
996 | `step |
997 | ,i++; // Executes after the body on each iteration |
998 | d) The general loop algorithm works like this |
999 | - Run begin |
1000 | `-> (if condition -> run body and run step) |
1001 | `-> (if condition -> run body and run step) |
1002 | `-> (if condition -> run body and run step) |
1003 | `-> ... |
1004 | e) That is, 'begin' executes once, and then it iterates |
1005 | - after each 'condition' test, 'body' and 'step' are executed |
1006 | f) If you're a new to loops, it could help to go back to the example |
1007 | and reproduce how it runs step-by-step on a piece of paper |
1008 | ^ The 'for' loop (part 2) |
1009 | a) Here's exactly what happens in our case |
1010 | - // for (let i = 0; i < 3; i++) alert (i) |
1011 | - // run begin |
1012 | `let i = 0; |
1013 | - // if condition -> run body and run step |
1014 | `if (i < 3) { alert(i); i++ } |
1015 | - // if condition -> run body and run step |
1016 | `if (i < 3) { alert(i); i++ } |
1017 | - // if condition -> run body and run step |
1018 | `if (i < 3) { alert(i); i++ } |
1019 | - // finish, because now i == 3 |
1020 | ^ Inline variable declaration |
1021 | a) Here, the 'counter' variable i is declared right in the loop |
1022 | - This is called an 'inline' variable declaration |
1023 | - Such variables are visible only inside the loop |
1024 | b) for (let i = 0; i < 3; i++) { |
1025 | - alert(i); // 0, 1, 2 |
1026 | - } |
1027 | - alert(i); // error, no such variable |
1028 | c) Instead of defining a variable, we could use an existing one |
1029 | - let i = 0; |
1030 | - for (i = 0; i < 3; i++) { |
1031 | - alert(i); |
1032 | - } |
1033 | - alert(i); // 3, visible, because declared outside of the loop |
1034 | ^ Skipping parts |
1035 | a) Any part of 'for' can be skipped |
1036 | b) For example, we can omit begin if we don't need to do anything at the loop start |
1037 | c) Like here |
1038 | - let i = 0; // we have i already declared and assigned |
1039 | - for (; i < 3; i++) { // no need 'for' begin |
1040 | - alert(i); // 0, 1, 2 |
1041 | - } |
1042 | d) We can also remove the 'step' part |
1043 | - let i = 0; |
1044 | - for (; i < 3;) { |
1045 | - alert(i++); |
1046 | - } |
1047 | e) This makes the loop identical to while (i < 3) |
1048 | f) We can actually remove everything, creating an infinite loop |
1049 | - for (;;) { |
1050 | - // repeats without limits |
1051 | - } |
1052 | g) Please note that the two 'for' semicolons ';' must be present |
1053 | - Otherwise, there would be a syntax error |
1054 | ^ Breaking the loop |
1055 | a) Normally, a loop exits when its condition becomes falsy |
1056 | b) But we can force exits at any time using the special 'break' directive |
1057 | c) For example, the loop below asks the user for a series of numbers, 'breaking' when no number is entered |
1058 | - let sum = 0; |
1059 | - while (true) { |
1060 | - let value = +prompt('Enter a number', ""); |
1061 | - if (!value) break; // (*) |
1062 | - sum += value; |
1063 | - } |
1064 | - alert('Sum: ' + sum); |
1065 | d) The 'break' directive is activated at the line (*) if the user enters an empty line or cancels the input. |
1066 | e) It stops the loop immediately, passing control to the first line after the loop. |
1067 | - Namely, 'alert' |
1068 | e) The combination 'infinite loop + 'break' as needed' is great for situations when a loop's condition must be checked not in the beginning or end of the loop, but in the middle or even in several places of its body |
1069 | ^ Continue to the next iteration |
1070 | a) The 'continue' directive is a 'lighter version' of 'break' |
1071 | b) It doesn't stop the whole loop |
1072 | c) Instead, it stops the current iteration and forces the loop to start a new one (if the condition allows) |
1073 | d) We can use it if we're done with the current iteration and would like to move on to the next one |
1074 | e) The loop below uses 'continue' to output only odd values |
1075 | - for (let i = 0; i < 10; i++) { |
1076 | - // if true, skip the remaining part of the body |
1077 | - if (i % 2 == 0) continue; |
1078 | - alert(i); // 1, then 3, 5, 7, 9 |
1079 | - } |
1080 | f) For even values of 'i', the 'continue' directive stops executing the body and passes control to the next iteration of 'for' (with the next number) |
1081 | g) So the alert is only called for odd values |
1082 | ^ The 'continue' directive helps decrease nesting |
1083 | a) A loop that shows odd values could look like this |
1084 | - for (let i = 0; i < 10; i++) { |
1085 | - if (i % 2) { |
1086 | - alert(i); |
1087 | -} |
1088 | -} |
1089 | b) From a technical point of view, this is identical to the example above |
1090 | c) Surely, we can just wrap the code in an 'if' block instead of using 'continue' |
1091 | d) But as a side-effect, this created one more level of nesting (the alert call inside the curly braces) |
1092 | e) If the code inside of 'if' no longer than a few lines, that may decrease the overall readability |
1093 | ^ No 'break/continue' to the right side of '?' |
1094 | a) Please note that syntax constructs that are not expressions can't be used with the ternary operator '?' |
1095 | b) In particular, directives such an 'break/continue' aren't allowed there |
1096 | c) For example, if we take this code |
1097 | - if (i > 5) { |
1098 | - alert(i); |
1099 | - } else { |
1100 | - continue; |
1101 | - } |
1102 | d) And rewrite it using a question mark '?' |
1103 | - (i > 5) ? alert(i) : continue; // continue isn't allowed here |
1104 | e) It stops working |
1105 | - there's a syntax error |
1106 | f) This is just another reason not to use the question mark operator ? instead of if |
1107 | * Labels for break/continue |
1108 | ^ Part 1 |
1109 | a) Sometimes we need to break out from multiple nested loops at once |
1110 | b) For example, in the code below we loop over 'i' and 'j', prompting for the coordinates (i, j) from (0, 0) to (2, 2) |
1111 | - for (let i = 0; i < 3; i++) { |
1112 | - for (let j = 0; j < 3; j++) { |
1113 | - let input = prompt(`Value at coords (${1}, ${j}`, ""); |
1114 | - // what if we want to exit from here to Done (below)? |
1115 | - } |
1116 | - } |
1117 | - alert('Done'); |
1118 | c) We need a way to stop the process if the user cancels the input |
1119 | d) The ordinary 'break' after 'input' would only break the inner loop |
1120 | e) That's not sufficient - labels, come to the rescue |
1121 | f) A label is an identifier with a colon before a loop |
1122 | - labelName: for (...) { |
1123 | - ... |
1124 | - } |
1125 | ^ Labels for break/continue (Part 2) |
1126 | a) The 'break <labelName> statement in the loop below breaks out to the label |
1127 | - outer: for (let i = 0; i < 3; i++) { |
1128 | - for (let j = 0; j < 3; J++) { |
1129 | - let input = prompt(`Value at coords (${i}, ${j})`, ""); |
1130 | - // if an empty string or canceled, then break out of both loops |
1131 | - if (!input) break outer; // (*) |
1132 | - // do something with the value |
1133 | - } |
1134 | - } |
1135 | - alert('Done'); |
1136 | b) In the code above, break outer looks upwards for the label named 'outer' and breaks out of that loop |
1137 | c) So the control goes straight from (*) to alert('Done') |
1138 | d) We can also move the label onto a separate line |
1139 | - outer: |
1140 | - for (let i = 0; i < 3; i++) {...} |
1141 | e) The 'continue' directive can also be used with a label. |
1142 | f) In this case, code execution jumps to the next iteration of the labeled loop. |
1143 | ^ Labels do not allow to 'jump' anywhere |
1144 | a) Labels don't allow us to jump into an arbitrary place in the code |
1145 | b) For example, it's impossible to do this |
1146 | - break label; // jump to the label below (doesn't work) |
1147 | - label: for (...) |
1148 | c) A break directive must be inside a code block |
1149 | d) Technically, any labelled code block will do, e.g.: |
1150 | - label: { |
1151 | - // ... |
1152 | - break label; // works |
1153 | - // ... |
1154 | - } |
1155 | e) Although, 99,9% of the time 'break' is used inside loops, as we've seen in the examples above |
1156 | f) A 'continue' is only possible from inside a loop |
1157 | ^ Summary |
1158 | a) We covered 3 types of loops |
1159 | - while -- The condition is checked before each iteration |
1160 | - do..while -- The condition is checked after each iteration |
1161 | - for (;;) -- The condition is checked before each iteration, additional settings available |
1162 | b) To make an 'infinite' loop, usually the 'while(true)' construct is used. |
1163 | - Such a loop, just like any other, can be stopped with the 'brea' directive |
1164 | c) If we don't want to do anything in the current iteration and would like to forward to the next one, we can use the 'continue' directive |
1165 | d) 'break/continue' support labels before the loop |
1166 | - A label is the only way for 'break/continue' to escape a nested loop to go to an outer one. |
1167 | * Tasks (Loops) |
1168 | ^ Last loop value |
1169 | a) What is the last value alerted by this code? Why? |
1170 | - let i = 3; |
1171 | - while (i) { |
1172 | - alert(i--); |
1173 | - } |
1174 | b) Answer |
1175 | - Every loop iteration decreases 'i' by 1 |
1176 | - The check while(i) stops the loop when i = 0 |
1177 | - Hence, the steps of the loop form the following sequence ('loop unrolled') |
1178 | c) Answer (continue) |
1179 | - let i = 3; |
1180 | - alert(i--); // shows 3, decreases i to 2 |
1181 | - alert(i--); // shows 2, decreases i to 1 |
1182 | - alert(i--); // shows 1, decreases i to 0 |
1183 | - // done, while(i) check stops the loop |
1184 | ^ Which values does the while loop show? |
1185 | a) For every loop iteration, write down which value it outputs and then compare it with the solution |
1186 | b) Both loops 'alert' the same values, or not? |
1187 | - The prefix form ++1 |
1188 | `let i = 0; |
1189 | `while (++i < 5) alert (i); |
1190 | - The postfix form 1++ |
1191 | `let i = 0; |
1192 | `while (i++ < 5) alert (i); |
1193 | c) Answer. The task demonstrates how postfix/prefix forms can lead to different results when used in comparisons |
1194 | - From 1 to 4 |
1195 | `The first value is i = 1, because ++i first increments i and then returns the new value |
1196 | `So the first comparison is 1 < 5 and then 'alert' shows '1' |
1197 | `Then follow 2, 3, 4 -- the values show up one after another |
1198 | `The comparison always uses the incremented value, because ++ is before the variable |
1199 | `Finally, i = 4 is incremented to 5, the comparison while(5 < 5) fails, and the loop stops |
1200 | `So 5 is not shown |
1201 | - From 1 to 5 |
1202 | `The first value is again i = 1. |
1203 | `The postfix form of i++ increments 'i' and then returns the old value, so the comparison i++ < 5 will use i = 0 (contrary to i++ < 5) |
1204 | `But the alert calls is separate |
1205 | `It's another statement which executes after the increment and the comparison |
1206 | `So it gets the current i = 1 |
1207 | `Then follow 2, 3, 4 |
1208 | `Let's stop on i = 4 |
1209 | `The prefix form ++i would increment it and use 5 in the comparison |
1210 | `But here we have the postfix form i++ |
1211 | `So it increments 'i' to 5, but returns the old value |
1212 | `Hence the comparison is actually while(4 < 5) - true, and the control goes on to alert |
1213 | `The value i = 5 is the last one, because on the next step while(5 < 5) is false |
1214 | ^ Which values get shown by the 'for' loop |
1215 | a) For each loop write down which values it is going to show. Then compare with the answer |
1216 | b) Both loops alert same values or not? |
1217 | - The postfix form |
1218 | `for (let i = 0; i < 5; i++) alert(i); |
1219 | - The prefix form |
1220 | `for (let i = 0; i < 5; ++i) alert(i); |
1221 | c) The answer: from 0 to 4 in both cases |
1222 | - That can be easily deducted from the algorithm of 'for' |
1223 | `Execute once i = 0 before every thing (begin) |
1224 | `Check the condition i < 5 |
1225 | `If 'true' -- executes the loop body alert(i), and then i++ |
1226 | - The increment i++ is separated from the condition i < 5 |
1227 | `That's just another statement |
1228 | - The value returned by the increment is not used here, so there's no difference between ++i and i++ |
Комментарии