This commit is contained in:
Ilya Kantor 2017-03-24 17:28:37 +03:00
parent c9401b3104
commit 0fcf9f84fa
58 changed files with 673 additions and 643 deletions

View file

@ -1,10 +1,10 @@
# Debugging in Chrome
Before going further, let's talk about debugging.
Before writing more complex code, let's talk about debugging.
All modern browsers and most other environments support "debugging" -- a special UI in developer tools that makes finding and fixing errors much easier.
We'll be talking about Chrome here, because they are probably the most feature-rich.
We'll be using Chrome here, because it's probably the most feature-rich in this aspect.
[cut]
@ -28,66 +28,70 @@ Let's click it and select `index.html` and then `hello.js` in the tree view. Tha
Here we can see three zones:
1. **Resources zone** lists html, javascript, css and other files including images that are attached to the page. Currently active chrome extensions may appear here too.
1. **Resources zone** lists html, javascript, css and other files including images that are attached to the page. Chrome extensions may appear here too.
2. **Source zone** shows the source code.
3. **Information and control zone** is for debugging, we'll explore it later.
3. **Information and control zone** is for debugging, we'll explore it soon.
Now you could click the same toggler <span class="devtools" style="background-position:-200px -76px"></span> again to hide the resources list, as we won't need it soon.
Now you could click the same toggler <span class="devtools" style="background-position:-200px -76px"></span> again to hide the resources list and give the code some space.
## Console
If we press `Esc`, then a console opens below, to type commands into.
If we press `Esc`, then a console opens below, we can type commands there and press `key:Enter` to execute.
After a statement is executed, its result is shown.
After a statement is executed, its result is shown below.
For example, here `1+2` results in `3` and `hello("debugger")` call shows a message, but there's no result, so it's `undefined`:
For example, here `1+2` results in `3`, and `hello("debugger")` returns nothing, so the result is `undefined`:
![](chrome-sources-console.png)
## Breakpoints
Let's examine what's going on within the code. In `hello.js`, click at the line number `4`, right on the `4` digit.
Let's examine what's going on within the code. In `hello.js`, click at the line number `4`. Yes, right on the `"4"` digit, not on the code.
Contratulations! You've set a breakpoint. Please also click on the number for line `8`.
Should look like this:
Should look like this (blue is where you should click):
![](chrome-sources-breakpoint.png)
A *breakpoint* is a point of code where the debugger will automatically pause the JavaScript execution.
While the code is paused, we can examine current variables, execute commands in the console etc. That is -- debug it.
While the code is paused, we can examine current variables, execute commands in the console etc. In other words, to debug it.
Breakpoints also show in the right pane. We can always find a list of breakpoints there.
It allows to:
We can always find a list of breakpoints in the right pane. That's useful when we have many breakpoints in various files. It allows to:
- Quickly jump to the breakpoint in the code (by clicking on it in the right pane).
- Temporarily disable the breakpoint by unchecking it.
- Remove the breakpoint by right-clicking the text and selecting Remove.
- Remove the breakpoint by right-clicking and selecting Remove.
- ...And so on.
````smart header="Breakpoint alternatives"
- We can make debugger to pause the code by using the `debugger` command, like this:
```smart header="Conditional breakpoints"
*Right click* on the line number allows to create a *conditional* breakpoint. It only triggers when the given expression is truthy.
```js
function hello(name) {
let phrase = `Hello, ${name}!`;
That's handy when we need to stop only for a certain variable value or for a certain function parameters.
```
*!*
debugger; // <-- the debugger stops here
*/!*
## Debugger command
say(phrase);
}
```
- *Right click* on the line number allows to create a conditional breakpoint. It only triggers when a given expression is truthy.
We can also pause the code by using the `debugger` command, like this:
```js
function hello(name) {
let phrase = `Hello, ${name}!`;
*!*
debugger; // <-- the debugger stops here
*/!*
say(phrase);
}
```
That's very convenient when we are in a code editor and don't want to switch to the browser and look up the script in developer tools to set the breakpoint.
That's handy when we need to stop only for a certain variable value or for a certain function parameters.
````
## Pause and look around
In our example, `hello()` is called during page load, so the easiest way to activate debugger -- is to reload the page. So let's press `key:F5` (Windows, Linux) or `key:Cmd+R` (Mac).
In our example, `hello()` is called during the page load, so the easiest way to activate debugger -- is to reload the page. So let's press `key:F5` (Windows, Linux) or `key:Cmd+R` (Mac).
As the breakpoint is set, the execution pauses at the 4th line:
@ -97,13 +101,13 @@ Please open the informational dropdowns to the right (labelled with arrows). The
1. **`Watch` -- shows current values for any expressions.**
You can click the plus `+` and input an expression. The debugger will shown its value at any moment, automatically recalculating it in in the process.
You can click the plus `+` and input an expression. The debugger will shown its value at any moment, automatically recalculating it in in the process of execution.
2. **`Call Stack` -- shows the nested calls chain.**
At the current moment the debugger is inside `hello()` call, called by a script in `index.html` (no function there).
At the current moment the debugger is inside `hello()` call, called by a script in `index.html` (no function there, so it's called "anonymous").
If you click the item there, the debugger jumps to the corresponding code, and all its variables can be examined as well.
If you click on a stack item, the debugger jumps to the corresponding code, and all its variables can be examined as well.
3. **`Scope` -- current variables.**
`Local` shows local function variables. You can also see their values highlighted right over the source.
@ -116,41 +120,43 @@ Please open the informational dropdowns to the right (labelled with arrows). The
Now let's time to *trace* the script.
There are buttons for it at the right-top:
There are buttons for it at the top of the right pane. Let's engage them.
<span class="devtools" style="background-position:-7px -76px"></span> -- continue the execution, hotkey `key:F8`.
: Resumes the execution. If there are no additional breakpoints, then the execution just continues and the debugger looses the control.
Let's click it:
Here's what we can see after a click on it:
![](chrome-sources-debugger-trace-1.png)
The execution has resumed, reached another breakpoint inside `say()` and paused there. Take a look at the "Call stack" at the right. It has increased by one more call. We're inside `say()` now.
<span class="devtools" style="background-position:-137px -76px"></span> -- make a step (run the next command), but *not go into the function*, hotkey `key:F10`.
: If we click it now, `alert` will be shown. The important thing is that if `alert` were not native, but a JavaScript function, then the execution would "step over it", skipping the function internals.
: If we click it now, `alert` will be shown. The important thing is that `alert` can be any function, the execution "steps over it", skipping the function internals.
<span class="devtools" style="background-position:-72px -76px"></span> -- make a step, hotkey `key:F11`.
: The same as the previous one, but "steps in" nested functions. Clicking this will step through all script actions one by one.
: The same as the previous one, but "steps into" nested functions. Clicking this will step through all script actions one by one.
<span class="devtools" style="background-position:-104px -76px"></span> -- continue the execution till the end of the current function, hotkey `key:Shift+F11`.
: The execution would stop at the very last line of the current function. That's handy when we accidentally entered a nested call using <span class="devtools" style="background-position:-137px -76px"></span>, but it does not interest us and we want to continue to its end as soon as possible.
: The execution would stop at the very last line of the current function. That's handy when we accidentally entered a nested call using <span class="devtools" style="background-position:-72px -76px"></span>, but it does not interest us, and we want to continue to its end as soon as possible.
<span class="devtools" style="background-position:-7px -28px"></span> -- enable/disable all breakpoints.
: That button does not move the execution. Just a mass on/off for breakpoints.
<span class="devtools" style="background-position:-264px -4px"></span> -- enable/disable automatic pause in case of an error.
: When it's enabled (by default it is), an error pauses the execution and we can analyze variables to see what went wrong. So if our script dies with error, we can open debugger and reload the page to see where it stopped and what was the context at that moment.
: When enabled, and the developer tools is open, a script error automatically pauses the execution. Then we can analyze variables to see what went wrong. So if our script dies with an error, we can open debugger, enable this option and reload the page to see where it dies and what's the context at that moment.
```smart header="Continue to here"
Right click on the line number opens the contextual menu where we can "Continue to here". That's handy when we want to move multiple steps forward, but too lazy to set a breakpoint.
Right click on a line of code opens the context menu with a great option called "Continue to here".
That's handy when we want to move multiple steps forward, but too lazy to set a breakpoint.
```
## Logging
To output something to console, there's `console.log` function.
For instance, this logs values from `0` to `4`:
For instance, this outputs values from `0` to `4` to console:
```js run
// open console to see
@ -159,9 +165,9 @@ for (let i = 0; i < 5; i++) {
}
```
To see the output, you can either switch to "Console" tab in developer tools or press `key:Esc` while in another tab: that opens the console at the bottom.
Regular users don't see that output, it is in the console. To see it -- either open the Console tab of developer tools or press `key:Esc` while in another tab: that opens the console at the bottom.
That's called "logging". If we have enough logging in our code, then we can see what's going on from the records, without the debugger.
If we have enough logging in our code, then we can see what's going on from the records, without the debugger.
## Summary
@ -170,12 +176,10 @@ As we can see, there are 3 main ways to pause a script:
2. The `debugger` statements.
3. An error (if dev tools are open and the button <span class="devtools" style="background-position:-264px -4px"></span> is "on")
Then the debugging process usually consists of examining variables and stepping on to see where the execution goes the wrong way.
Then we can examine variables and step on to see where the execution goes the wrong way.
There's much more power in developer tools.
There's much more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>
The better manual is at <https://developers.google.com/web/tools/chrome-devtools>
The information from this chapter is enough to begin debugging, but later, especially if you are doing in-browser stuff, please go there and look through more advanced capabilities of developer tools.
The information from this chapter is enough to begin debugging, but later, especially if you do a lot of browser stuff, please go there and look through more advanced capabilities of developer tools.
Oh, and also you can click at various places of dev tools and just see what's showing up. That's probably the fastest route to learn dev tools. Don't forget about the right click as well!

View file

@ -38,13 +38,15 @@ if (n < 0) {
-->
Nothing is "carved in stone" here, so let's discuss the rules in detail.
Now let's discuss the rules and reasons for them in detail.
Nothing is "carved in stone" here. Everything is optional and can be changed: these are coding rules, not religious dogmas.
### Figure brackets
In most JavaScript projects figure brackets are written on the same line. A so-called "egyptian" style. There's also a space before an opening bracket.
In most JavaScript projects figure brackets are written on the same line, not on the new line. A so-called "egyptian" style. There's also a space before an opening bracket.
A corner-case if a single-line `if/for`. Should we use brackets at all? If yes, then where?
An edge case is a single-line `if/for`. Should we use brackets at all? If yes, then where?
Here are the annotated variants, so you can judge about their readability on your own:
@ -64,9 +66,9 @@ if (n < 0) {
-->
![](figure-bracket-style.png)
As a summary, for a really short code one line is acceptable: like `if (cond) return null`.
But a separate line for each statement in brackets is usually better.
As a summary:
- For a really short code one line is acceptable: like `if (cond) return null`.
- But a separate line for each statement in brackets is usually better.
### Line length
@ -80,7 +82,7 @@ There are two types of indents:
- **A horizontal indent: 2(4) spaces.**
A horizantal identation is made using either 2 or 4 spaces or the "Tab" symbol. Which one to choose is a kind of an old holy war. Spaces are a little more common nowadays.
A horizantal identation is made using either 2 or 4 spaces or the "Tab" symbol. Which one to choose is a kind of an old holy war. Spaces are more common nowadays.
One of advantages of spaces over tabs is that they allow more flexible configurations of indents than the "Tab" symbol.
@ -97,7 +99,7 @@ There are two types of indents:
}
```
- **A vertical indent, line breaks for splitting the code in logical blocks.**
- **A vertical indent: empty lines for splitting the code in logical blocks.**
Even a single function can often be divided in logical blocks. In the example below, the initialization of variables, the main loop and returning the result are split vertically:
@ -113,7 +115,7 @@ There are two types of indents:
}
```
Insert an additional line break where it helps to make the code more readable. There should not be more than 9 lines of code without a vertical indentation.
Insert an extra newline where it helps to make the code more readable. There should not be more than 9 lines of code without a vertical indentation.
### A semicolon
@ -121,7 +123,7 @@ A semicolons should be after each statement. Even if could possibly be skipped.
There are languages where a semicolon is truly optional. It's rarely used there.
But in JavaScript a line break is sometimes interpreted as a semicolon and sometimes not. That leaves a place for programming errors, so semicolons should be at place.
But in JavaScript there are few cases when a line break is sometimes not interpreted as a semicolon. That leaves a place for programming errors, so semicolons should be at place.
### Nesting levels
@ -189,7 +191,7 @@ function pow(x, n) {
}
```
...But the second one is more readable, because the "edge case" is handled early on, and then we have the "main" code flow, without an additional nesting.
...But the second one is more readable, because the "edge case" of `n < 0` is handled early on, and then we have the "main" code flow, without an additional nesting.
## Functions below the code
@ -238,7 +240,7 @@ If you are writing several "helper" functions and the code to use them, then the
...
}
```
3. Mixed, a function is described when it's first used.
3. Mixed, a function is described where it's first used.
Most of time, the second variant is preferred.
@ -246,11 +248,11 @@ That's because when reading a code, we first want to know "what it does". If the
## Style guides
There are many peculiar details in the code style.
There are many details in the code style.
As the team becomes bigger, a common agreement on them becomes the "team style guide".
There are many open style guides, so there we could just accept the one we like the most.
There are many open style guides, for instance:
- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
@ -258,17 +260,17 @@ There are many open style guides, so there we could just accept the one we like
There exist more there in the wild.
As you become more mature in JavaScript programming, you might want to read them all to pick up the common principles.
You can browse them and choose something as a base. As you become more mature in JavaScript programming, you might want to read them all to pick up the common principles.
## Style checkers
There are great tools that can check the code style automatically. They are called "linters".
There are tools that can check the code style automatically. They are called "linters".
Please note, they not only check the style, but sometimes help to find bugs, like a typo in variable name or a function.
The great thing about them is that style-checking also finds some bugs, like a typo in variable name or a function.
So it's really beneficial to install one. Even if you don't want to stick to a "code style". They help to find typos -- and that's already good enough.
So it's recommended to install one, even if you don't want to stick to a "code style". They help to find typos -- and that's already good enough.
Most well known are:
Most well-known tools are:
- [JSLint](http://www.jslint.com/) -- one of the oldest open-source solutions.
- [JSHint](http://www.jshint.com/) -- the more "featured" variant of JSLint.
@ -278,7 +280,7 @@ All of them can do the job. The author uses [ESLint](http://eslint.org/).
Here are simple steps to start using it:
1. Install [Node.JS](https://nodejs.org/), necessary to run them.
1. Install [Node.JS](https://nodejs.org/).
2. Install eslint: `npm i -g eslint` (npm is Node.JS package installer).
3. Create a config file `.eslintrc` in your JavaScript project (the dot at the start is mandatory).
@ -309,4 +311,4 @@ All syntax rules from this chapter and the style guides aim to increase readabil
All of them are debatable.
When we think about "how to write better?", the sole criterion is "what makes the code more readable and easier to understand? what helps to evade errors?" The answer helps to pick up best practices. And maybe to abandon some in case if they don't contribute.
When we think about "how to write better?", the sole criterion is "what makes the code more readable and easier to understand? what helps to evade errors?"

View file

@ -2,11 +2,11 @@
Comments are generally a good thing. But novices in programming generally get that wrong. They write comments explaining "what is going on in the code".
**But the amount of such "explanatory" comments should be minimal.**
But the amount of such "explanatory" comments should be minimal.
Seriously, a good code should be easy to understand without them.
There's a great rule about that: "if the code is not clear without a comment, then may be it should be rewritten instead".
There's a great rule about that: "if the code is so unclear that it requires a comment, then maybe it should be rewritten instead".
[cut]
@ -19,10 +19,10 @@ function showPrimes(n) {
*!*
// check if i is a prime number
*/!*
for (let j = 2; j < i; j++) {
if (i % j == 0) continue nextPrime;
}
*/!*
alert(i);
}
@ -96,14 +96,14 @@ function addJuice(container) {
That's readable without comments. And also the code structure is better when split. It's clear what every function does, what it takes and what it returns.
In reality, we can't totally evade "explanatory" comments. There are complex algorithms. And there are smart code tweaks made for optimization. But generally we should try to keep the code as simple as possible, and apply those only when needed.
In reality, we can't totally evade "explanatory" comments. There are complex algorithms. And there are smart "tweaks" for purposes of optimization. But generally we should try to keep the code simple and self-descriptive.
## Good comments
Which comments are good?
Describe the architecture
: That's the list of components, how they interact, what's the control flow in various situations... In short -- the bird's eye view of the code. There's a special diagram language [UML](http://wikipedia.org/wiki/Unified_Modeling_Language) for high-level architecture diagrams. Definitely worth studying.
: Provide a high-level overview of components, how they interact, what's the control flow in various situations... In short -- the bird's eye view of the code. There's a special diagram language [UML](http://wikipedia.org/wiki/Unified_Modeling_Language) for high-level architecture diagrams. Definitely worth studying.
Document a function usage
: There's a special syntax [JSDoc](http://en.wikipedia.org/wiki/JSDoc) to document a function: usage, parameters, returned value.
@ -122,7 +122,7 @@ Document a function usage
}
```
Such comments allow to understand the purpose of the function and use it the right way. Even without looking in its code.
Such comments allow to understand the purpose of the function and use it the right way without looking in its code.
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
@ -134,16 +134,14 @@ Why the task is solved this way?
If there are many ways to solve the task, why this one? Especially when it's not the most obvious one.
Without such comments the following situation is possible:
1. You (or your colleague) open the code written some time ago, and see it's "suboptimal".
1. You (or your colleague) open the code written some time ago, and see that it's "suboptimal".
2. You think: "How stupid I was then, and how much smarter I'm now", and rewrite using the "more obvious and correct" variant.
3. ...The urge to rewrite was good. But in the process you see that the "more obvious" solution is actually lacking. Hopefully, you revert to the correct variant, but the time was spent.
3. ...The urge to rewrite was good. But in the process you see that the "more obvious" solution is actually lacking. You even dimly remember why, because you already tried it long ago. You revert to the correct variant, but the time was wasted.
Comments that explain the solution are very important. They help to understand what happens and continue development the right way.
Comments that explain the solution are very important. They help to continue development the right way.
Any subtle features of the code? Where they are used?
: If the code has anything subtle, it's definitely worth commenting.
One of signs of a good developer is his comments. Good comments allow to maintain the code well, return to it after a long delay and use features more effectively.
: If the code has anything subtle and counter-obvious, it's definitely worth commenting.
## Style guides
@ -212,6 +210,8 @@ For that reason even if you're not concerned about styles, using a linter is rea
## Summary
Code style is important, especially in the team. When other people look at your code, the impression is largely defined by the style. Good code is easier to read and understand.
One of signs of a good developer is his comments. Good comments allow to maintain the code well, return to it after a long delay and use features more effectively.
Code style is important too, especially in the team. When other people look at your code, the impression is largely defined by the style. Good code is easier to read and understand.
Speaking about style rules: quotes, spaces etc, we should keep in mind that all of them are good *only* if they make the code better. More readable, easier to maintain. That's the main thing to keep in mind when choosing the style or discussing which one is better.

View file

@ -1,27 +1,31 @@
# How to write bad code?
Programmer ninjas of the past used these tricks to make code maintainers cry. Code review gurus search for signs of them in test tasks. Novice developers sometimes use them even better than programmer ninjas (indeed there's power in being a newbie!).
Programmer ninjas of the past used these tricks to make code maintainers cry. Code review gurus look for them in test tasks. Novice developers sometimes use them even better than programmer ninjas.
Read them carefully and find who you are -- a ninja, a novice, or maybe a code reviewer?
Read them carefully and find out who you are -- a ninja, a novice, or maybe a code reviewer?
[cut]
```warn header="Irony detected"
These are rules of writing bad code. Just... You know, some people miss the point.
```
## Brevity is the soul of wit
Make the code as short as possible. Show how smart you are.
Let subtle language features guide you.
For instance, take a look at the ternary operator `'?'`:
For instance, take a look at this ternary operator `'?'`:
```js
// taken from a well-known javascript library
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
```
The developer who comes across such line and tries to understand the value of `i` will probably come to you seeking for an answer.
Cool, right? If you write like that, the developer who comes across this line and tries to understand what is the value of `i` will probably have a merry time. Then come to you, seeking for an answer.
Tell him that the shorter is the better. Initiate him into the paths of ninja. Don't forget to give him [Tao Te Ching](http://www.w66.eu/elib/html/ttk.html).
Tell him that shorter is always better. Initiate him into the paths of ninja.
## One-letter variables
@ -32,15 +36,15 @@ completed.
Another way to code faster (and much worse!) is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
That makes the variable blend in the code like a real ninja in the forest. No one will be able to find it using the "search" of the editor. And even if someone does, he won't be able to "decipher" what it means.
A short variable disappears in the code like a real ninja in the forest. No one will be able to find it using the "search" of the editor. And even if someone does, he won't be able to "decipher" what the name `a` or `b` means.
...But there's an exception. A real ninja will never use `i` as the counter in a `"for"` loop. Anywhere, but not there. Look, there's so much more exotic letters. For instance, `x` or `y`.
...But there's an exception. A real ninja will never use `i` as the counter in a `"for"` loop. Anywhere, but not here. Look around, there are so much more exotic letters. For instance, `x` or `y`.
The approach is even more effective when the loop body takes 1-2 pages (make it longer? yes, if you can). Then it becomes impossible to guess that the variable is the loop counter, when looking in the middle of the code.
An exotic variable as a loop counter is especially cool if the loop body takes 1-2 pages (make it longer if you can). Then if someone looks deep inside the loop, he won't be able to figure out fast that the variable is the loop counter.
## Can't one-letter? Then shorten
## Use abbreviations
If the team rules forbid to use one-letter and vague names -- shorten them.
If the team rules forbid to use one-letter and vague names -- shorten them, make abbreviations.
Like this:
@ -49,7 +53,7 @@ Like this:
- `browser` -> `brsr`.
- ...etc
Only the one with a truly good intuition will be able to understand all such names. Try to shorten everything. Only a worthy person will be able to uphold development of such code.
Only the one with a truly good intuition will be able to understand all such names. Try to shorten everything. Only a worthy person will be able to uphold the development of such code.
## Soar high. Be abstract.
@ -62,31 +66,29 @@ The great image has no form.
While choosing a name try to use the most abstract word. Like `obj`, `data`, `value`, `item`, `elem` and so on.
- **The ideal variable for a variable is `data`.** Use it everywhere where you can. Indeed, every variable holds *data*, right?
- **The ideal name for a variable is `data`.** Use it everywhere where you can. Indeed, every variable holds *data*, right?
...But what to do if `data` is already taken? Try `value`, it's also universal. A variable always has a *value*, correct?
Taken as well? An experienced ninja should find a way.
- **Name the variable by its type: `str`, `num`, `list`...**
- **Name the variable by its type: `str`, `num`...**
...But will that make the code worse? Actually, yes!
From the one hand, the variable name still means something. It says what's inside the variable: a string, a number or an list. But when a person unfamiliar to the Dao will try to understand the code -- he'll be surprised to see that there's actually no information at all!
From one hand, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code -- he'll be surprised to see that there's actually no information at all!
Actually, the value type is easy to see by debugging. But what's the meaning of the variable? Which string/number it stores? There's just no way to figure out without a good meditation!
- **...But what if there's no more such names?** Just add a letter: `item1, item2, elem5, data1`...
- **...But what if there are no more such names?** Just add a letter: `item1, item2, elem5, data1`...
## Attention test
Only a truly attentive programmer should be able to understand the code. But how to check that?
**One of the ways -- is to use similar variable names, like `date` and `data`.**
**One of the ways -- use similar variable names, like `date` and `data`.**
Mix them where you can.
A quick read of such code becomes impossible. And when there's a typo... Ummm... We're stuck for long, time to drink some tea.
A quick read of such code becomes impossible. And when there's a typo... Ummm... We're stuck for long, time to drink tea.
## Smart synonyms
@ -95,21 +97,19 @@ A quick read of such code becomes impossible. And when there's a typo... Ummm...
The hardest thing of all is to find a black cat in a dark room, especially if there is no cat.
```
Don't be bored. Use *similar* names for *same* things.
Use *similar* names for *same* things, that makes life more interesting and shows your creativity to the public.
For instance, the function prefixes. If a function shows something on the screen -- start it with `display..` (like `displayMessage`), and the similar function that shows a question should start with `show...` (like `showName`).
For instance, the function prefixes. If a function shows a message on the screen -- start it with `display…`, like `displayMessage`. And then if another function shows something else, like a user name, start it with `show…` (like `showName`).
**Insinuate that there's a subtle difference difference between such functions, while there is none.**
Insinuate that there's a subtle difference difference between such functions, while there is none.
Make a pact with fellow ninjas of the team: if John starts "showing" functions with `display...` in his code, then Peter could use `render..`, and Ann -- `paint...`.
Make a pact with fellow ninjas of the team: if John starts "showing" functions with `display...` in his code, then Peter could use `render..`, and Ann -- `paint...`. Note how more interesting and diverse the code became.
...And now the hat trick!
**For two functions with important differences -- use the same word!**
For two functions with important differences -- use the same prefix!
For instance, the function `printPage(page)` will use a printer. And the function `printText(text)` will put the text on-screen.
Let an unfamiliar reader think well over things: "Where does `printMessage(message)` put the message?". To make it really shine, `printMessage(message)` should output it in the new window!
For instance, the function `printPage(page)` will use a printer. And the function `printText(text)` will put the text on-screen. Let an unfamiliar reader think well over things: "Where does `printMessage(message)` put the message? To a printer or on the screen?". To make it really shine, `printMessage(message)` should output it in the new window!
## Reuse names
@ -126,9 +126,11 @@ Instead, reuse existing names. Just write new values into them.
In a function try to use only variables passed as parameters.
That would make it impossible to identify what's exactly in the variable *now*. And also where it comes from. A person without a skill would have to analyze the code line-by-line and track the changes through every code branch.
That would make it impossible to identify what's exactly in the variable *now*. And also where it comes from. A person with weak intuition would have to analyze the code line-by-line and track the changes through every code branch.
**An advanced variant of the approach is to covertly (!) replace the value with something alike, for instance:**
**An advanced variant of the approach is to covertly (!) replace the value with something alike in the middle of a loop or a function.**
For instance:
```js
function ninjaFunction(elem) {
@ -136,13 +138,13 @@ function ninjaFunction(elem) {
elem = clone(elem);
// 20 more lines, now working with the new elem!
// 20 more lines, now working with the clone of the elem!
}
```
A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code he will find out that he worked with he's working with the clone!
A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code he will find out that he's working with a clone!
Deadly effective even against an experienced ninja. Met in the code regularly.
Deadly effective even against an experienced ninja. Seen in code regularly.
## Underscores for fun
@ -154,9 +156,9 @@ A smart ninja puts underscores at one spot of code and evades them at other plac
## Show your love
Let everyone see how magnificent your entities are! Names like `superElement`, `megaFrame` and `niceItem` will definitely enlighten the reader.
Let everyone see how magnificent your entities are! Names like `superElement`, `megaFrame` and `niceItem` will definitely enlighten a reader.
Indeed, from one hand, something is written: `super..`, `mega..`, `nice..` But from the other hand -- that brings no details. The reader may decide to look for a hidden meaning and meditate for an hour or two.
Indeed, from one hand, something is written: `super..`, `mega..`, `nice..` But from the other hand -- that brings no details. A reader may decide to look for a hidden meaning and meditate for an hour or two.
## Overlap outer variables
@ -180,33 +182,15 @@ function render() {
}
```
A programmer who jumps inside the `render` will probably miss to notice that the local `user` overlaps the outer one. Then he'll try to work with it assuming that it's the resulf of `authenticateUser()`... The trap is sprung! Hello, debugger...
A programmer who jumps inside the `render` will probably miss to notice that there's a local `user` shadowing the outer one.
## Powerful functions!
```quote author="Laozi (Tao Te Ching)"
The great Tao flows everywhere,<br>
both to the left and to the right.
```
Don't limit the function by what's written in its name. Be wider.
For instance, a function `validateEmail(email)` could, besides checking the email for correctness, to show an error message and ask to re-enter the email.
**Add at least two more actions to the main purpose of the function.**
They should not be obvious from the function name. A true ninja coder will make them not obvious from the code as well.
**Joining several actions into one protects your code from reuse.**
Imagine, another developer wants only to check the email, and not output any message. Your function `validateEmail(email)` that does both will not suit him. So he will not break your meditation by asking anything about it.
Then he'll try to work with `user` it assuming that it's the external variable, the result of `authenticateUser()`... The trap is sprung! Hello, debugger...
## Side-effects everywhere!
There are functions that look like they don't change anything. Like `isReady()`, `checkPermission()`, `findTags()`... They are assumed to carry out calculations, find and return the data, without changing anything outside of them. That's called "no side-effects".
**A really beautiful trick -- is to add a "useful" action to them, besides the main task.**
The expression of dazed surprise on the face of your colleague when he see a function named `is..`, `check..` or `find...` changing something -- will definitely broaden your boundaries of reason.
@ -217,6 +201,24 @@ Show your original thinking! Let the call of `checkPermission` return not `true/
Those developers who try to write `if (checkPermission(..))`, will wonder why it doesn't work. Tell them: "Read the docs!". And give this article.
## Powerful functions!
```quote author="Laozi (Tao Te Ching)"
The great Tao flows everywhere,<br>
both to the left and to the right.
```
Don't limit the function by what's written in its name. Be broader.
For instance, a function `validateEmail(email)` could (besides checking the email for correctness) show an error message and ask to re-enter the email.
Additional actions should not be obvious from the function name. A true ninja coder will make them not obvious from the code as well.
**Joining several actions into one protects your code from reuse.**
Imagine, another developer wants only to check the email, and not output any message. Your function `validateEmail(email)` that does both will not suit him. So he won't break your meditation by asking anything about it.
## Summary
All "pieces of advice" above are from the real code... Sometimes, written by experienced developers. Maybe even more experienced than you are ;)

View file

@ -21,4 +21,4 @@ it("Raises x to the power n", function() {
});
```
P.S. Syntactically it's correct and passes.
P.S. Syntactically the test is correct and passes.

View file

@ -1,32 +1,32 @@
# Automated testing with mocha
Automated testing will be used further on in tasks. It's actually a part of the "educational minimum" of a developer.
Automated testing will be used further in tasks.
It's actually a part of the "educational minimum" of a developer.
[cut]
## Why we need tests?
When we write a function, we usually can imagine what it should do, which parameters yield which results.
When we write a function, we usually can imagine what it should do: which parameters give which results.
During the development, we can check the function by running it and comparing the outcome with the expected one. For instance, we can do it in the console.
If something's wrong -- then fix the code, run again, check the result -- and so on till it works.
If something's wrong -- then we fix the code, run again, check the result -- and so on till it works.
But such manual "re-runs" are rather imperfect.
But such manual "re-runs" are imperfect.
**When testing a code manually -- it's easy to miss something.**
**When testing a code by manual re-runs -- it's easy to miss something.**
For instance, we're composing a function `f`. Wrote some code, testing: `f(1)` works, but `f(2)` doesn't work. We fix the code and now `f(2)` works. Looks complete? But we forgot to re-test `f(1)`. That may lead to an error.
For instance, we're creating a function `f`. Wrote some code, testing: `f(1)` works, but `f(2)` doesn't work. We fix the code and now `f(2)` works. Looks complete? But we forgot to re-test `f(1)`. That may lead to an error.
That's actually fair. When we develop something, we keep a lot of possible use cases and mind. But it's hard to expect from programmer to check all of them manually after every change. So it becomes easy to fix one thing and break another one.
That's very typical. When we develop something, we keep a lot of possible use cases and mind. But it's hard to expect from programmer to check all of them manually after every change. So it becomes easy to fix one thing and break another one.
**Automated testing means that tests are written separately, in addition to the code. They can be executed easily and check all the main use cases.**
## Behavior Driven Development (BDD)
Let's get down to a technique named [Behavior Driven Development](http://en.wikipedia.org/wiki/Behavior-driven_development) or, in short, BDD. That approach is used among many projects.
BDD is not just about testing. That's more.
Let's use a technique named [Behavior Driven Development](http://en.wikipedia.org/wiki/Behavior-driven_development) or, in short, BDD. That approach is used among many projects. BDD is not just about testing. That's more.
**BDD is three things in one: tests AND documentation AND examples.**
@ -36,9 +36,9 @@ Enough words. Let's see the example.
Let's say we want to make a function `pow(x, n)` that raises `x` to an integer power `n`. We assume that `n≥0`.
That task is quite simple, there's even a `**` operator in JavaScript that can do that, but here we concentrate not on the function itself, but on the development flow, that can be applied to more complex tasks as well.
That task is just an example: there's `**` operator in JavaScript that can do that, but here we concentrate on the development flow that can be applied to more complex tasks as well.
Before creating the code of `pow`, we can imagine what the function should do and describe it using BDD.
Before creating the code of `pow`, we can imagine what the function should do and describe it.
Such description is called a *specification* or, in short, a spec, and looks like this:
@ -55,10 +55,10 @@ describe("pow", function() {
A spec has three main building blocks that you can see above:
`describe("title", function() { ... })`
: What functionality we're describing. Uses to group "working horses" -- the `it` blocks. In our case we're describing the function `pow`.
: What functionality we're describing. Uses to group "workers" -- the `it` blocks. In our case we're describing the function `pow`.
`it("title", function() { ... })`
: In the title of `it` we *in a human-readable way* describe the particular use case, and then goes a function that tests it.
: In the title of `it` we *in a human-readable way* describe the particular use case, and the second argument is a function that tests it.
`assert.equal(value1, value2)`
: The code inside `it` block, if the implementation is correct, should execute without errors.
@ -73,25 +73,28 @@ The flow of development usually looks like this:
1. An initial spec is written, with tests for the most basic functionality.
2. An initial implementation is created.
3. To check whether it works, we run the testing framework [Mocha](http://mochajs.org/) together with assertion framework (we'll pick [Chai](http://chaijs.com/)), the spec and the implementation. Functions `describe` and `it` actually belong to Mocha and `assert.*` come from Chai. If there are errors, we make corrections till everything works.
3. To check whether it works, we run the testing framework [Mocha](http://mochajs.org/) (more details soon) that runs the spec. Errors are displayed. We make corrections till everything works.
4. Now we have a working initial implementation with tests.
5. We add more use cases to the spec, probably not yet supported by the implementations. Tests start to fail.
6. Go to 3, alter the implementation till everything works, and repeat the same till the functionality is ready.
6. Go to 3, update the implementation till tests give no errors.
7. Repeat steps 3-6 till the functionality is ready.
So, the development is *iterative*. We write the spec, implement it, then write more tests, make sure they work etc. At the end we have both a working implementation and tests for it.
So, the development is *iterative*. We write the spec, implement it, make sure tests pass, then write more tests, make sure they work etc. At the end we have both a working implementation and tests for it.
In our case, the first step is complete: we have an initial spec. So let's make an implementation. But before that let's make a "zero" run of the spec, just to see that tests are working (they will all fail).
In our case, the first step is complete: we have an initial spec for `pow`. So let's make an implementation. But before that let's make a "zero" run of the spec, just to see that tests are working (they will all fail).
## The spec in action
Here in the tutorial we'll be using the following JavaScript libraries for tests:
- [Mocha](http://mochajs.org/) -- the main framework with common testing functions including `describe` and `it`.
- [Mocha](http://mochajs.org/) -- the core framework: it provides common testing functions including `describe` and `it` and the main function that runs tests.
- [Chai](http://chaijs.com) -- the library with many assertions. It allows to use a lot of different assertions, for now we need only `assert.equal`.
- [Sinon](http://sinonjs.org/) -- to emulate built-in functions, to spy over functions and more, we'll need it much later.
- [Sinon](http://sinonjs.org/) -- a library to spy over functions, emulate built-in functions and more, we'll need it much later.
These libraries are suitable for both in-browser and server-side testing. Here we'll consider the browser variant.
The full HTML page with these frameworks and `pow` spec:
```html src="index.html"
```
@ -107,9 +110,9 @@ The result:
[iframe height=250 src="pow-1" border=1 edit]
As of now, the tests fail. That's logical: we have an empty function code in `pow`.
As of now, the test fails, there's an error. That's logical: we have an empty function code in `pow`, so `pow(2,3)` returns `undefined` instead of `8`.
For the future, let's note that for browser there exist advanced test-runners, like [karma](https://karma-runner.github.io/) and others. So it's generally not a problem to setup environment for many tests. But right now, for a single function, our test page is more than enough.
For future, let's note that for there are advanced test-runners, like [karma](https://karma-runner.github.io/) and others. So it's generally not a problem to setup many different tests.
## Initial implementation
@ -129,7 +132,7 @@ Wow, now it works!
What we've done -- is definitely a cheat. The function does not work: an attempt to calculate `pow(3,4)` would give an incorrect result, but tests pass.
Here we can see a typical situation: tests pass, but the function works wrong. That happens. Our spec is imperfect. We need to add more use cases to it.
...But the situation is quite typical, it happens in practice. Tests pass, but the function works wrong. Our spec is imperfect. We need to add more use cases to it.
Let's add one more test to see if `pow(3, 4) = 81`.
@ -167,7 +170,7 @@ We can select one of two ways to organize the test here:
The principal difference is that when `assert` triggers an error, the `it` block immediately terminates. So, in the first variant if the first `assert` fails, then we'll never see the result of the second `assert`.
**Making tests separate is useful to get more information about what's going on.**
Making tests separate is useful to get more information about what's going on, so the second variant is better.
And besides that, there's one more rule good to follow.
@ -175,13 +178,13 @@ And besides that, there's one more rule good to follow.
If we look at the test and see two independent checks in it -- better to split it into two simpler ones.
For these reasons, the second variant is preferable.
So let's continue with the second variant.
The result:
[iframe height=250 src="pow-2" edit border="1"]
As we could expect, the second test failed. Sure, the function returns `8` while the `assert` expects `27`.
As we could expect, the second test failed. Sure, our function always returns `8`, while the `assert` expects `27`.
## Improving the implementation
@ -224,7 +227,7 @@ The result:
## Nested describe
We're going to add more tests. But before that let's note that the helper function `makeTest` and `for` should be grouped together. We won't need `makeTest` in other tests, it's needed only in `for`: their common task is to check how `pow` raises into the given power.
We're going to add even more tests. But before that let's note that the helper function `makeTest` and `for` should be grouped together. We won't need `makeTest` in other tests, it's needed only in `for`: their common task is to check how `pow` raises into the given power.
Grouping is done with a nested `describe`:
@ -258,10 +261,10 @@ The nested `describe` defines a new "subgroup" of tests. In the output we can se
[iframe height=250 src="pow-4" edit border="1"]
In the future we can add more `it` and `describe` with helper functions of their own.
In the future we can add more `it` and `describe` on the top level with helper functions of their own, they won't see `makeTest`.
````smart header="`before/after` and `beforeEach/afterEach`"
A `describe` can have `before/after` functions that execute before/after running tests, and also `beforeEach/afterEach` functions that execute before/after *every* `it`.
We can setup `before/after` functions that execute before/after running tests, and also `beforeEach/afterEach` functions that execute before/after *every* `it`.
For instance:
@ -280,20 +283,20 @@ describe("test", function() {
});
```
The `=>` denotes, as you may recall, [arrow functions](info:function-expressions-arrows#arrow-functions). The running sequence will be:
The running sequence will be:
```
Testing started before all tests
Before a test enter a test
Testing started before all tests (before)
Before a test enter a test (beforeEach)
1
After a test exit a test
Before a test enter a test
After a test exit a test (afterEach)
Before a test enter a test (beforeEach)
2
After a test exit a test
Testing finished after all tests
After a test exit a test (afterEach)
Testing finished after all tests (after)
```
[edit src="beforeafter" title="Open the example in the sandbox"]
[edit src="beforeafter" title="Open the example in the sandbox."]
Usually, `beforeEach/afterEach` (`before/each`) are used to perform initialization, zero out counters or do something else between the tests (or test groups).
````
@ -369,9 +372,7 @@ Now it works, all tests pass:
[iframe height=300 src="pow-full" edit border="1"]
[edit src="pow-full" title="Open the full final example in the sandbox"]
[edit src="pow-full" title="Open the full final example in the sandbox."]
## Summary
@ -385,27 +386,27 @@ The spec can be used in three ways:
With the spec, we can safely improve, change, even rewrite the function from the scratch and make sure it still works right.
That's especially important when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
That's especially important in large projects when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
In practice that leads to one of the consequences:
Without tests, people have two ways:
1. Functions still get changed, refactored no matter what. And then probably users meet bugs and report them. If we can afford that.
2. Or people become afraid to modify such functions, if the punishment for errors is harsh. Then it becomes old, overgrown with cobwebs, no one wants to get into it.
1. To perform the change, no matter what. And then our users meet bugs and report them. If we can afford that.
2. Or people become afraid to modify such functions, if the punishment for errors is harsh. Then it becomes old, overgrown with cobwebs, no one wants to get into it, and that's not good.
**Automatically tested code is contrary to that!**
If the project is covered with tests -- there's just no such problem. We can run tests and a lot of checks are made in a matter of seconds.
If the project is covered with tests -- there's just no such problem. We can run tests and see a lot of checks made in a matter of seconds.
**Besides, a well-tested code has better architecture.**
Naturally, that's because it's easier to change and improve. But not only that.
Naturally, that's because it's easier to change and improve it. But not only that.
To write tests, the code should be organized in such a way that every function has a clearly described task to do, well-defined input and output. That means a good architecture from the beginning.
To write tests, the code should be organized in such a way that every function has a clearly described task, well-defined input and output. That means a good architecture from the beginning.
In real life that's sometimes not that easy. Sometimes it's difficult to write a spec before the actual code, because it's not yet clear how it should behave. But generally writing tests makes development faster and more stable.
In real life that's sometimes not that easy. Sometimes it's difficult to write a spec before the actual code, because it's not yet clear how it should behave. But in general writing tests makes development faster and more stable.
## What now?
Later in the tutorial you will meet many tasks with tests baked-in. So you will more practical examples.
Later in the tutorial you will meet many tasks with tests baked-in. So you'll see more practical examples.
Writing tests requires good JavaScript knowledge. But we're just starting to learn it. So, to settle down everything, as of now you're not required to write tests, but you should already be able to read them even if they are a little bit more complex than in this chapter.

View file

@ -6,8 +6,7 @@
<!-- add mocha framework code -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
<script>
// enable bdd-style testing
mocha.setup('bdd');
mocha.setup('bdd'); // minimal setup
</script>
<!-- add chai -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>

View file

@ -1,57 +1,57 @@
# Polyfills
The JavaScript language steadily evolves. The new proposals get analyzed and, if they look worthy, are appended to the list at <https://tc39.github.io/ecma262/> and then progress to the [specification](http://www.ecma-international.org/publications/standards/Ecma-262.htm).
The JavaScript language steadily evolves. The new proposals to the language appear regularly, they are analyzed and, if considered worthy, are appended to the list at <https://tc39.github.io/ecma262/> and then progress to the [specification](http://www.ecma-international.org/publications/standards/Ecma-262.htm).
Each JS engine has its own idea about what to implement first. It may implement proposals that are not approved yet and fail to implement things that are already in the spec, because they are less interesting or just harder to do.
Teams behind JavaScript engines have their own ideas about what to implement first. It may decide to implement proposals that are in draft and postpone things that are already in the spec, because they are less interesting or just harder to do.
So it's quite common for an engine to implement only the part of the standard.
A good page to see the current state of support for language features is <https://kangax.github.io/compat-table/es6/> (remember the link to use in the future when you know the language).
A good page to see the current state of support for language features is <https://kangax.github.io/compat-table/es6/> (it's big, we have a lot to study yet).
## Babel.JS
When we use all the modern features of the language, some engines may fail to support such code. Just as it was said, not all features are implemented everywhere.
When we use modern features of the language, some engines may fail to support such code. Just as said, not all features are implemented everywhere.
Here Babel.JS comes to the rescue.
[Babel.JS](https://babeljs.io) is a [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler). It rewrites the modern JavaScript code into the previous standard.
[Babel.JS](https://babeljs.io) is a [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler). It rewrites modern JavaScript code into the previous standard.
Actually, there are two parts in Babel:
1. The transpiler program, which rewrites the code.
1. First, the transpiler program, which rewrites the code. The developer run it on his own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build system like [webpack](http://webpack.github.io/) or [brunch](http://brunch.io/) provide means to run transpiler automatically on every code change, so that doesn't involve any time loss from our side.
The transpiler runs on a developer's computer. It rewrites the code, which is then bundled by a project build system (like [webpack](http://webpack.github.io/) or [brunch](http://brunch.io/)). Most build systems can support Babel easily.
2. Second, the polyfill.
2. The polyfill.
The transpiler rewrites the code, so syntax features are covered. But for new functions we need to a special script that implements them. JavaScript is a highly dynamic language, scripts may not just add new functions, but also modify built-in ones, so that they behave according to the modern standard.
For some functions we also need add a special script that should run before our scripts and introduce modern functions that the engine may not support by itself. There's a term "polyfill" for such scripts.
There's a term "polyfill" for scripts that "fill in" the gap and add missing implementations.
The two interesting variants are [babel polyfill](https://babeljs.io/docs/usage/polyfill/) that supports a lot, but is big and the [polyfill.io](http://polyfill.io) service that allows to load/construct polyfills on-demand, depending on the features we need.
Two interesting polyfills are:
- [babel polyfill](https://babeljs.io/docs/usage/polyfill/) that supports a lot, but is big.
- [polyfill.io](http://polyfill.io) service that allows to load/construct polyfills on-demand, depending on the features we need.
The transpiler and/or polyfill may be not needed if we orient towards more-or-less modern engines and don't use rarely supported features.
So, we need to setup the transpiler and add the polyfill for old engines to support modern features.
If we orient towards modern engines and do not use features except those supported everywhere, then we don't need to use Babel.JS.
## Examples in the tutorial
```warn header="Browser support is required"
Examples that use modern JS will work only if your browser supports it.
```
````online
Most examples are runnable at-place, like here:
Most examples are runnable at-place, like this:
```js run
alert('Press the "Play" button in the upper-right corner to run');
```
...But if it uses a feature that your browser does not support, an error is shown.
Examples that use modern JS will work only if your browser supports it.
```
That doesn't mean that the example is wrong! It's just the browser lacking the support for certain features yet.
````
```offline
As you're reading the offline version, examples are not runnable. But they usually work :)
```
[Chrome Canary](https://www.google.com/chrome/browser/canary.html) is good for more examples.
[Chrome Canary](https://www.google.com/chrome/browser/canary.html) is good for all examples, but other modern browsers are mostly fine too.
Note that on production we can use Babel to translate the code into suitable for less recent browsers, so there will be no such limitation, the code will run everywhere.
Now we can go coding, so let's choose a good code editor.

View file

@ -1,5 +1,3 @@
# Code quality
This chapter goes early to explain coding practices that we'll use further in the development.
This chapter explains coding practices that we'll use further in the development.