diff --git a/1-js/2-first-steps/01-hello-world/1-hello-alert/solution.md b/1-js/2-first-steps/01-hello-world/1-hello-alert/solution.md new file mode 100644 index 00000000..e69de29b diff --git a/1-js/2-first-steps/01-hello-world/1-hello-alert/solution.view/index.html b/1-js/2-first-steps/01-hello-world/1-hello-alert/solution.view/index.html new file mode 100644 index 00000000..45e6744b --- /dev/null +++ b/1-js/2-first-steps/01-hello-world/1-hello-alert/solution.view/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/1-js/2-first-steps/01-hello-world/1-hello-alert/task.md b/1-js/2-first-steps/01-hello-world/1-hello-alert/task.md new file mode 100644 index 00000000..afed6a91 --- /dev/null +++ b/1-js/2-first-steps/01-hello-world/1-hello-alert/task.md @@ -0,0 +1,12 @@ +importance: 5 + +--- + +# Show an alert + +Create a page that shows a message "I'm JavaScript!". + +Do it in a sandbox, or on your hard drive, doesn't matter, just ensure that it works. + +[demo src="solution"] + diff --git a/1-js/2-first-steps/01-hello-world/article.md b/1-js/2-first-steps/01-hello-world/article.md new file mode 100644 index 00000000..9ea5b652 --- /dev/null +++ b/1-js/2-first-steps/01-hello-world/article.md @@ -0,0 +1,85 @@ +# Hello, world! + +In this chapter we'll create a simple script and see it working. + +[cut] + +## The "script" tag + +```smart header="What if I want to move faster?" +In the case if the reader has developed with JavaScript already or has a lot of experience in another language, he can skip detailed explanatins and jump to . There he can find an essense of important features. + +If you have enough time and want to learn things in details then read on :) +``` + +JavaScript programs can be inserted in any place of HTML with the help of the ` +*/!* + +

...After the script.

+ + + + +``` + +```online +You can run the example clicking on a "Play" button in it's right-top corner. +``` + +The ` + ``` + + These comments were supposed to hide the code from an old browser that did't understand a ` + + + + \ No newline at end of file diff --git a/1-js/2-first-steps/02-external-script/1-hello-alert-ext/solution.md b/1-js/2-first-steps/02-external-script/1-hello-alert-ext/solution.md new file mode 100644 index 00000000..f42c41e6 --- /dev/null +++ b/1-js/2-first-steps/02-external-script/1-hello-alert-ext/solution.md @@ -0,0 +1,8 @@ +The HTML code: + +[html src="index.html"] + +For the file `alert.js` in the same folder: + +[js src="alert.js"] + diff --git a/1-js/2-first-steps/02-external-script/1-hello-alert-ext/task.md b/1-js/2-first-steps/02-external-script/1-hello-alert-ext/task.md new file mode 100644 index 00000000..fe38de40 --- /dev/null +++ b/1-js/2-first-steps/02-external-script/1-hello-alert-ext/task.md @@ -0,0 +1,9 @@ +importance: 5 + +--- + +# Show an alert with an external script + +Take the solution of the previous task . Modify it by extracting the script content into an external file `alert.js`, residing in the same folder. + +Open the page, ensures that the alert works. diff --git a/1-js/2-first-steps/02-external-script/2-async-defer-first/solution.md b/1-js/2-first-steps/02-external-script/2-async-defer-first/solution.md new file mode 100644 index 00000000..0513e5fc --- /dev/null +++ b/1-js/2-first-steps/02-external-script/2-async-defer-first/solution.md @@ -0,0 +1,5 @@ +Answers: + +1. The first is `big.js`, that's a normal sequence for external ` + +``` + +What if we add `async`? + +```html + + +``` + +What if we switch to `defer`? + +```html + + +``` + diff --git a/1-js/2-first-steps/02-external-script/article.md b/1-js/2-first-steps/02-external-script/article.md new file mode 100644 index 00000000..78753c04 --- /dev/null +++ b/1-js/2-first-steps/02-external-script/article.md @@ -0,0 +1,251 @@ +# External scripts + +If we have a lot of JavaScript code, we can it put it into a separate file. + +The script file is attached to HTML like this: + +```html + +``` + +Here `/path/to/script.js` is an absolute path to the file with the script (from the site root). + +It is also possible to provide a path relative to the current page. For instance, `src="script.js"` would mean a file `"script.js"` from the current folder. + +We can give a full URL al well, for instance: + +```html + +``` + +To attach several scripts, use multiple tags: + +```html + + +… +``` + +```smart +As a rule, only simplest scripts are put into HTML. More complex ones reside in separate files. + +The benefit of a separate file is that the browser will download it and then store in its [cache](https://en.wikipedia.org/wiki/Web_cache). + +After it, other pages which want the same script will take it from the cache instead of downloading it. So the file is actually downloaded only once. + +That saves traffic and makes pages faster. +``` + +````warn header="If `src` is set, the script content is ignored." +A single ` +``` + +We must choose: either it's an external ` + +``` +```` + +## Asynchronous scripts: defer/async + +Browser loads and shows HTML gradually as it comes. That's clearly noticeable on the slow internet connection. The browser doesn't wait for the page to load fully. It shows the part that has been loaded already, and then adds content to it as it loads. + +As we noted before, when the browser meets a ` +*/!* + +

Rabbits counted!

+ + + +``` + +The behavior is called "synchronous". Usually it causes no problems, but there's an important consequence. + +**If the script is external, then until the browser executes it, it can't show the page below.** + +So, in this document, until `big.js` loads and executes, the `` content is hidden: + +```html + + +*!* + +*/!* + + + This text is not shown until the browser executes big.js. + + +``` + +The question is -- do we really want to hide the body until the script finishes? + +Most of time, we don't. + +Sometimes, a script may contain a very important code that really must be loaded before the rest of the page is parsed (and the scripts below executed). But that's only sometimes. + +Usually a visitor should see the page content while the script is loading. + +There are situations when such blocking is even dangerous. For example, when we attach a script from the banner system, or a 3rd-party integration code. + +Like this: + +```html run height=100 +Wait. The text belown will shown up only after the script executes. + + + +

…Important information!

+``` + +It's just wrong that the rest of the page is not shown until the banner is loaded. And what if their server is overloaded and responds slowly? Our visitors will have wait even more, maybe even leave for another, faster site. + +So, how to "fix" the blocking behavior? + +Our first attempt could be to put all such scripts to the bottom of the ``, after all content. + +But the solution is not perfect: + +1. The script won't start loading until the whole page loads. If the page is large, then the delay may be significant. We'd like the browser to start loading a script early, but still do not block the page. +2. If there is more than one script at the bottom of the page, and the first script is slow, then the second one will have to wait for it. Browser executes only one ` + +

…Important information!

+``` + +Now if we run it, we'll see that the whole document is displayed immediately, and the external script runs when it loads. + +## Defer vs Async + +Please note that there is one similarity and two differences between `defer` and `async`. + +1. **Both attributes allow the browser to show the page without waiting for the script to load.** +2. **Deferred scripts keep the relative order, while async scripts do not.** + + For example, in the code below (with `async`) there are two scripts. The one which loads first will run first. + + ```html + + + ``` + + If `2.js` is bigger than `1.js`, it may happen that `2.js` will run before `1.js`. That's normal. Async scripts are totally independent. + + And in the code below `defer` is used, which forces browser to keeps execution order. Even if `2.js` loads first, it waits and executes after `1.js`: + + ```html + + + ``` + + This feature of "deferred" scripts is important when `2.js` relies on the result of `1.js` and we must be sure that the order is determined. + +3. **A script with `defer` always waits for the HTML-document to fully load. The `async` script runs immediately as it loads.** + + For instance, when the document is large, like: + + ```html + + + + A long long text. Many words. + ... + ``` + + ...Here `async.js` executes when it loads -- possibly, before the document is fully loaded. In contrast, `defer.js` always waits for the full document to be ready. + + The choice between `defer` and `async` here depends on our intentions. Sometimes a script doesn't need the document at all (like a web counter), so it should execute ASAP. In this case `async` is superb. + + And in another case a script may need the whole document to do some work with it. Then `defer` is preferable. + +## Summary + +- Scripts in an external file can be inserted on the page via ``. +- Normally, the browser doesn't show the document after the script until it executes. Unless the script has `async` or `defer` attributes. +- Both `async` and `defer` allow the browser to start script loading and then continue to parse/show the page. They only work on external scripts. +- The difference is that `defer` keeps the relative script order and always executes after the document is fully loaded. In contrast, `async` script executes when it loads, without any conditions. + +Before inserting an external ` + + + + + diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2@2x.png b/1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2@2x.png new file mode 100644 index 00000000..a4b94874 Binary files /dev/null and b/1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2@2x.png differ diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/solution.md b/1-js/2-first-steps/11-ifelse/2-check-standard/solution.md new file mode 100644 index 00000000..99dea945 --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/2-check-standard/solution.md @@ -0,0 +1,4 @@ + + +[html run src="ifelse_task2/index.html"] + diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/task.md b/1-js/2-first-steps/11-ifelse/2-check-standard/task.md new file mode 100644 index 00000000..46fe05dc --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/2-check-standard/task.md @@ -0,0 +1,14 @@ +importance: 2 + +--- + +# The name of JavaScript + +Using the `if..else` construct, write the code which asks: 'What is the "official" name of JavaScript?' + +If the visitor enters "ECMAScript", then output "Right!", otherwise -- output: "Didn't know? ECMAScript!" + +![](ifelse_task2.png) + +[demo src="ifelse_task2"] + diff --git a/1-js/2-first-steps/11-ifelse/3-sign/if_sign/index.html b/1-js/2-first-steps/11-ifelse/3-sign/if_sign/index.html new file mode 100644 index 00000000..f168360d --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/3-sign/if_sign/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/1-js/2-first-steps/11-ifelse/3-sign/solution.md b/1-js/2-first-steps/11-ifelse/3-sign/solution.md new file mode 100644 index 00000000..262a605c --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/3-sign/solution.md @@ -0,0 +1,14 @@ + + +```js run +let value = prompt('Type a number', 0); + +if (value > 0) { + alert( 1 ); +} else if (value < 0) { + alert( -1 ); +} else { + alert( 0 ); +} +``` + diff --git a/1-js/2-first-steps/11-ifelse/3-sign/task.md b/1-js/2-first-steps/11-ifelse/3-sign/task.md new file mode 100644 index 00000000..0c5d0e00 --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/3-sign/task.md @@ -0,0 +1,15 @@ +importance: 2 + +--- + +# Show the sign + +Using `if..else`, write the code which gets a number via `prompt` and then shows in `alert`: + +- `1`, if the value is greater than zero, +- `-1`, if less than zero, +- `0`, if equals zero. + +In this task we assume that the input is always a number. + +[demo src="if_sign"] diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task.png b/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task.png new file mode 100644 index 00000000..8b54dc83 Binary files /dev/null and b/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task.png differ diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task@2x.png b/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task@2x.png new file mode 100644 index 00000000..92001dfe Binary files /dev/null and b/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task@2x.png differ diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/solution.md b/1-js/2-first-steps/11-ifelse/4-check-login/solution.md new file mode 100644 index 00000000..acfac25e --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/4-check-login/solution.md @@ -0,0 +1,31 @@ + + +```js run demo +let userName = prompt('Who's there?', ''); + +if (userName == 'Admin') { + + let pass = prompt('Password?', ''); + + if (pass == 'TheMaster') { + alert( 'Welcome!' ); + } else if (pass == null || pass == '') { // (*) + alert( 'Canceled.' ); + } else { + alert( 'Wrong password' ); + } + +} else if (userName == null || userName == '') { // (**) + + alert( 'Canceled' ); + +} else { + + alert( "I don't know you" ); + +} +``` + +Please note the `if` check in lines `(*)` and `(**)`. Every browser except Safari returns `null` when the input is canceled, and Safari returns an empty string. So we must treat them same for compatibility. + +Also note the vertical indents inside the `if` blocks. They are technically not required, but make the code more readable. diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/task.md b/1-js/2-first-steps/11-ifelse/4-check-login/task.md new file mode 100644 index 00000000..4b371a1d --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/4-check-login/task.md @@ -0,0 +1,23 @@ +importance: 3 + +--- + +# Check the login + +Write the code which asks for a login with `prompt`. + +If the visitor enters `"Admin"`, then `prompt` for a password, if the input is an empty line or `key:Esc` -- show "Canceled.", if it's another string -- then show "I don't know you". + +The password is checked as follows: + +- If it equals "TheMaster", then show "Welcome!", +- Another string -- show "Wrong password", +- For an empty string or cancelled input, show "Canceled." + +The schema: + +![](ifelse_task.png) + +Please use nested `if` blocks. Mind the overall readability of the code. + +[demo] diff --git a/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/solution.md b/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/solution.md new file mode 100644 index 00000000..638ce81f --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/solution.md @@ -0,0 +1,6 @@ + + +```js +result = (a + b < 4) ? 'Below' : 'Over'; +``` + diff --git a/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/task.md b/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/task.md new file mode 100644 index 00000000..684e239f --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/task.md @@ -0,0 +1,16 @@ +importance: 5 + +--- + +# Rewrite 'if' into '?' + +Rewrite this `if` using the ternary operator `'?'`: + +```js +if (a + b < 4) { + result = 'Below'; +} else { + result = 'Over'; +} +``` + diff --git a/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/solution.md b/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/solution.md new file mode 100644 index 00000000..6d68e29d --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/solution.md @@ -0,0 +1,9 @@ + + +```js +let message = (login == 'Employee') ? 'Hello' : + (login == 'Director') ? 'Greetings' : + (login == '') ? 'No login' : + ''; +``` + diff --git a/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/task.md b/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/task.md new file mode 100644 index 00000000..91504cf4 --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/task.md @@ -0,0 +1,24 @@ +importance: 5 + +--- + +# Rewrite 'if..else' into '?' + +Rewrite `if..else` using multiple ternary operators `'?'`. + +For readability, it's recommended to split the code span over lines. + +```js +let message; + +if (login == 'Employee') { + message = 'Hello'; +} else if (login == 'Director') { + message = 'Greetings'; +} else if (login == '') { + message = 'No login'; +} else { + message = ''; +} +``` + diff --git a/1-js/2-first-steps/11-ifelse/article.md b/1-js/2-first-steps/11-ifelse/article.md new file mode 100644 index 00000000..499d1954 --- /dev/null +++ b/1-js/2-first-steps/11-ifelse/article.md @@ -0,0 +1,237 @@ +# Conditional operators: if, '?' + +Sometimes we need to perform different actions basing on a condition. + +There's an `if` operator for that and also the "question mark" operator: `"?"` for conditional evaluation. + +[cut] + +## The "if" operator + +The "if" operator gets a condition, evaluates it and -- if the result is `true` -- executes the code. + +For example: + +```js run +let year = prompt('In which year was ECMAScript-2015 specification published?', ''); + +*!* +if (year == 2015) alert( 'You are right!' ); +*/!* +``` + +In the example above, the condition is a simple equality check: `year == 2015`, but it can be much more complex. + +If there's more than one command to execute -- we can use a code block in figure brackets: + +```js +if (year == 2015) { + alert( "That's correct!" ); + alert( "You're so smart!" ); +} +``` + +It is recommended to use figure brackets every time with `if`, even if there's only one command. That improves readability. + +## Boolean conversion + +The `if (…)` operator evaluates the condition in brackets and converts it to boolean type. + +Let's recall the rules. In the logical context: + +- A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`, +- Other values -- `true`. + +So, the code under this condition would never execute: + +```js +if (0) { // 0 is falsy + ... +} +``` + +...And inside this condition -- always works: + +```js +if (1) { // 1 is truthy + ... +} +``` + +We can also pass a pre-evaluated logical value to `if`, like here: + +```js +let cond = (year == 2015); // equality evaluates to true or false + +if (cond) { + ... +} +``` + +## The "else" clause + +The `if` operator may contain an optional "else" block. It executes when the condition is wrong. + +For example: +```js run +let year = prompt('In which year was ECMAScript-2015 specification published?', ''); + +if (year == 2015) { + alert( 'You guessed it right!' ); +} else { + alert( 'How can you be so wrong?' ); // any value except 2015 +} +``` + +## Several conditions: "else if" + +Sometimes we'd like to test several variants of a condition. There's an `else if` clause for that. + +For example: + +```js run +let year = prompt('In which year was ECMAScript-2015 specification published?', ''); + +if (year < 2015) { + alert( 'Too early...' ); +} else if (year > 2015) { + alert( 'Too late' ); +} else { + alert( 'Exactly!' ); +} +``` + +In the code above JavaScript first checks `year < 2015`, if it is falsy then goes to the next condition `year > 2015`. Any number of `else if` may follow with an optional last `else`. + +## Ternary operator '?' + +Sometimes we need to assign a variable depending on a condition. + +For instance: + +```js run no-beautify +let hasAccess; +let age = prompt('How old are you?', ''); + +*!* +if (age > 18) { + hasAccess = true; +} else { + hasAccess = false; +} +*/!* + +alert(hasAccess); +``` + +The so called "ternary" or "question mark" operator allows to do that shorter and simpler. + +The operator is represented by a question mark `"?"`. The formal term "ternary" means that the operator has 3 operands. It is actually the one and only operator in JavaScript which has that many. + +The syntax is: +```js +let result = condition ? value1 : value2 +``` + +The `condition` is evaluated, if it's truthy then `value1` is returned, otherwise -- `value2`. + +For example: + +```js +let hasAccess = (age > 18) ? true : false; +``` + +We can omit brackets around `age > 14`, because the question mark operator has a low precedence. It executes after comparisons, so: + +```js +// the same +let hasAccess = age > 18 ? true : false; +``` + +...But brackets make the code more readable. So it's recommended to put them. + +````smart +In the described case it is possible to evade the question mark operator, because the comparison by itself returns `true/false`: + +```js +// the same +let hasAccess = age > 18; +``` +```` + +## Multiple '?' + +A sequence of question mark `"?"` operators allows to return a value depending on more than one condition. + +For instance: +```js run +let age = prompt('age?', 18); + +let message = (age < 3) ? 'Hi, baby!' : + (age < 18) ? 'Hello!' : + (age < 100) ? 'Greetings!' : + 'What an unusual age!'; + +alert( message ); +``` + +It may be difficult at first to grasp what's going on. But looking more carefully we note that it's just an ordinary sequence of tests. + +1. The first question mark checks for `age < 3`. +2. If true -- returns `'Hi, baby!'`, otherwise -- goes to the right side of the colon `":"` and checks for `age < 18`. +3. If that's true -- returns `'Hello!'`, otherwise checks for `age < 100` and returns `'Greetings!'` if that is so... +4. At last, if all checks are falsy, the `message` becomes `'What an unusual age!'`. + +The same logic using `if..else`: + +```js +if (age < 3) { + message = 'Hi, baby!'; +} else if (a < 18) { + message = 'Hello!'; +} else if (age < 100) { + message = 'Greetings!'; +} else { + message = 'What an unusual age!'; +} +``` + +## Non-traditional use of '?' + +Sometimes the question mark `'?'` is used as a replacement for `if`: + +```js run no-beautify +let company = prompt('Which company created JavaScript?', ''); + +*!* +(company == 'Netscape') ? + alert('Right!') : alert('Wrong.'); +*/!* +``` + +Depending on the condition `company == 'Netscape'`, either the first or the second part after `"?"` gets executed and shows the alert. + +We don't assign a result to a variable here, cause the `alert` doesn't return anything anyway, our purpose is only to execute it. + +**It is not recommended to use the question mark operator in this way.** + +The notation seem to be shorter than `if`, that appeals to some programmers. But it is less readable. + +Here's the same with `if` for comparison: + +```js run no-beautify +let company = prompt('Which company created JavaScript?', ''); + +*!* +if (company == 'Netscape') { + alert('Right!'); +} else { + alert('Wrong.'); +} +*/!* +``` + +Our eyes scan the code vertically. The constructs which span several lines are easier to understand than a long horizontal instruction set. + +The idea of a question mark `'?'` is to return one or another value depending on the condition. Please use it for exactly that. There's `if` to execute different branches of the code. + diff --git a/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/solution.md b/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/solution.md new file mode 100644 index 00000000..8869d32e --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/solution.md @@ -0,0 +1,6 @@ +The answer is `2`, that's the first truthy value. + +```js run +alert( null || 2 || undefined ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/task.md b/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/task.md new file mode 100644 index 00000000..eda8c905 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/task.md @@ -0,0 +1,12 @@ +importance: 5 + +--- + +# What's the result of OR? + +What the code below is going to output? + +```js +alert( null || 2 || undefined ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/2-alert-or/solution.md b/1-js/2-first-steps/12-logical-ops/2-alert-or/solution.md new file mode 100644 index 00000000..aa388e02 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/2-alert-or/solution.md @@ -0,0 +1,13 @@ +The answer: first `1`, then `2`. + +```js run +alert( alert(1) || 2 || alert(3) ); +``` + +The call to `alert` does not return a value. Or, in other words, it returns `undefined`. + +1. The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`. +2. The `alert` returns `undefined`, so OR goes on to the second operand in it's search of a truthy value. +3. The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert. + +There will be no `3`, because the evaluation does not reach `alert(3)`. diff --git a/1-js/2-first-steps/12-logical-ops/2-alert-or/task.md b/1-js/2-first-steps/12-logical-ops/2-alert-or/task.md new file mode 100644 index 00000000..bc622abf --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/2-alert-or/task.md @@ -0,0 +1,12 @@ +importance: 3 + +--- + +# What's the result of OR'ed alerts? + +What the code below will output? + +```js +alert( alert(1) || 2 || alert(3) ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/solution.md b/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/solution.md new file mode 100644 index 00000000..5c2455ef --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/solution.md @@ -0,0 +1,6 @@ +The answer: `null`, because it's the first falsy value from the list. + +```js run +alert( 1 && null && 2 ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/task.md b/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/task.md new file mode 100644 index 00000000..53ec7874 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/task.md @@ -0,0 +1,12 @@ +importance: 5 + +--- + +# What is the result of AND? + +What this code is going to show? + +```js +alert( 1 && null && 2 ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/4-alert-and/solution.md b/1-js/2-first-steps/12-logical-ops/4-alert-and/solution.md new file mode 100644 index 00000000..b6fb10d7 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/4-alert-and/solution.md @@ -0,0 +1,10 @@ +The answer: `1`, and then `undefined`. + +```js run +alert( alert(1) && alert(2) ); +``` + +The call to `alert` returns `undefined` (it just shows a message, so there's no meaningful return). + +Because of that, `&&` evaluates the left operand (outputs `1`), and immediately stops, because `undefined` is a falsy value. And `&&` looks for a falsy value and returns it, so it's done. + diff --git a/1-js/2-first-steps/12-logical-ops/4-alert-and/task.md b/1-js/2-first-steps/12-logical-ops/4-alert-and/task.md new file mode 100644 index 00000000..69f877b9 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/4-alert-and/task.md @@ -0,0 +1,12 @@ +importance: 3 + +--- + +# What is the result of AND'ed alerts? + +What will this code show? + +```js +alert( alert(1) && alert(2) ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/5-alert-and-or/solution.md b/1-js/2-first-steps/12-logical-ops/5-alert-and-or/solution.md new file mode 100644 index 00000000..32a8ccf2 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/5-alert-and-or/solution.md @@ -0,0 +1,16 @@ +The answer: `3`. + +```js run +alert( null || 2 && 3 || 4 ); +``` + +The precedence of AND `&&` is higher than `||`, so it executes first. + +The result of `2 && 3 = 3`, so the expression becomes: + +``` +null || 3 || 4 +``` + +Now the result if the first truthy value: `3`. + diff --git a/1-js/2-first-steps/12-logical-ops/5-alert-and-or/task.md b/1-js/2-first-steps/12-logical-ops/5-alert-and-or/task.md new file mode 100644 index 00000000..4b2ad046 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/5-alert-and-or/task.md @@ -0,0 +1,12 @@ +importance: 5 + +--- + +# The result of OR AND OR + +What will be the result? + +```js +alert( null || 2 && 3 || 4 ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/solution.md b/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/solution.md new file mode 100644 index 00000000..87c733b2 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/solution.md @@ -0,0 +1,6 @@ + + +```js +if (age >= 14 && age <= 90) +``` + diff --git a/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/task.md b/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/task.md new file mode 100644 index 00000000..cc00ca9f --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/task.md @@ -0,0 +1,9 @@ +importance: 3 + +--- + +# Check the range between + +Write an "if" condition to check that `age` is between `14` and `90` inclusively. + +"Inclusively" means that `age` can reach the edges `14` or `90`. diff --git a/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/solution.md b/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/solution.md new file mode 100644 index 00000000..d1946a96 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/solution.md @@ -0,0 +1,12 @@ +The first variant: + +```js +if (!(age >= 14 && age <= 90)) +``` + +The second variant: + +```js +if (age < 14 || age > 90) +``` + diff --git a/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/task.md b/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/task.md new file mode 100644 index 00000000..7c22d6ad --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/task.md @@ -0,0 +1,9 @@ +importance: 3 + +--- + +# Check the range outside + +Write an `if` condition to check that `age` is NOT between 14 and 90 inclusively. + +Create two variants: the first one using NOT `!`, the second one -- without it. diff --git a/1-js/2-first-steps/12-logical-ops/8-if-question/solution.md b/1-js/2-first-steps/12-logical-ops/8-if-question/solution.md new file mode 100644 index 00000000..21050975 --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/8-if-question/solution.md @@ -0,0 +1,20 @@ +The answer: the first and the third will execute. + +Details: + +```js run +// Runs. +// The result of -1 || 0 = -1, truthy +if (-1 || 0) alert( 'first' ); + +// Doesn't run +// -1 && 0 = 0, falsy +if (-1 && 0) alert( 'second' ); + +// Executes +// Operator && has a higher precedence than || +// so -1 && 1 executes first, giving us the chain: +// null || -1 && 1 -> null || 1 -> 1 +if (null || -1 && 1) alert( 'third' ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/8-if-question/task.md b/1-js/2-first-steps/12-logical-ops/8-if-question/task.md new file mode 100644 index 00000000..f824779b --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/8-if-question/task.md @@ -0,0 +1,16 @@ +importance: 5 + +--- + +# A question about "if" + +Which of these `alert`s are going to execute? + +What will be the results of the expressions inside `if(...)`? + +```js +if (-1 || 0) alert( 'first' ); +if (-1 && 0) alert( 'second' ); +if (null || -1 && 1) alert( 'third' ); +``` + diff --git a/1-js/2-first-steps/12-logical-ops/article.md b/1-js/2-first-steps/12-logical-ops/article.md new file mode 100644 index 00000000..4bc82f4f --- /dev/null +++ b/1-js/2-first-steps/12-logical-ops/article.md @@ -0,0 +1,308 @@ +# Logical operators + +There are three logical operators in JavaScript: `||` (OR), `&&` (AND), `!` (NOT). + +Although they are called "logical", they can be applied to values of any type, not only boolean. The result can also be of any type. + +Let's see the details. + +[cut] + +## || (OR) + +The "OR" operator is represented with two vertical line symbols: + +```js +result = a || b; +``` + +In classical programming, logical OR is meant to manipulate boolean values. If any of it's arguments is `true`, then it returns `true`, otherwise -- returns `false`. + +In JavaScript the operator is a little bit more tricky and powerful. But first let's see what happens with logical values. + +A table of possible logical combinations: + +```js run +alert( true || true ); // true +alert( false || true ); // true +alert( true || false ); // true +alert( false || false ); // false +``` + +As we can see, the result is always `true` except for the case when both operands are `false`. + +If an operand is not boolean, then it's converted to boolean for the evaluation. + +For instance, a number `1` is treated as `true`, a number `0` -- as `false`: + +```js run +if (1 || 0) { // works just like if( true || false ) + alert( 'truthy!' ); +} +``` + +Most of time, OR `||` is used in the `if` expression to test if *any* of given conditions is correct. + +For example: + +```js run +let hour = 9; + +*!* +if (hour < 10 || hour > 18) { +*/!* + alert( 'The office is closed.' ); +} +``` + +We can pass more conditions: + +```js run +let hour = 12; +let isWeekend = true; + +if (hour < 10 || hour > 18 || isWeekend) { + alert( 'The office is closed.' ); // it is weekend +} +``` + +## OR seeks the first truthy value + +The logic described above is somewhat classical. Now let's bring in the "extra" features of JavaScipt. + +The extended algorithm works as follows. + +Given multiple OR'ed values: + +```js +result = value1 || value2 || value3; +``` + +The OR `"||"` operator is doing the following: + +- Evalutes operands from left to right. +- For each value converts it to boolean and stops immediately returning it if it's true. +- The value is returned in it's original form, without the conversion. + +In other words, it returns the first truthy value or the last one if no such value found. + +For instance: + +```js run +alert( 1 || 0 ); // 1 (is truthy) +alert( true || 'no matter what' ); // (true is truthy) + +alert( null || 1 ); // 1 (1 is the first truthy) +alert( null || 0 || 1 ); // 1 (the first truthy) +alert( undefined || null || 0 ); // 0 (all falsy, returns the last value) +``` + +This logic does not contradict to what was spoken above. If you check this behavior with the boolean table, you see that it still works the same. + +But there leads to some interesting usages compared to a "pure, classical, boolean-only OR". + +1. **Getting the first truthy value from the list of variables or expressions.** + + Imagine we have several variables, which can either contain the data or be `null/undefined`. And we need to choose the first one with data. + + Using OR `||` for that: + + ```js run + let currentUser = null; + let defaultUser = "John"; + + *!* + let name = currentUser || defaultUser || "unnamed"; + */!* + + alert( name ); // selects "John" – the first truthy value + ``` + + If both `currentUser` and `defaultUser` were falsy then `"unnamed"` would be the result. +2. **Short-circuit evaluation.** + + Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. The process is called "a short-circuit evaluation", because it goes as short as possible from left to right. + + This is clearly seen when the expression given as the second argument has a side effect. Like a variable assignment. + + If we run the example below, `x` would not get assigned: + + ```js run no-beautify + let x; + + *!*true*/!* || (x = 1); + + alert(x); // undefined, because (x = 1) not evaluated + ``` + + ...And if the first argument were `false`, then `OR` would goes on and evaluate the second one thus running the assignment: + + ```js run no-beautify + let x; + + *!*false*/!* || (x = 1); + + alert(x); // 1 + ``` + + An assignment is a simple case, other side effects can be involved. + + As we can see, such use case is a "shorter way to do `if`". The first operand is converted to boolean and if it's false then the second one is evaluated. + + Most of time it's better to use `if` for that for code clarity. + +## && (AND) + +The AND operator is represented with two ampersands `&&`: + +```js +result = a && b; +``` + +In classic programming AND returns `true` if both operands are truthy and `false` -- otherwise: + +```js run +alert( true && true ); // true +alert( false && true ); // false +alert( true && false ); // false +alert( false && false ); // false +``` + +An example with `if`: + +```js run +let hour = 12; +let minute = 30; + +if (hour == 12 && minute == 30) { + alert( 'Time is 12:30' ); +} +``` + +Just as for OR, any value is allowed as an operand of AND and gets converted to a boolean in the process: + +```js run +if (1 && 0) { // evaluated as true && false + alert( "won't work, because the result is falsy" ); +} +``` + + +## AND seeks the first falsy value + +Given multiple AND'ed values: + +```js +result = value1 && value2 && value3; +``` + +The AND `"&&"` operator is doing the following: + +- Evalutes operands from left to right. +- For each value converts it to a boolean. If the result is `false`, stops and returns it without conversion. +- If values finished (all are truthy), returns the last one. + +In other words, AND returns the first falsy value or the last one if none found. + +The rules above are similar to OR. The difference is that AND returns the first *falsy* value while OR returns the first *truthy* one. + +Examples: + +```js run +// if the first operand is truthy, +// && returns the second one. +alert( 1 && 0 ); // 0 +alert( 1 && 5 ); // 5 + +// now the first operand is falsy, +// it is returned, and the second one is ignored +alert( null && 5 ); // null +alert( 0 && "no matter what" ); // 0 +``` + +We can also pass several values in a row. See how the first falsy one is returned: + +```js run +alert( 1 && 2 && null && 3 ); // null +``` + +...And now when all of them are truthy: + +```js run +alert( 1 && 2 && 3 ); // 3, the last one +``` + +````smart header="AND `&&` executes before OR `||`" +The precedence of the AND `&&` operator is higher than OR `||`, so it executes before OR. + +In the code below `1 && 0` is calculated first: + +```js run +alert( 5 || 1 && 0 ); // 5 +``` +```` + +Just like OR, the AND `&&` operator can sometimes replace `if`. + +For instance: + +```js run +let x = 1; + +(x > 0) && alert( 'Greater than zero!' ); +``` + +The action in the right part of `&&` would execute only if the evaluation reaches it. That is: only if `(x > 0)` is true. + +So we basically have an analogue for: + +```js run +let x = 1; + +if (x > 0) { + alert( 'Greater than zero!' ); +} +``` + +The variant with `&&` appears to be shorter. But `if` is more obvious and tends to be a little bit more readable. + +So it is recommended to use every construct for it's purpose. Use `if` if we want if. And use `&&` if we want AND. + +## ! (NOT) + +The boolean NOT operator is represented with an exclamation sign `"!"`. + +The syntax is one of the simplest: + +```js +result = !value; +``` + +The operator accepts a single argument and does the following: + +1. Converts the operand to boolean type: `true/false`. +2. Returns an inverse value. + +For instance: + +```js run +alert( !true ); // false +alert( !0 ); // true +``` + +A double NOT is sometimes used for converting a value to boolean type: + +```js run +alert( !!"non-empty string" ); // true +alert( !!null ); // false +``` + +That is: the first NOT converts the value to boolean and returns the inverse, and the second NOT inverses it again, so we have a plain value-to-boolean conversion. + +There's a little more verbose to do the same -- a built-in `Boolean` function: + +```js run +alert( Boolean("non-empty string") ); // true +alert( Boolean(null) ); // false +``` + diff --git a/1-js/2-first-steps/13-while-for/1-loop-last-value/solution.md b/1-js/2-first-steps/13-while-for/1-loop-last-value/solution.md new file mode 100644 index 00000000..ef1da254 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/1-loop-last-value/solution.md @@ -0,0 +1,26 @@ +The answer: `1`. + +```js run +let i = 3; + +while (i) { + alert( i-- ); +} +``` + +Every loop iteration decreases `i` by `1`. The check `while(i)` stops the loop when `i = 0`. + +Hence, the steps of the loop make the following sequence ("loop unrolled"): + +```js +let i = 3; + +alert(i--); // shows 3, decreases i to 2 + +alert(i--) // shows 2, decreases i to 1 + +alert(i--) // shows 1, decreases i to 0 + +// done, while(i) check stops the loop +``` + diff --git a/1-js/2-first-steps/13-while-for/1-loop-last-value/task.md b/1-js/2-first-steps/13-while-for/1-loop-last-value/task.md new file mode 100644 index 00000000..bb57126c --- /dev/null +++ b/1-js/2-first-steps/13-while-for/1-loop-last-value/task.md @@ -0,0 +1,16 @@ +importance: 3 + +--- + +# Last loop value + +What is be the last value alerted by this code? Why? + +```js +let i = 3; + +while (i) { + alert( i-- ); +} +``` + diff --git a/1-js/2-first-steps/13-while-for/2-which-value-while/solution.md b/1-js/2-first-steps/13-while-for/2-which-value-while/solution.md new file mode 100644 index 00000000..540a0258 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/2-which-value-while/solution.md @@ -0,0 +1,31 @@ +The task demonstrates how postfix/prefix forms can lead to different results when used in comparisons. + +1. **From 1 to 4** + + ```js run + let i = 0; + while (++i < 5) alert( i ); + ``` + + The first value is `i=1`, because `++i` first increments `i` and then returns the new value. So the first comparison is `1 < 5` and the `alert` shows `1`. + + Then follow `2,3,4…` -- the values show up one after another. The comparison always uses the incremented value, because `++` is before the variable. + + Finally, `i=4` is incremented to `5`, the comparison `while(5 < 5)` fails and the loop stops. So `5` is not shown. +2. **From 1 to 5** + + ```js run + let i = 0; + while (i++ < 5) alert( i ); + ``` + + The first value is again `i=1`. 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`). + + But the `alert` call is separate. It's another statement which executes after the increment and the comparison. So it gets the current `i=1`. + + Then follow `2,3,4…` + + Let's stop on `i=4`. The prefix form `++i` would increment it and use `5` in the comparison. But here we have the postfix form `i++`. So it increments `i` to `5`, but returns the old value. Hence the comparison is actually `while(4 < 5)` -- true, and the control goes on to `alert`. + + The value `i=5` is the last one, because on the next step `while(5 < 5)` is false. + diff --git a/1-js/2-first-steps/13-while-for/2-which-value-while/task.md b/1-js/2-first-steps/13-while-for/2-which-value-while/task.md new file mode 100644 index 00000000..0d10f6ce --- /dev/null +++ b/1-js/2-first-steps/13-while-for/2-which-value-while/task.md @@ -0,0 +1,23 @@ +importance: 4 + +--- + +# Which values shows the while? + +For every loop, scribe down which values it shows, in your opinion. + +And then compare with the answer. + +1. The prefix form `++i`: + + ```js + let i = 0; + while (++i < 5) alert( i ); + ``` +2. The postfix form `i++` + + ```js + let i = 0; + while (i++ < 5) alert( i ); + ``` + diff --git a/1-js/2-first-steps/13-while-for/3-which-value-for/solution.md b/1-js/2-first-steps/13-while-for/3-which-value-for/solution.md new file mode 100644 index 00000000..3255310a --- /dev/null +++ b/1-js/2-first-steps/13-while-for/3-which-value-for/solution.md @@ -0,0 +1,17 @@ +**The answer: from `0` to `4` in both cases.** + +```js run +for (let i = 0; i < 5; ++i) alert( i ); + +for (let i = 0; i < 5; i++) alert( i ); +``` + +That can be easily deducted from the algorithm of `for`: + +1. Execute once `i=0` before everything. +2. Check the condition `i<5` +3. If `true` -- execute the loop body `alert(i)`, and then `i++` + +The increment `i++` is separated from the condition check (2). That's just another statement. + +The value returned by the increment is not used here, so there's no difference between `i++` and `++i`. diff --git a/1-js/2-first-steps/13-while-for/3-which-value-for/task.md b/1-js/2-first-steps/13-while-for/3-which-value-for/task.md new file mode 100644 index 00000000..c895f88c --- /dev/null +++ b/1-js/2-first-steps/13-while-for/3-which-value-for/task.md @@ -0,0 +1,21 @@ +importance: 4 + +--- + +# Which values get shown by the "for" loop? + +For each loop scribe down which values it is going to show. + +Then compare with the answer. + +1. The postfix form: + + ```js + for (let i = 0; i < 5; i++) alert( i ); + ``` +2. The prefix form: + + ```js + for (let i = 0; i < 5; ++i) alert( i ); + ``` + diff --git a/1-js/2-first-steps/13-while-for/4-for-even/solution.md b/1-js/2-first-steps/13-while-for/4-for-even/solution.md new file mode 100644 index 00000000..e8e66bb4 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/4-for-even/solution.md @@ -0,0 +1,11 @@ + + +```js run demo +for (let i = 2; i <= 10; i++) { + if (i % 2 == 0) { + alert( i ); + } +} +``` + +We use the "modulo" operator `%` to get the remainder and check for the evenness here. diff --git a/1-js/2-first-steps/13-while-for/4-for-even/task.md b/1-js/2-first-steps/13-while-for/4-for-even/task.md new file mode 100644 index 00000000..ff34e7e4 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/4-for-even/task.md @@ -0,0 +1,9 @@ +importance: 5 + +--- + +# Output even numbers in the loop + +Use the `for` loop to output even numbers from `2` to `10`. + +[demo] diff --git a/1-js/2-first-steps/13-while-for/5-replace-for-while/solution.md b/1-js/2-first-steps/13-while-for/5-replace-for-while/solution.md new file mode 100644 index 00000000..612cf559 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/5-replace-for-while/solution.md @@ -0,0 +1,10 @@ + + +```js run +let i = 0; +while (i < 3) { + alert( `number ${i}!` ); + i++; +} +``` + diff --git a/1-js/2-first-steps/13-while-for/5-replace-for-while/task.md b/1-js/2-first-steps/13-while-for/5-replace-for-while/task.md new file mode 100644 index 00000000..a62c9af3 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/5-replace-for-while/task.md @@ -0,0 +1,14 @@ +importance: 5 + +--- + +# Replace "for" with "while" + +Rewrite the code changing the `for` loop to `while` without altering it's behavior (the output should stay same). + +```js run +for (let i = 0; i < 3; i++) { + alert( `number ${i}!` ); +} +``` + diff --git a/1-js/2-first-steps/13-while-for/6-repeat-until-correct/solution.md b/1-js/2-first-steps/13-while-for/6-repeat-until-correct/solution.md new file mode 100644 index 00000000..dce31abc --- /dev/null +++ b/1-js/2-first-steps/13-while-for/6-repeat-until-correct/solution.md @@ -0,0 +1,15 @@ + +```js run demo +let num; + +do { + num = prompt("Enter a number greater than 100?", 0); +} while (num <= 100 && num); +``` + +The loop `do..while` repeats while both checks are truthy: + +1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`. +2. The check for a truthiness of `num` checks that `num != null` and `num != ""` simultaneously. + +P.S. By the way, if `num` is `null` then `num <= 100` would return `false`, not `true`! diff --git a/1-js/2-first-steps/13-while-for/6-repeat-until-correct/task.md b/1-js/2-first-steps/13-while-for/6-repeat-until-correct/task.md new file mode 100644 index 00000000..729835e9 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/6-repeat-until-correct/task.md @@ -0,0 +1,14 @@ +importance: 5 + +--- + +# Repeat until the input is incorrect + +Write a loop which prompts for a number greater than `100`. If the visitor enters another number -- ask him to repeat the input, and so on. + +The loop must ask for a number until either the visitor enters a number greater than `100` or cancels the input/enters an empty line. + +Here we can assume that the visitor only inputs numbers. There's no need to implement the special handling for a non-numeric input in this task. + +[demo] + diff --git a/1-js/2-first-steps/13-while-for/7-list-primes/solution.md b/1-js/2-first-steps/13-while-for/7-list-primes/solution.md new file mode 100644 index 00000000..5234fa44 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/7-list-primes/solution.md @@ -0,0 +1,28 @@ +There are many algorithms for this task. + +Let's use a nested loop: + +```js +For each i in the interval { + check if i has a divisor from 1..i + if yes => the value is not a prime + if no => the value is a prime, show it +} +``` + +The code using a label: + +```js run +nextPrime: +for (let i = 2; i < 10; i++) { // for each i... + + for (let j = 2; j < i; j++) { // look for a divisor.. + if (i % j == 0) continue nextPrime; // not a prime, go next i + } + + alert( i ); // a prime +} +``` + +Surely, there's a lot of space to opimize it. Like, we could look for the divisors from `2` to square root of `i`. But anyway, if we want to be really efficient for large intervals, we need change the approach and rely heavily on advanced maths and algorithms like [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) etc. + diff --git a/1-js/2-first-steps/13-while-for/7-list-primes/task.md b/1-js/2-first-steps/13-while-for/7-list-primes/task.md new file mode 100644 index 00000000..c7945e29 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/7-list-primes/task.md @@ -0,0 +1,18 @@ +importance: 3 + +--- + +# Output prime numbers + +An integer number greater than `1` is called a [prime](https://en.wikipedia.org/wiki/Prime_number) if it cannot be not divided without a remainder by anything except `1` and itself. + +In other words, `n>1` is a prime if the result of it's division by anything except `1` and `n` is not integer. + +For example, `5` is a prime, because it cannot be divided without a remainder by `2`, `3` and `4`. + +**Write the code which outputs prime numbers in the interval from `2` to `10`.** + +The result will be `2,3,5,7`. + +P.S. The code should be easily modifiable for other intervals. + diff --git a/1-js/2-first-steps/13-while-for/article.md b/1-js/2-first-steps/13-while-for/article.md new file mode 100644 index 00000000..77e44b17 --- /dev/null +++ b/1-js/2-first-steps/13-while-for/article.md @@ -0,0 +1,371 @@ +# Loops: while and for + +We often have a need to perform similar actions many times in a row. + +For example, when we need to output goods from the list one after another. Or just run the same code for each number from 1 to 10. + +*Loops* are a way to repeat the same part of code multiple times. + +[cut] + +## The "while" loop + +The `while` loop has the following syntax: + +```js +while (condition) { + // code ("loop body") +} +``` + +While the `condition` is `true` -- the `code` from the loop body is executed. + +For instance, the loop below outputs `i` while `i<3`: + +```js run +let i = 0; +while (i < 3) { // shows 0, then 1, then 2 + alert( i ); + i++; +} +``` + +There's a special term *iteration* for each loop run. The loop in the example above makes 3 iterations. + +If there were no `i++` in the example above, the loop would repeat (in theory) forever, eating 100% CPU. In practice, the browser would show a message about a "hanging" script and let the user stop it. + +The `while` converts `condition` to a logical value. It can be any expression, not just a comparison. + +For instance, the shorter way to write `while (i!=0)` could be `while (i)`: + +```js run +let i = 3; +*!* +while (i) { // when i becomes 0, the condition is falsy and the loop stops +*/!* + alert( i ); + i--; +} +``` + +````smart header="Brackes are not required for a single-line body" +If the loop body has a single statement, we can omit the brackets `{…}`: + +```js run +let i = 3; +*!* +while (i) alert(i--); +*/!* +``` +```` + +## The "do..while" loop + +The condition check can be moved *below* the loop body using the `do..while` syntax: + +```js +do { + // loop body +} while (condition); +``` + +The loop will first execute the body and then check the condition. + +For example: + +```js run +let i = 0; +do { + alert( i ); + i++; +} while (i < 3); +``` + +This form of syntax is rarely used, because the ordinary `while` is more obvious. We don't need to scroll down the code looking for the condition. + +## The "for" loop + +The `for` loop is actually the most often used one. + +It looks like this: + +```js +for (begin; condition; step) { + // ... loop body ... +} +``` + +Let's see these parts in an example. The loop below runs `alert(i)` for `i` from `0` up to (but not including) `3`: + +```js run +let i; + +for (i = 0; i < 3; i++) { // shows 0, then 1, then 2 + alert( i ); +} +``` + +Let's split the last example into parts: + +begin: `i=0` +: Executes once upon entering the loop. + +condition: `i<3` +: Checked before every loop iteration, if fails the loop stops. + +body: `alert(i)` +: Runs again and again while the condition is truthy + +step: `i++` +: Executes after the body on each iteration, but before the condition check. + +The execution flow is: +``` +Begin + → (if condition → run body and run step) + → (if condition → run body and run step) + → ... repeat until the condition is falsy. +``` + +````smart header="Inline variable declaration" +We can declare a "counter" variable right in the beginning of the loop. + +```js run no-beautify +for (*!*let*/!* i = 0; i < 3; i++) { + alert(i); // 0, 1, 2 +} +``` + +The variable will be visible only inside the loop. +```` + +## Skipping parts + +Any part of the `for` can be skipped. + +For example, we can omit `begin` if we don't need to do anything at the loop start. + +Like here: + +```js run +let i = 0; + +for (; i < 3; i++) { + alert( i ); // 0, 1, 2 +} +``` + +It would work same as `for(let i=0; ...)`. + +We can also remove the `step` part: + +```js run +let i = 0; + +for (; i < 3;) { + alert( i ); + // the loop became identical to while (i<3) +} +``` + +We can actually remove everything, thus creating an infinite loop: + +```js +for (;;) { + // repeats without limits +} +``` + +Please note that the semicolons `;` must present, otherwise it would be a syntax error. + +```smart header="`for..in` and `for..of`" +There are special constructs: `for..in` and `for..of` for more advanced iterations over objects. + +We'll get to them later, in chapters about objects. +``` + +## Breaking the loop + +Normally the loop exists when the condition becomes falsy. + +But we can force the exit at any moment. There's a special `break` directive for that. + +For example, this code below asks user for numbers and breaks if no number entered: + +```js +let sum = 0; + +while (true) { + + let value = +prompt("Enter a number", ''); + +*!* + if (!value) break; // (*) +*/!* + + sum += value; + +} +alert( 'Sum: ' + sum ); +``` + +The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after it's loop. Namely, `alert`. + +The composition: "infinite loop + break as needed" is a great thing for situations when the condition must be checked not in beginning/end of the loop, but in the middle. Or even in several places of the body. + +## Continue to the next iteration [#continue] + +The `continue` directive is a "lighter version" of `break`. It doesn't stop the whole loop. Instead if stops the current iteration and forces the loop to start a new one (if the condition allows). + +We can use it if we're done on the current iteration and would like to move on to the next. + +The loop above uses `continue` to output only odd values: + +```js run no-beautify +for (let i = 0; i < 10; i++) { + + // if true, skip the remaining part of the body + *!*if (i % 2 == 0) continue;*/!* + + alert(i); // 1, then 3, 5, 7, 9 +} +``` + +For even values of `i` the `continue` directive stops body execution, passing the control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values. + +````smart header="The directive `continue` helps to decrease nesting level" +A loop for odd-only values could look like this: + +```js +for (let i = 0; i < 10; i++) { + + if (i % 2) { + alert( i ); + } + +} +``` + +From the technical point of view it's identical. Surely, we can just wrap the code in the `if` block instead of `continue`. + +But as a side-effect we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that may decrease the overall readability. +```` + +````warn header="No `break/continue` to the right side of '?'" +Please note that syntax constructs that are not expressions cannot be used in `'?'`. In particular, directives `break/continue` are disallowed there. + +For example, if one we took this code: + +```js +if (i > 5) { + alert(i); +} else { + continue; +} +``` + +...And rewrote it using a question mark: + + +```js no-beautify +(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here +``` + +...Then it won't work. The code like this will give a syntax error: + + +That's just another reason not to use a question mark operator `'?'` instead of `if`. +```` + +## Labels for break/continue + +Sometimes we need to break out from multiple nested loops at once. + +For example, in the code below we loop over `i` and `j` asking for values on coordinates `(i, j)` from `(0,0)` to `(3,3)`: + +```js run no-beautify +for (let i = 0; i < 3; i++) { + + for (let j = 0; j < 3; j++) { + + let input = prompt(`Value at coords (${i},${j})`, ''); + + // what if I want to exit from here? + + } +} + +alert('Done!'); +``` + +Let's say we need a way to stop the process. Like if we user decides to cancel the input. + +The ordinary `break` after `input` would only break the inner loop. That's not sufficient. Labels come to the rescue. + +A *label* is an identifier with a colon before a loop: +```js +labelName: for(...) { + ... +} +``` + +We can put the `labelName` after a break statement, and it will break out of the labelled loop. + +Like here: + +```js run no-beautify +*!*outer:*/!* for (let i = 0; i < 3; i++) { + + for (let j = 0; j < 3; j++) { + + let input = prompt(`Value at coords (${i},${j})`, ''); + + // if an empty string or canceled, then break out of both loops + if (!input) *!*break outer*/!*; // (*) + + // do something with the value... + } +} +alert('Done!'); +``` + +In the code above `break outer` looks upwards for the label named `outer` and breaks out of that loop. + +So the control goes straight from `(*)` to `alert('Done!')`. + +We can also move a label into the separate string: + +```js no-beautify +outer: +for (let i = 0; i < 3; i++) { ... } +``` + +The `continue` directive can also be used with a label. In this case the execution would jump onto the next iteration of the labelled loop. + +````warn header="Labels are not a \"goto\"" +Labels do not allow to jump into an arbitrary place of code. + +For example, it is impossible to do like this: +```js +break label; // jumps to label? No. + +label: for(...) +``` + +The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive. +```` + +## Summary + +There are 3 types of loops in JavaScript: + +- `while` -- the condition is checked before each iteration. +- `do..while` -- the condition is checked after each iteration. +- `for` -- the condition is checked before each iteration, additional settings available. + +To make in "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive. + +If we don't want to do anything more on this iteration and would like to forward on to the next one -- the `continue` directive does it. + +`Break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop. + diff --git a/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/solution.md b/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/solution.md new file mode 100644 index 00000000..50d1174b --- /dev/null +++ b/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/solution.md @@ -0,0 +1,20 @@ +To be precise, the `if` must use a strict comparison `'==='`. + +In reality though, probably a simple `'=='` would do. + +```js no-beautify +if(browser == 'Edge') { + alert("You've got the Edge!"); +} else if (browser == 'Chrome' + || browser == 'Firefox' + || browser == 'Safari' + || browser == 'Opera') { + alert( 'Okay we support these browsers too' ); +} else { + alert( 'We hope that this page looks ok!' ); +} +``` + +Please note: the construct `browser == 'Chrome' || browser == 'Firefox' …` is split into multiple lines for better readability. + +But the `switch` is still neater and more descriptive. diff --git a/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/task.md b/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/task.md new file mode 100644 index 00000000..f4dc0e5f --- /dev/null +++ b/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/task.md @@ -0,0 +1,26 @@ +importance: 5 + +--- + +# Rewrite the "switch" into an "if" + +Write the code using `if..else` which would correspond to the following `switch`: + +```js +switch (browser) { + case 'Edge': + alert( "You've got the Edge!" ); + break; + + case 'Chrome': + case 'Firefox': + case 'Safari': + case 'Opera': + alert( 'Okay we support these browsers too' ); + break; + + default: + alert( 'We hope that this page looks ok!' ); +} +``` + diff --git a/1-js/2-first-steps/14-switch/2-rewrite-if-switch/solution.md b/1-js/2-first-steps/14-switch/2-rewrite-if-switch/solution.md new file mode 100644 index 00000000..f3a8b021 --- /dev/null +++ b/1-js/2-first-steps/14-switch/2-rewrite-if-switch/solution.md @@ -0,0 +1,27 @@ +The first two checks are a usual `case`. The third one is split into two cases: + +```js run +let a = +prompt('a?', ''); + +switch (a) { + case 0: + alert( 0 ); + break; + + case 1: + alert( 1 ); + break; + + case 2: + case 3: + alert( '2,3' ); +*!* + break; +*/!* +} +``` + +Please note: the `break` at the bottom is not required. But we put it to make the code future-proof. + +In the future, there is a chance that we'd want to add one more `case`, for example `case 4`. And if we forget to add a break before it, at the end of `case 3`, there will be an error. So that's a kind of self-insurance. + diff --git a/1-js/2-first-steps/14-switch/2-rewrite-if-switch/task.md b/1-js/2-first-steps/14-switch/2-rewrite-if-switch/task.md new file mode 100644 index 00000000..ec99d098 --- /dev/null +++ b/1-js/2-first-steps/14-switch/2-rewrite-if-switch/task.md @@ -0,0 +1,23 @@ +importance: 4 + +--- + +# Rewrite "if" into "switch" + +Rewrite the code below using a single `switch` statement: + +```js run +let a = +prompt('a?', ''); + +if (a == 0) { + alert( 0 ); +} +if (a == 1) { + alert( 1 ); +} + +if (a == 2 || a == 3) { + alert( '2,3' ); +} +``` + diff --git a/1-js/2-first-steps/14-switch/article.md b/1-js/2-first-steps/14-switch/article.md new file mode 100644 index 00000000..eb568816 --- /dev/null +++ b/1-js/2-first-steps/14-switch/article.md @@ -0,0 +1,169 @@ +# The "switch" statement + +A `switch` statement can replace multiple `if` checks. + +It gives a more descriptive way to compare a value with multiple variants. + +[cut] + +## The syntax + +It looks like this: + +```js no-beautify +switch(x) { + case 'value1': // if (x === 'value1') + ... + [break] + + case 'value2': // if (x === 'value2') + ... + [break] + + default: + ... + [break] +} +``` + +- The value of `x` is checked for a strict equality to the value from the first `case`, that is: `value1`, then to the second `value2` and so on. +- If the equality is found -- `switch` starts to execute the code starting from the corresponding `case`, and to the nearest `break` (or to the end of `switch`). +- If no case matched then the `default` code is executed (if exists). + +## An example + +An example of `switch` (the executed code is highlighted): + +```js run +let a = 2 + 2; + +switch (a) { + case 3: + alert( 'Too small' ); + break; +*!* + case 4: + alert( 'Exactly!' ); + break; +*/!* + case 5: + alert( 'Too large' ); + break; + default: + alert( "I don't know such values" ); +} +``` + +Here the `switch` starts to compare `a` from the first `case` variant that is `3`. The match fails. + +Then `4`. That's the match, so the execution starts from `case 4` and till the nearest `break`. + +**If there is no `break` then the execution continues with the next `case` without any checks.** + +An example without `break`: + +```js run +let a = 2 + 2; + +switch (a) { + case 3: + alert( 'Too small' ); +*!* + case 4: + alert( 'Exactly!' ); + case 5: + alert( 'Too big' ); + default: + alert( "I don't know such values" ); +*/!* +} +``` + +In the example above we'll see sequential execution of three `alert`s: + +```js +alert( 'Exactly!' ); +alert( 'Too big' ); +alert( "I don't know such values" ); +``` + +````smart header="Any expresion can be a `switch/case` argument" +Both `switch` and case allow arbitrary expresions. + +For example: + +```js run +let a = "1"; +let b = 0; + +switch (+a) { +*!* + case b + 1: + alert( 1 ); + break; +*/!* + + default: + alert('no-no, see the code above, it executes'); +} +``` +```` + +## Grouping of "case" + +Several variants of `case` can be grouped. + +For example, if we want the same code for `case 3` and `case 5`: + +```js run no-beautify +let a = 2 + 2; + +switch (a) { + case 4: + alert('Right!'); + break; + +*!* + case 3: // (*) + case 5: + alert('Wrong!'); + alert('How about to take maths classes?'); + break; +*/!* + + default: + alert('The result is strange. Really.'); +} +``` + +The grouping is just a side-effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`. + +## The type matters + +Let's emphase that the equality check is always strict. The values must be of the same type to match. + +For example, let's consider the code: + +```js run +let arg = prompt("Enter a value?") +switch (arg) { + case '0': + case '1': + alert( 'One or zero' ); + + case '2': + alert( 'Two' ); + break; + + case 3: + alert( 'Never executes!' ); + + default: + alert( 'An unknown value' ) +} +``` + +1. For `0`, `1`, the first `alert` runs. +2. For `2` the second `alert` runs. +3. But for `3`, the result of the `prompt` is a string `"3"`, which is not strictly equal `===` to the number `3`. So we've got a dead code in `case 3`! The `default` variant will execite. +