up
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
@ -121,7 +121,7 @@ let userName = 'John';
|
|||
|
||||
function showMessage() {
|
||||
*!*
|
||||
let userName = "Bob"; // declare a local variable
|
||||
let userName = "Bob"; // declare a local variable
|
||||
*/!*
|
||||
|
||||
let message = 'Hello, my name is ' + userName;
|
||||
|
@ -137,16 +137,16 @@ alert( userName ); // John, the function did not access the outer variable
|
|||
```
|
||||
|
||||
[smart header="Global variables"]
|
||||
Variables declared outside of any function, are called *global*.
|
||||
Variables declared outside of any function, such as the outer `userName` in the code above, are called *global*.
|
||||
|
||||
Global variables are visible from any function.
|
||||
Global variables are visible from any function.
|
||||
|
||||
They should be only used to store the data of project-wide importance. Variables needed by specific tasks should reside in the corresponding functions.
|
||||
They should only be used if the data is so important that it really must be seen from anywhere.
|
||||
[/smart]
|
||||
|
||||
## Parameters
|
||||
|
||||
We can pass arbitrary data to the function using it's parameters (also called *function arguments*) .
|
||||
We can pass arbitrary data to function using it's parameters (also called *function arguments*) .
|
||||
|
||||
In the example below, the function has two parameters: `from` and `text`.
|
||||
|
||||
|
@ -374,7 +374,7 @@ Few examples of breaking this rule:
|
|||
<ul>
|
||||
<li>`getAge` -- if shows the age to the visitor (should only get).</li>
|
||||
<li>`createForm` -- if modifies the document, adds a form to it (should only create it and return).</li>
|
||||
<li>`checkPermission` -- if displays the `access granted/denied` message or stores the result of the check anywhere (should only perform the check and return the result).</li>
|
||||
<li>`checkPermission` -- if displays the `access granted/denied` message or stores the result of the check (should only perform the check and return the result).</li>
|
||||
</ul>
|
||||
[/smart]
|
||||
|
||||
|
@ -384,7 +384,7 @@ Functions that are used *very often* sometimes have ultrashort names.
|
|||
|
||||
For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
|
||||
|
||||
These are exceptions though. Generally the functions names should be concise, but descriptive.
|
||||
These are exceptions. Generally functions names should be concise, but descriptive.
|
||||
[/smart]
|
||||
|
||||
|
||||
|
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
@ -1,263 +0,0 @@
|
|||
# Function expressions
|
||||
|
||||
Function Expression is analternative syntax for creating a function.
|
||||
|
||||
It looks like this:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let func = function(parameters) {
|
||||
// body
|
||||
};
|
||||
```
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let sayHi = function(person) {
|
||||
alert( `Hello, ${person}` );
|
||||
};
|
||||
|
||||
sayHi('John'); // Hello, John
|
||||
```
|
||||
|
||||
The function created in the example above is fully functional and identical to:
|
||||
|
||||
```js
|
||||
function sayHi(person) {
|
||||
alert( `Hello, ${person}` );
|
||||
}
|
||||
```
|
||||
|
||||
## Function is a value
|
||||
|
||||
Function Expressions clearly demonstrate one simple thing.
|
||||
|
||||
**In JavaScript, function is a kind of a value.**
|
||||
|
||||
We can declare it as we did before:
|
||||
|
||||
```js
|
||||
function sayHi() {
|
||||
alert( "Hello" );
|
||||
}
|
||||
```
|
||||
|
||||
...Or as a function expression:
|
||||
|
||||
```js
|
||||
let sayHi = function() {
|
||||
alert( "Hello" );
|
||||
}
|
||||
```
|
||||
|
||||
The meaning of these lines is the same: create a function, put it into the variable `sayHi`.
|
||||
|
||||
Yes, no matter, how it is defined -- it's just a value.
|
||||
|
||||
We can even show it using `alert`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sayHi() {
|
||||
alert( "Hello" );
|
||||
}
|
||||
|
||||
*!*
|
||||
alert( sayHi ); // shows the function code
|
||||
*/!*
|
||||
```
|
||||
|
||||
Note that there are no brackets after `sayHi` in the last line. The function is not called there. Instead the `alert` shows it's string representation, that is the source code.
|
||||
|
||||
As the function is a value, we can also copy it to another variable:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
function sayHi() { // (1) create
|
||||
alert( "Hello" );
|
||||
}
|
||||
|
||||
let func = sayHi; // (2) copy
|
||||
func(); // Hello // (3) callable!
|
||||
|
||||
sayHi = null; // (4) kill old
|
||||
sayHi(); // error! (null now)
|
||||
```
|
||||
|
||||
<ol>
|
||||
<li>Function declaration `(1)` creates the function and puts it into the variable `sayHi`"</li>
|
||||
<li>Line `(2)` copies it into variable `func`.
|
||||
|
||||
Please note again: there are no brackets after `sayHi`. If they were, then the call `let func = sayHi()` would write a *result* of `sayHi()` into `func`, not the function itself.</li>
|
||||
<li>At the moment `(3)` the function can be called both as `sayHi()` and `func()`.</li>
|
||||
<li>...We can overwrite `sayHi` easily. As well as `func`, they are normal variables. Naturally, the call attempt would fail in the case `(4)`.</li>
|
||||
</ol>
|
||||
|
||||
Again, it does not matter how to create a function here. If we change the first line above into a Function Expression: `let sayHi = function() {`, everything would be the same.
|
||||
|
||||
[smart header="A function is a value representing an \"action\""]
|
||||
Regular values like strings or numbers represent the *data*.
|
||||
|
||||
A function can be perceived as an *action*.
|
||||
|
||||
We can copy it between variables and run when we want.
|
||||
[/smart]
|
||||
|
||||
|
||||
## Comparison with Function Declaration
|
||||
|
||||
The "classic" syntax of the function that looks like `function name(params) {...}` is called a "Function Declaration".
|
||||
|
||||
We can formulate the following distinction:
|
||||
<ul>
|
||||
<li>*Function Declaration* -- is a function, declared as a separate statement.
|
||||
|
||||
```js
|
||||
// Function Declaration
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>*Function Expression* -- is a function, created anywhere inside an expression.
|
||||
|
||||
Here the function is created to the right side of an assignment:
|
||||
```js
|
||||
// Function Expression
|
||||
let sum = function(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
Soon we're going to see more use cases.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
The main difference between them is the creation time.
|
||||
|
||||
<ul>
|
||||
<li>Function Declarations are processed before the code begins to execute.</li>
|
||||
<li>Function Expressions are created when the execution reaches them.</li>
|
||||
</ul>
|
||||
|
||||
In other words, when JavaScript prepares to run the code block, it looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage". Then it runs the code. And while the code is running, Function Expressions can be created.
|
||||
|
||||
As a side-effect, functions declared as Function Declaration can be called earlier than they are defined.
|
||||
|
||||
For instance, this works:
|
||||
|
||||
```js
|
||||
//+ run refresh untrusted
|
||||
*!*
|
||||
sayHi("John"); // Hello, John
|
||||
*/!*
|
||||
|
||||
function sayHi(name) {
|
||||
alert( `Hello, ${name}` );
|
||||
}
|
||||
```
|
||||
|
||||
...And if there were Function Expression, then it wouldn't work:
|
||||
|
||||
```js
|
||||
//+ run refresh untrusted
|
||||
*!*
|
||||
sayHi("John"); // error!
|
||||
*/!*
|
||||
|
||||
let sayHi = function(name) { // (*)
|
||||
alert( `Hello, ${name}` );
|
||||
};
|
||||
```
|
||||
|
||||
Function Expressions are created in the process of evaluation of the expression with them. So, in the code above, the function is created and assigned to `sayHi` only when the execution reaches the line `(*)`.
|
||||
|
||||
Usually, a Function Declaration is preferable because of that. It gives more freedom in how to organize our code.
|
||||
|
||||
## Anonymous functions
|
||||
|
||||
As a function is a value, it can be created on-demand and passed to another place of the code.
|
||||
|
||||
For instance, let's consider the following real-life task.
|
||||
|
||||
Function `ask(question, yes, no)` should accept a question and two other functions: `yes` and `no`. It asks a question and, if the user responds positively, executes `yes()`, otherwise `no()`.
|
||||
|
||||
It could look like this:
|
||||
```js
|
||||
//+ run
|
||||
function ask(question, yes, no) {
|
||||
if (confirm(question)) yes()
|
||||
else no();
|
||||
}
|
||||
```
|
||||
|
||||
In real-life `ask` will be usually much more complex, because it draws a nice windows and takes care about how to preent the `question` to the user, but here we don't care about that. Just `confirm` is enough.
|
||||
|
||||
So, how do we use it?
|
||||
|
||||
If we had only Function Declarations in our toolbox, we could declare `showOk/showCancel` as actions and pass them:
|
||||
|
||||
|
||||
```js
|
||||
function showOk() {
|
||||
alert( "Ok, proceeding." );
|
||||
}
|
||||
|
||||
function showCancel() {
|
||||
alert( "Execution canceled." );
|
||||
}
|
||||
|
||||
// usage
|
||||
ask("Should we proceed?", showOk, showCancel);
|
||||
```
|
||||
|
||||
...But Function Expressions allow us to solve the task much more elegantly:
|
||||
|
||||
```js
|
||||
ask("Should we proceed?",
|
||||
function() { alert( "Ok, proceeding." ); },
|
||||
function() { alert( "Execution canceled." ); },
|
||||
);
|
||||
```
|
||||
|
||||
So, we can declare a function in-place, exactly when we need it.
|
||||
|
||||
These functions are sometimes called "anonymous" meaning that they are defined without a name. So to say, we can't reuse this function anywhere outside of `ask`. And that's fine here.
|
||||
|
||||
Creating functions in-place is very natural and in the spirit of JavaScript.
|
||||
|
||||
## Summary
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Functions are values. They can be assigned, copied or declared in any place of the code.
|
||||
<ul>
|
||||
<li>If the function is declared as a separate statement -- it's called a Function Declaration.</li>
|
||||
<li>If the function is created as a part of an expression -- it's a Function Expression.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Function Declarations are processed before the code is executed. JavaScript scans the code, looks for them and creates corresponding functions.</li>
|
||||
<li>Function Expressions are created when the execution flow reaches them.</li>
|
||||
</ul>
|
||||
|
||||
Novice programmers sometimes overuse Function Expressions. Somewhy, maybe not quite understanding what's going on, they create functions like `let func = function()`.
|
||||
|
||||
But in most cases Function Declaration is preferable.
|
||||
|
||||
Compare, which one is more readable:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
// Function Expression
|
||||
let f = function() { ... }
|
||||
|
||||
// Function Declaration
|
||||
function f() { ... }
|
||||
```
|
||||
|
||||
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the declaration.
|
||||
|
||||
Use Function Expression only when the function must be created at-place, inside another expression.
|
||||
|
390
1-js/2-first-steps/19-function-expression/article.md
Normal file
|
@ -0,0 +1,390 @@
|
|||
# Function expressions
|
||||
|
||||
Function Expression is an analternative syntax for declaring a function.
|
||||
|
||||
It looks like this:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let func = function(parameters) {
|
||||
// body
|
||||
};
|
||||
```
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let sayHi = function(person) {
|
||||
alert( `Hello, ${person}` );
|
||||
};
|
||||
|
||||
sayHi('John'); // Hello, John
|
||||
```
|
||||
|
||||
The function `sayHi` created in the example above is identical to:
|
||||
|
||||
```js
|
||||
function sayHi(person) {
|
||||
alert( `Hello, ${person}` );
|
||||
}
|
||||
```
|
||||
|
||||
## Function is a value
|
||||
|
||||
Function Expression clearly demonstrates one simple thing.
|
||||
|
||||
**In JavaScript, a function is a value.**
|
||||
|
||||
We can declare it as we did before:
|
||||
|
||||
```js
|
||||
function sayHi() {
|
||||
alert( "Hello" );
|
||||
}
|
||||
```
|
||||
|
||||
...Or as a function expression:
|
||||
|
||||
```js
|
||||
let sayHi = function() {
|
||||
alert( "Hello" );
|
||||
}
|
||||
```
|
||||
|
||||
The meaning of these lines is the same: create a function, put it into the variable `sayHi`.
|
||||
|
||||
Yes, no matter, how it is defined -- it's just a value, stored in the variable `sayHi`.
|
||||
|
||||
We can even show it using `alert`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sayHi() {
|
||||
alert( "Hello" );
|
||||
}
|
||||
|
||||
*!*
|
||||
alert( sayHi ); // shows the function code
|
||||
*/!*
|
||||
```
|
||||
|
||||
Note that there are no brackets after `sayHi` in the last line. The function is not called there. Instead the `alert` shows it's string representation, that is the source code.
|
||||
|
||||
As the function is a value, we can also copy it to another variable:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
function sayHi() { // (1) create
|
||||
alert( "Hello" );
|
||||
}
|
||||
|
||||
let func = sayHi; // (2) copy
|
||||
|
||||
func(); // Hello // (3) call the copy (it works)!
|
||||
|
||||
sayHi = null; // (4) nullify the old variable
|
||||
sayHi(); // error! (null now)
|
||||
```
|
||||
|
||||
<ol>
|
||||
<li>Function declaration `(1)` creates the function and puts it into the variable `sayHi`"</li>
|
||||
<li>Line `(2)` copies it into variable `func`.
|
||||
|
||||
Please note again: there are no brackets after `sayHi`. If they were, then `func = sayHi()` would write *the result* of the call `sayHi()` into `func`, not the function `sayHi` itself.</li>
|
||||
<li>At the moment `(3)` the function can be called both as `sayHi()` and `func()`.</li>
|
||||
<li>...We can overwrite `sayHi` easily. As `func`, it is a normal variable. Naturally, the call attempt would fail in the case `(4)`.</li>
|
||||
</ol>
|
||||
|
||||
Again, it does not matter how to create a function here. If we change the first line above into a Function Expression: `let sayHi = function() {`, everything would be the same.
|
||||
|
||||
[smart header="A function is a value representing an \"action\""]
|
||||
Regular values like strings or numbers represent the *data*.
|
||||
|
||||
A function can be perceived as an *action*.
|
||||
|
||||
We can copy it between variables and run when we want.
|
||||
[/smart]
|
||||
|
||||
|
||||
Function Expressions are very convenient for creating an action in-place and passing it along the code.
|
||||
|
||||
For instance, let's consider the following real-life task.
|
||||
|
||||
Function `ask(question, yes, no)` should accept a textual `question` and two other functions: `yes` and `no`. It asks a question and, if the user responds positively, executes `yes()`, otherwise `no()`.
|
||||
|
||||
It could look like this:
|
||||
```js
|
||||
//+ run
|
||||
function ask(question, yes, no) {
|
||||
if (confirm(question)) yes()
|
||||
else no();
|
||||
}
|
||||
```
|
||||
|
||||
In real-life `ask` would be usually much more complex, because it would draw a nice window and take care about how to present the `question` to the user etc. But here we consider the general form of such function, so `confirm` is enough.
|
||||
|
||||
So, how do we use it?
|
||||
|
||||
If we had only Function Declarations in our toolbox, we could declare `showOk/showCancel` and pass them:
|
||||
|
||||
|
||||
```js
|
||||
function showOk() {
|
||||
alert( "Ok, proceeding." );
|
||||
}
|
||||
|
||||
function showCancel() {
|
||||
alert( "Execution canceled." );
|
||||
}
|
||||
|
||||
// usage
|
||||
ask("Should we proceed?", showOk, showCancel);
|
||||
```
|
||||
|
||||
...But Function Expressions allow us to solve the task much more elegantly:
|
||||
|
||||
```js
|
||||
ask("Should we proceed?",
|
||||
function() { alert( "Ok, proceeding." ); },
|
||||
function() { alert( "Execution canceled." ); },
|
||||
);
|
||||
```
|
||||
|
||||
Here we can declare actions in-place, exactly when we need it.
|
||||
|
||||
Such functions are sometimes called "anonymous" meaning that they are defined without a name. So to say, we can't reuse them outside of `ask`, because there is no variable for them. But for this kind of task it's exactly what we want.
|
||||
|
||||
Creating functions in-place is very natural and in the spirit of JavaScript.
|
||||
|
||||
|
||||
## Function Expression vs Function Declaration
|
||||
|
||||
The "classic" syntax of the function that looks like `function name(params) {...}` is called a "Function Declaration".
|
||||
|
||||
We can formulate the following distinction:
|
||||
<ul>
|
||||
<li>*Function Declaration* -- is a function, declared as a separate statement.
|
||||
|
||||
```js
|
||||
// Function Declaration
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>*Function Expression* -- is a function, created in the context of an expression.
|
||||
|
||||
In the example above the function was created in the context of an "assignment expression":
|
||||
```js
|
||||
// Function Expression
|
||||
let sum = function(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
The main differences between them are visibility and creation time.
|
||||
|
||||
**Function Expressions are created when the execution reaches them.**
|
||||
|
||||
That's easy. Once the execution flow passes to the right side of the assignment -- here we go, the function is made and can be used (assigned, called etc) from now on.
|
||||
|
||||
Function Declarations are different. They are treated in a special way.
|
||||
|
||||
When JavaScript prepares to run the code block, it looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage". Then it runs the code.
|
||||
|
||||
**Function Declarations are created before the code block begins to execute and is visible in the whole block.**
|
||||
|
||||
If the Function Declaration is not inside a code block `{...}`, then it is created when the script starts and is visible in the whole script. All declarations in the examples above are not in the code block.
|
||||
|
||||
As a natural effect, a function declared as Function Declaration can be called earlier than it is defined.
|
||||
|
||||
For instance, this works:
|
||||
|
||||
```js
|
||||
//+ run refresh untrusted
|
||||
*!*
|
||||
sayHi("John"); // Hello, John
|
||||
*/!*
|
||||
|
||||
function sayHi(name) {
|
||||
alert( `Hello, ${name}` );
|
||||
}
|
||||
```
|
||||
|
||||
Function Declaration `sayHi` is created when the script starts and is visible everywhere in it.
|
||||
|
||||
...And if there were Function Expression, then it wouldn't work:
|
||||
|
||||
```js
|
||||
//+ run refresh untrusted
|
||||
*!*
|
||||
sayHi("John"); // error!
|
||||
*/!*
|
||||
|
||||
let sayHi = function(name) { // (*)
|
||||
alert( `Hello, ${name}` );
|
||||
};
|
||||
```
|
||||
|
||||
Function Expressions are created in the process of evaluation of the expression with them. So, in the code above, the function would be created and assigned to `sayHi` only when the execution reaches the line `(*)`. Too late.
|
||||
|
||||
Now let's explore the visibility thing.
|
||||
|
||||
Imagine, we need to declare `welcome()` depending on some data we get in run-time.
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
||||
let age = prompt("What is your age?", 18);
|
||||
|
||||
if (age < 18) {
|
||||
|
||||
function welcome() {
|
||||
alert("Hello!");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
function welcome() {
|
||||
alert("Greetings!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*!*
|
||||
welcome(); // Error: welcome is not defined
|
||||
*/!*
|
||||
```
|
||||
|
||||
In the code above, we mean to create function `welcome()` depending on the `age`. So, that it can't be called later, probably from another place of the code, in case of an event or such.
|
||||
|
||||
But that doesn't work, because Function Declaration `function welcome()` is visible only inside the block where it resides.
|
||||
|
||||
We can call it from within the block, but not from outside:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let age = 15; // let's consider 15 as an example
|
||||
|
||||
if (age < 18) {
|
||||
*!*
|
||||
welcome(); // \ (works)
|
||||
*/!*
|
||||
// |
|
||||
function welcome() { // |
|
||||
alert("Hello!"); // | available here
|
||||
} // |
|
||||
// |
|
||||
*!*
|
||||
welcome(); // / (works)
|
||||
*/!*
|
||||
|
||||
} else {
|
||||
// \
|
||||
function welcome() { // |
|
||||
alert("Greetings!"); // | available here
|
||||
} // |
|
||||
// /
|
||||
}
|
||||
|
||||
*!*
|
||||
welcome(); // Error: outside of the block there's no welcome
|
||||
*/!*
|
||||
```
|
||||
|
||||
What can we do to fix the problem? One of the ways is to use a Function Expression and assign `welcome` to the variable which has the proper visibility:
|
||||
|
||||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
||||
let age = prompt("What is your age?", 18);
|
||||
|
||||
let welcome;
|
||||
|
||||
if (age < 18) {
|
||||
|
||||
welcome = function() {
|
||||
alert("Hello!");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
welcome = function() {
|
||||
alert("Greetings!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*!*
|
||||
welcome(); // ok now
|
||||
*/!*
|
||||
```
|
||||
|
||||
Or we could go on to simplify it even further using a question mark operator `?`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
||||
let age = prompt("What is your age?", 18);
|
||||
|
||||
let welcome = (age < 18) ?
|
||||
function() {
|
||||
alert("Hello!");
|
||||
} :
|
||||
function() {
|
||||
alert("Greetings!");
|
||||
}
|
||||
|
||||
*!*
|
||||
welcome(); // ok now
|
||||
*/!*
|
||||
```
|
||||
|
||||
[smart header="So, a Declaration or an Expression?"]
|
||||
As a rule of thumb, if we want just to declare a function, then a Function Declaration is prefered. It gives more freedom in how to organize our code, because we can put "helper functions" below. It's also a little bit easier to read and look up them in the text.
|
||||
|
||||
Only if a Function Declaration does not suit us for some reason, then a Function Expression should be used.
|
||||
[/smart]
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Functions are values. They can be assigned, copied or declared in any place of the code.
|
||||
<ul>
|
||||
<li>If the function is declared as a separate statement -- it's called a Function Declaration.</li>
|
||||
<li>If the function is created as a part of an expression -- it's a Function Expression.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Function Declarations are processed before the code block is executed. So they are available everywhere in the block. Or in the whole script if not enclosed in a block.</li>
|
||||
<li>Function Expressions are created when the execution flow reaches them.</li>
|
||||
</ul>
|
||||
|
||||
Novice programmers sometimes overuse Function Expression by creating many functions with `let func = function()`.
|
||||
|
||||
But in most cases Function Declaration is preferable.
|
||||
|
||||
Compare, which code is more readable:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
// Function Expression
|
||||
let f = function() { ... }
|
||||
|
||||
// Function Declaration
|
||||
function f() { ... }
|
||||
```
|
||||
|
||||
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the declaration.
|
||||
|
||||
Use Function Expression only when the function must be created at-place, inside another expression.
|
|
@ -1,101 +1,105 @@
|
|||
# Советы по стилю кода
|
||||
# Coding style
|
||||
|
||||
Код должен быть максимально читаемым и понятным. Для этого нужен *хороший стиль* написания кода. В этой главе мы рассмотрим компоненты такого стиля.
|
||||
Our code must be as clean and easy to read as possible.
|
||||
|
||||
That is actually an art of programming -- to take a complex task and describe it using a programming language in a way that is both correct and human-readable.
|
||||
|
||||
One thing to help is a good code style. In this chapter we cover it's components.
|
||||
|
||||
[cut]
|
||||
## Синтаксис
|
||||
## Syntax
|
||||
|
||||
Шпаргалка с правилами синтаксиса (детально их варианты разобраны далее):
|
||||
A cheatsheep with the rules (more details below):
|
||||
|
||||
<img src="code-style.png">
|
||||
|
||||
<!--
|
||||
```js
|
||||
function pow(x, n) {
|
||||
var result = 1;
|
||||
let result = 1;
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var x = prompt("x?", "");
|
||||
var n = prompt("n?", "");
|
||||
let x = prompt("x?", "");
|
||||
let n = prompt("n?", "");
|
||||
|
||||
if (n < 0) {
|
||||
alert('Степень ' + n +
|
||||
'не поддерживается, введите целую степень, большую 0');
|
||||
alert(`Power ${n} is not supported,
|
||||
please enter an integer number, greater than 0`);
|
||||
} else {
|
||||
alert( pow(x, n) );
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
Не всё здесь однозначно, так что разберём эти правила подробнее.
|
||||
Nothing is "carved in stone" here, so let's discuss the rules in detail.
|
||||
|
||||
### Фигурные скобки
|
||||
### 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.
|
||||
|
||||
If the code has only one line, then there are options:
|
||||
|
||||
<!--
|
||||
```js
|
||||
//+ no-beautify
|
||||
if (n < 0) {alert('Степень ' + n + ' не поддерживается');}
|
||||
if (n < 0) {alert(`Power ${n} is not supported`);}
|
||||
|
||||
|
||||
|
||||
if (n < 0) alert('Степень ' + n + ' не поддерживается');
|
||||
if (n < 0) alert(`Power ${n} is not supported`);
|
||||
|
||||
|
||||
|
||||
if (n < 0) {
|
||||
alert('Степень ' + n + ' не поддерживается');
|
||||
alert(`Power ${n} is not supported`);
|
||||
}
|
||||
|
||||
```
|
||||
-->
|
||||
|
||||
<img src="figure-bracket-style.png">
|
||||
|
||||
Если у вас уже есть опыт в разработке, и вы привыкли делать скобку на отдельной строке -- это тоже вариант. В конце концов, решать вам. Но в большинстве JavaScript-фреймворков стиль именно такой.
|
||||
For a real short code like `if (cond) return null`, one line is acceptable... But still a separate line for each statement is usually easier to read.
|
||||
|
||||
Если условие и код достаточно короткие, например `if (cond) return null`, то запись в одну строку вполне читаема... Но, как правило, отдельная строка всё равно воспринимается лучше.
|
||||
### Line length
|
||||
|
||||
### Длина строки
|
||||
Line length is limited. No one likes to eye-follow a long horizontal line. Doesn't matter if it's a text or a OR'ed list of `if` conditions. It's better to split it.
|
||||
|
||||
Максимальную длину строки согласовывают в команде. Как правило, это либо `80`, либо `120` символов, в зависимости от того, какие мониторы у разработчиков.
|
||||
Maximal line length is agreed on the team-level. It's usually 80 or 120 characters.
|
||||
|
||||
Более длинные строки необходимо разбивать для улучшения читаемости.
|
||||
### Indents
|
||||
|
||||
### Отступы
|
||||
|
||||
Отступы нужны двух типов:
|
||||
There are two types of indents:
|
||||
|
||||
<ul>
|
||||
<li>**Горизонтальный отступ, при вложенности -- два(или четыре) пробела.**
|
||||
<li>**A horizontal indent: 2(4) spaces.**
|
||||
|
||||
Как правило, используются именно пробелы, т.к. они позволяют сделать более гибкие "конфигурации отступов", чем символ "Tab".
|
||||
Usually spaces are used, because they allow more flexible configurations of indents than the "Tab" symbol.
|
||||
|
||||
For instance, we can align the arguments with the opening bracket:
|
||||
|
||||
Например, выровнять аргументы относительно открывающей скобки:
|
||||
```js
|
||||
//+ no-beautify
|
||||
show("Строки" +
|
||||
" выровнены" +
|
||||
" строго" +
|
||||
" одна под другой");
|
||||
show(parameters,
|
||||
aligned,
|
||||
one,
|
||||
after,
|
||||
another);
|
||||
```
|
||||
</li>
|
||||
<li>**Вертикальный отступ, для лучшей разбивки кода -- перевод строки.**
|
||||
<li>**A vertical indent, line breaks 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:
|
||||
|
||||
```js
|
||||
function pow(x, n) {
|
||||
var result = 1;
|
||||
// <--
|
||||
for (var i = 0; i < n; i++) {
|
||||
let result = 1;
|
||||
// <--
|
||||
for (let i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
// <--
|
||||
|
@ -103,59 +107,64 @@ function pow(x, n) {
|
|||
}
|
||||
```
|
||||
|
||||
Вставляйте дополнительный перевод строки туда, где это сделает код более читаемым. Не должно быть более 9 строк кода подряд без вертикального отступа.
|
||||
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.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
### Точка с запятой
|
||||
### A semicolon
|
||||
|
||||
Точки с запятой нужно ставить, даже если их, казалось бы, можно пропустить.
|
||||
Semicolons must be after each statement. Even if could possiblty be skipped.
|
||||
|
||||
Есть языки, в которых точка с запятой не обязательна, и её там никто не ставит. В JavaScript перевод строки её заменяет, но лишь частично, поэтому лучше её ставить, как обсуждалось [ранее](#semicolon).
|
||||
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 must be at place, just as discussed [before](#semicolon).
|
||||
|
||||
## The Naming
|
||||
|
||||
The general rule:
|
||||
|
||||
Общее правило:
|
||||
<ul>
|
||||
<li>Имя переменной -- существительное.</li>
|
||||
<li>Имя функции -- глагол или начинается с глагола. Бывает, что имена для краткости делают существительными, но глаголы понятнее.</li>
|
||||
<li>Variable name should be a noun.</li>
|
||||
<li>Function name should be a verb, or start with a verbal prefix. There can be exceptions if covered by another rule.</li>
|
||||
</ul>
|
||||
|
||||
Для имён используется английский язык (не транслит) и верблюжья нотация.
|
||||
English language and camel-case notation is used.
|
||||
|
||||
Более подробно -- читайте про [имена функций](#function-naming) и [имена переменных](#variable-naming).
|
||||
Also discussed before -- [function naming](#function-naming) and [variables naming](#variable-naming).
|
||||
|
||||
## Уровни вложенности
|
||||
## Nesting levels
|
||||
|
||||
Уровней вложенности должно быть немного.
|
||||
There should not be too many nesting levels.
|
||||
|
||||
Например, [проверки в циклах можно делать через "continue"](#continue), чтобы не было дополнительного уровня `if(..) { ... }`:
|
||||
Sometimes it's a good idea to [use the "continue"](#continue) directive in the loop to evade extra nesting in `if(..) { ... }`:
|
||||
|
||||
Вместо:
|
||||
Instead of:
|
||||
|
||||
```js
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (i подходит) {
|
||||
... // <- уровень вложенности 2
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (cond) {
|
||||
... // <- one more nesting level
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Используйте:
|
||||
We can write:
|
||||
|
||||
```js
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (i *!*не*/!* подходит) *!*continue*/!*;
|
||||
... // <- уровень вложенности 1
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (!cond) *!*continue*/!*;
|
||||
... // <- no extra nesting level
|
||||
}
|
||||
```
|
||||
|
||||
Аналогичная ситуация -- с `if/else` и `return`. Следующие две конструкции идентичны.
|
||||
The similar thing can be done with `if/else` and `return`.
|
||||
|
||||
Первая:
|
||||
For example, two constructs below are identical.
|
||||
|
||||
The first one:
|
||||
|
||||
```js
|
||||
function isEven(n) { // проверка чётности
|
||||
function isEven(n) { // returns whether the number is even
|
||||
if (n % 2 == 0) {
|
||||
return true;
|
||||
*!*
|
||||
|
@ -166,10 +175,10 @@ function isEven(n) { // проверка чётности
|
|||
}
|
||||
```
|
||||
|
||||
Вторая:
|
||||
The second one:
|
||||
|
||||
```js
|
||||
function isEven(n) { // проверка чётности
|
||||
function isEven(n) {
|
||||
if (n % 2 == 0) {
|
||||
return true;
|
||||
}
|
||||
|
@ -180,33 +189,33 @@ function isEven(n) { // проверка чётности
|
|||
}
|
||||
```
|
||||
|
||||
Если в блоке `if` идёт `return`, то `else` за ним не нужен.
|
||||
If there's a `return` inside the `if` block, then we need no `else` after it.
|
||||
|
||||
**Лучше быстро обработать простые случаи, вернуть результат, а дальше разбираться со сложным, без дополнительного уровня вложенности.**
|
||||
|
||||
В случае с функцией `isEven` можно было бы поступить и проще:
|
||||
...Of course we can write even shorter here:
|
||||
|
||||
```js
|
||||
function isEven(n) { // проверка чётности
|
||||
function isEven(n) {
|
||||
return !(n % 2);
|
||||
}
|
||||
```
|
||||
|
||||
...Однако, если код `!(n % 2)` для вас менее очевиден чем предыдущий вариант, то стоит использовать предыдущий.
|
||||
...But if the code `!(n % 2)` is less obvious for you than the former variant then use the former one.
|
||||
|
||||
Главное для нас -- не краткость кода, а его простота и читаемость. Совсем не всегда более короткий код проще для понимания, чем более развёрнутый.
|
||||
**The most important for us is not shortness, but simplicity and readability of the code.**
|
||||
|
||||
## Функции = Комментарии
|
||||
It's quite not always the case that a brief code is simpler to understand.
|
||||
|
||||
Функции должны быть небольшими. Если функция большая -- желательно разбить её на несколько.
|
||||
## Functions = Comments
|
||||
|
||||
Этому правилу бывает сложно следовать, но оно стоит того. При чем же здесь комментарии?
|
||||
Functions should be short and do exactly one thing. If that thing is big, maybe it's worth to split the function into parts.
|
||||
|
||||
Вызов отдельной небольшой функции не только легче отлаживать и тестировать -- сам факт его наличия является *отличным комментарием*.
|
||||
Sometimes following this rule may be not easy, but it's a definitely good thing. So what's with the comments?
|
||||
|
||||
Сравните, например, две функции `showPrimes(n)` для вывода простых чисел до `n`.
|
||||
A separate function is not only easier to test and debug -- it's very existance is a great comment.
|
||||
|
||||
Первый вариант использует метку:
|
||||
For instance, compare the two functions `showPrimes(n)` below. Each one outputs [prime numbers](https://en.wikipedia.org/wiki/Prime_number) up to `n`.
|
||||
|
||||
The first variant uses a label:
|
||||
|
||||
```js
|
||||
function showPrimes(n) {
|
||||
|
@ -216,42 +225,42 @@ function showPrimes(n) {
|
|||
if (i % j == 0) continue nextPrime;
|
||||
}
|
||||
|
||||
alert( i ); // простое
|
||||
alert( i ); // a prime
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Второй вариант -- дополнительную функцию `isPrime(n)` для проверки на простоту:
|
||||
The second variant uses an additional function `isPrime(n)` to test primality:
|
||||
|
||||
```js
|
||||
function showPrimes(n) {
|
||||
|
||||
for (var i = 2; i < n; i++) {
|
||||
for (let i = 2; i < n; i++) {
|
||||
*!*if (!isPrime(i)) continue;*/!*
|
||||
|
||||
alert(i); // простое
|
||||
alert(i); // a prime
|
||||
}
|
||||
}
|
||||
|
||||
function isPrime(n) {
|
||||
for (var i = 2; i < n; i++) {
|
||||
for (let i = 2; i < n; i++) {
|
||||
if ( n % i == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Второй вариант проще и понятнее, не правда ли? Вместо участка кода мы видим описание действия, которое там совершается (проверка `isPrime`).
|
||||
|
||||
## Функции -- под кодом
|
||||
The second variant is easier to understand isn't it? Instead of the code piece we see a name of the action (`isPrime`). Sometimes people refer to such code as *self-describing*.
|
||||
|
||||
Есть два способа расположить функции, необходимые для выполнения кода.
|
||||
## Functions below the code
|
||||
|
||||
There are three way to place the "helper" functions used in the code.
|
||||
|
||||
<ol>
|
||||
<li>Функции над кодом, который их использует:
|
||||
<li>Above the code that uses them:
|
||||
|
||||
```js
|
||||
// *!*объявить функции*/!*
|
||||
// *!*function declarations*/!*
|
||||
function createElement() {
|
||||
...
|
||||
}
|
||||
|
@ -264,22 +273,22 @@ function walkAround() {
|
|||
...
|
||||
}
|
||||
|
||||
// *!*код, использующий функции*/!*
|
||||
// *!*the code which uses them*/!*
|
||||
var elem = createElement();
|
||||
setHandler(elem);
|
||||
walkAround();
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Сначала код, а функции внизу:
|
||||
<li>Code first, then functions
|
||||
|
||||
```js
|
||||
// *!*код, использующий функции*/!*
|
||||
// *!*the code which uses the functions*/!*
|
||||
var elem = createElement();
|
||||
setHandler(elem);
|
||||
walkAround();
|
||||
|
||||
// --- *!*функции*/!* ---
|
||||
// --- *!*helper functions*/!* ---
|
||||
|
||||
function createElement() {
|
||||
...
|
||||
|
@ -295,17 +304,14 @@ function walkAround() {
|
|||
```
|
||||
|
||||
</li>
|
||||
<li>Mixed, a function is described when it's first used.</li>
|
||||
</ol>
|
||||
|
||||
...На самом деле существует еще третий "стиль", при котором функции хаотично разбросаны по коду, но это ведь не наш метод, да?
|
||||
Most of time, the second variant is preferred.
|
||||
|
||||
**Как правило, лучше располагать функции под кодом, который их использует.**
|
||||
That's because when reading a code, we first want to know "what it does". If the code goes first, then it provides that information. And then maybe we won't need to read functions at all, especially if their names are adequate to what they're doing.
|
||||
|
||||
То есть, предпочтителен 2й способ.
|
||||
|
||||
Дело в том, что при чтении такого кода мы хотим знать в первую очередь, *что он делает*, а уже затем *какие функции ему помогают.* Если первым идёт код, то это как раз дает необходимую информацию. Что же касается функций, то вполне возможно нам и не понадобится их читать, особенно если они названы адекватно и то, что они делают, понятно из названия.
|
||||
|
||||
## Плохие комментарии
|
||||
## Bad comments
|
||||
|
||||
В коде нужны комментарии.
|
||||
|
BIN
1-js/2-first-steps/20-coding-style/code-style.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
1-js/2-first-steps/20-coding-style/code-style@2x.png
Normal file
After Width: | Height: | Size: 236 KiB |
BIN
1-js/2-first-steps/20-coding-style/figure-bracket-style.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
1-js/2-first-steps/20-coding-style/figure-bracket-style@2x.png
Normal file
After Width: | Height: | Size: 100 KiB |
|
@ -1,45 +0,0 @@
|
|||
Решение **с использованием цикла**:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sumTo(n) {
|
||||
let sum = 0;
|
||||
for let i = 1; i <= n; i++) {
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
alert( sumTo(100) );
|
||||
```
|
||||
|
||||
Решение через **рекурсию**:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sumTo(n) {
|
||||
if (n == 1) return 1;
|
||||
return n + sumTo(n - 1);
|
||||
}
|
||||
|
||||
alert( sumTo(100) );
|
||||
```
|
||||
|
||||
Решение **по формуле**: `sumTo(n) = n*(n+1)/2`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function sumTo(n) {
|
||||
return n * (n + 1) / 2;
|
||||
}
|
||||
|
||||
alert( sumTo(100) );
|
||||
```
|
||||
|
||||
P.S. Надо ли говорить, что решение по формуле работает быстрее всех? Это очевидно. Оно использует всего три операции для любого `n`, а цикл и рекурсия требуют как минимум `n` операций сложения.
|
||||
|
||||
Вариант с циклом -- второй по скорости. Он быстрее рекурсии, так как операций сложения столько же, но нет дополнительных вычислительных затрат на организацию вложенных вызовов.
|
||||
|
||||
Рекурсия в данном случае работает медленнее всех.
|
||||
|
||||
P.P.S. Существует ограничение глубины вложенных вызовов, поэтому рекурсивный вызов `sumTo(100000)` выдаст ошибку.
|
|
@ -1,34 +0,0 @@
|
|||
# Вычислить сумму чисел до данного
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите функцию `sumTo(n)`, которая для данного `n` вычисляет сумму чисел от 1 до `n`, например:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
sumTo(1) = 1
|
||||
sumTo(2) = 2 + 1 = 3
|
||||
sumTo(3) = 3 + 2 + 1 = 6
|
||||
sumTo(4) = 4 + 3 + 2 + 1 = 10
|
||||
...
|
||||
sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050
|
||||
```
|
||||
|
||||
Сделайте три варианта решения:
|
||||
<ol>
|
||||
<li>С использованием цикла.</li>
|
||||
<li>Через рекурсию, т.к. `sumTo(n) = n + sumTo(n-1)` для `n > 1`.</li>
|
||||
<li>С использованием формулы для суммы [арифметической прогрессии](http://ru.wikipedia.org/wiki/%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F).</li>
|
||||
</ol>
|
||||
|
||||
Пример работы вашей функции:
|
||||
|
||||
```js
|
||||
function sumTo(n) { /*... ваш код ... */ }
|
||||
|
||||
alert( sumTo(100) ); // 5050
|
||||
```
|
||||
|
||||
**Какой вариант решения самый быстрый? Самый медленный? Почему?**
|
||||
|
||||
**Можно ли при помощи рекурсии посчитать `sumTo(100000)`? Если нет, то почему?**
|
|
@ -1,25 +0,0 @@
|
|||
По свойствам факториала, как описано в условии, `n!` можно записать как `n * (n-1)!`.
|
||||
|
||||
То есть, результат функции для `n` можно получить как `n`, умноженное на результат функции для `n-1`, и так далее до `1!`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function factorial(n) {
|
||||
return (n != 1) ? n * factorial(n - 1) : 1;
|
||||
}
|
||||
|
||||
alert( factorial(5) ); // 120
|
||||
```
|
||||
|
||||
Базисом рекурсии является значение `1`. А можно было бы сделать базисом и `0`. Тогда код станет чуть короче:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function factorial(n) {
|
||||
return n ? n * factorial(n - 1) : 1;
|
||||
}
|
||||
|
||||
alert( factorial(5) ); // 120
|
||||
```
|
||||
|
||||
В этом случае вызов `factorial(1)` сведётся к `1*factorial(0)`, будет дополнительный шаг рекурсии.
|
|
@ -1,29 +0,0 @@
|
|||
# Вычислить факториал
|
||||
|
||||
[importance 4]
|
||||
|
||||
*Факториа́л числа* -- это число, умноженное на "себя минус один", затем на "себя минус два" и так далее, до единицы. Обозначается `n!`
|
||||
|
||||
Определение факториала можно записать как:
|
||||
|
||||
```js
|
||||
n! = n * (n - 1) * (n - 2) * ...*1
|
||||
```
|
||||
|
||||
Примеры значений для разных `n`:
|
||||
|
||||
```js
|
||||
1! = 1
|
||||
2! = 2 * 1 = 2
|
||||
3! = 3 * 2 * 1 = 6
|
||||
4! = 4 * 3 * 2 * 1 = 24
|
||||
5! = 5 * 4 * 3 * 2 * 1 = 120
|
||||
```
|
||||
|
||||
Задача -- написать функцию `factorial(n)`, которая возвращает факториал числа `n!`, используя рекурсивный вызов.
|
||||
|
||||
```js
|
||||
alert( factorial(5) ); // 120
|
||||
```
|
||||
|
||||
Подсказка: обратите внимание, что `n!` можно записать как `n * (n-1)!`. Например: `3! = 3*2! = 3*2*1! = 6`
|
|
@ -1,99 +0,0 @@
|
|||
# Вычисление рекурсией (медленное)
|
||||
|
||||
Решение по формуле, используя рекурсию:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function fib(n) {
|
||||
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
alert( fib(3) ); // 2
|
||||
alert( fib(7) ); // 13
|
||||
// fib(77); // не запускаем, подвесит браузер
|
||||
```
|
||||
|
||||
При больших значениях `n` оно будет работать очень медленно. Например, `fib(77)` уже будет вычисляться очень долго.
|
||||
|
||||
Это потому, что функция порождает обширное дерево вложенных вызовов. При этом ряд значений вычисляется много раз. Например, посмотрим на отрывок вычислений:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
...
|
||||
fib(5) = fib(4) + fib(3)
|
||||
fib(4) = fib(3) + fib(2)
|
||||
...
|
||||
```
|
||||
|
||||
Здесь видно, что значение `fib(3)` нужно одновременно и для `fib(5)` и для `fib(4)`. В коде оно будет вычислено два раза, совершенно независимо.
|
||||
|
||||
Можно это оптимизировать, запоминая уже вычисленные значения, получится гораздо быстрее. Альтернативный вариант -- вообще отказаться от рекурсии, а вместо этого в цикле начать с первых значений `1`, `2`, затем из них получить `fib(3)`, далее `fib(4)`, затем `fib(5)` и так далее, до нужного значения.
|
||||
|
||||
Это решение будет наиболее эффективным. Попробуйте его написать.
|
||||
|
||||
# Алгоритм вычисления в цикле
|
||||
|
||||
Будем идти по формуле слева-направо:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
let a = 1, b = 1; // начальные значения
|
||||
let c = a + b; // 2
|
||||
|
||||
/* переменные на начальном шаге:
|
||||
a b c
|
||||
1, 1, 2
|
||||
*/
|
||||
```
|
||||
|
||||
Теперь следующий шаг, присвоим `a` и `b` текущие 2 числа и получим новое следующее в `c`:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
a = b, b = c;
|
||||
c = a + b;
|
||||
|
||||
/* стало так (ещё число):
|
||||
a b c
|
||||
1, 1, 2, 3
|
||||
*/
|
||||
```
|
||||
|
||||
Следующий шаг даст нам ещё одно число последовательности:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
a = b, b = c;
|
||||
c = a + b;
|
||||
|
||||
/* стало так (ещё число):
|
||||
a b c
|
||||
1, 1, 2, 3, 5
|
||||
*/
|
||||
```
|
||||
|
||||
Повторять в цикле до тех пор, пока не получим нужное значение. Это гораздо быстрее, чем рекурсия, хотя бы потому что ни одно из чисел не вычисляется дважды.
|
||||
|
||||
P.S. Этот подход к вычислению называется [динамическое программирование снизу-вверх](http://ru.wikipedia.org/wiki/%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
|
||||
|
||||
# Код для вычисления в цикле
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function fib(n) {
|
||||
let a = 1,
|
||||
b = 1;
|
||||
for let i = 3; i <= n; i++) {
|
||||
let c = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
alert( fib(3) ); // 2
|
||||
alert( fib(7) ); // 13
|
||||
alert( fib(77) ); // 5527939700884757
|
||||
```
|
||||
|
||||
Цикл здесь начинается с `i=3`, так как первое и второе числа Фибоначчи заранее записаны в переменные `a=1`, `b=1`.
|
|
@ -1,24 +0,0 @@
|
|||
# Числа Фибоначчи
|
||||
|
||||
[importance 5]
|
||||
|
||||
Последовательность [чисел Фибоначчи](http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8) имеет формулу <code>F<sub>n</sub> = F<sub>n-1</sub> + F<sub>n-2</sub></code>. То есть, следующее число получается как сумма двух предыдущих.
|
||||
|
||||
Первые два числа равны `1`, затем `2(1+1)`, затем `3(1+2)`, `5(2+3)` и так далее: `1, 1, 2, 3, 5, 8, 13, 21...`.
|
||||
|
||||
Числа Фибоначчи тесно связаны с [золотым сечением](http://ru.wikipedia.org/wiki/%D0%97%D0%BE%D0%BB%D0%BE%D1%82%D0%BE%D0%B5_%D1%81%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5) и множеством природных явлений вокруг нас.
|
||||
|
||||
Напишите функцию `fib(n)`, которая возвращает `n-е` число Фибоначчи. Пример работы:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
function fib(n) { /* ваш код */ }
|
||||
|
||||
alert( fib(3) ); // 2
|
||||
alert( fib(7) ); // 13
|
||||
alert( fib(77)); // 5527939700884757
|
||||
```
|
||||
|
||||
**Все запуски функций из примера выше должны срабатывать быстро.**
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
**Первый код выведет `function ...`, второй -- ошибку во всех браузерах, кроме IE8-.**
|
||||
|
||||
```js
|
||||
//+ run untrusted no-beautify
|
||||
// обычное объявление функции (Function Declaration)
|
||||
function g() { return 1; };
|
||||
|
||||
alert(g); // функция
|
||||
```
|
||||
|
||||
Во втором коде скобки есть, значит функция внутри является не `Function Declaration`, а частью выражения, то есть `Named Function Expression`. Его имя видно только внутри, снаружи переменная `g` не определена.
|
||||
|
||||
```js
|
||||
//+ run untrusted no-beautify
|
||||
// Named Function Expression!
|
||||
(function g() { return 1; });
|
||||
|
||||
alert(g); // Ошибка!
|
||||
```
|
||||
|
||||
Все браузеры, кроме IE8-, поддерживают это ограничение видимости и выведут ошибку, `"undefined variable"`.
|
|
@ -1,22 +0,0 @@
|
|||
# Проверка на NFE
|
||||
|
||||
[importance 5]
|
||||
|
||||
Каков будет результат выполнения кода?
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
function g() { return 1; }
|
||||
|
||||
alert(g);
|
||||
```
|
||||
|
||||
А такого? Будет ли разница, если да -- почему?
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
(function g() { return 1; });
|
||||
|
||||
alert(g);
|
||||
```
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
# Именованные функциональные выражения
|
||||
|
||||
Специально для работы с рекурсией в JavaScript существует особое расширение функциональных выражений, которое называется "Named Function Expression" (сокращённо NFE) или, по-русски, *"именованное функциональное выражение"*.
|
||||
|
||||
[cut]
|
||||
|
||||
|
||||
## Named Function Expression [#functions-nfe]
|
||||
|
||||
Обычное функциональное выражение:
|
||||
```js
|
||||
let f = function(...) { /* тело функции */ };
|
||||
```
|
||||
|
||||
Именованное с именем `sayHi`:
|
||||
|
||||
```js
|
||||
//+ no-beautify
|
||||
let f = function *!*sayHi*/!*(...) { /* тело функции */ };
|
||||
```
|
||||
|
||||
Что же это за имя, которое идёт в дополнение к `f`, и зачем оно?
|
||||
|
||||
Имя функционального выражения (`sayHi`) имеет особый смысл. Оно доступно только изнутри самой функции.
|
||||
|
||||
Это ограничение видимости входит в стандарт JavaScript и поддерживается всеми браузерами, кроме IE8-.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let f = function sayHi(name) {
|
||||
alert( sayHi ); // изнутри функции - видно (выведет код функции)
|
||||
};
|
||||
|
||||
alert( sayHi ); // снаружи - не видно (ошибка: undefined variable 'sayHi')
|
||||
```
|
||||
|
||||
Кроме того, имя NFE нельзя перезаписать:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let test = function sayHi(name) {
|
||||
*!*
|
||||
sayHi = "тест"; // попытка перезаписи
|
||||
*/!*
|
||||
alert( sayHi ); // function... (перезапись не удалась)
|
||||
};
|
||||
|
||||
test();
|
||||
```
|
||||
|
||||
В режиме `use strict` код выше выдал бы ошибку.
|
||||
|
||||
Как правило, имя NFE используется для единственной цели -- позволить изнутри функции вызвать саму себя.
|
||||
|
||||
## Пример использования
|
||||
|
||||
NFE используется в первую очередь в тех ситуациях, когда функцию нужно передавать в другое место кода или перемещать из одной переменной в другую.
|
||||
|
||||
**Внутреннее имя позволяет функции надёжно обращаться к самой себе, где бы она ни находилась.**
|
||||
|
||||
Вспомним, к примеру, функцию-факториал из задачи [](/task/factorial):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function f(n) {
|
||||
return n ? n * f(n - 1) : 1;
|
||||
};
|
||||
|
||||
alert( f(5) ); // 120
|
||||
```
|
||||
|
||||
Попробуем перенести её в другую переменную `g`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function f(n) {
|
||||
return n ? n * f(n - 1) : 1;
|
||||
};
|
||||
|
||||
*!*
|
||||
let g = f;
|
||||
f = null;
|
||||
*/!*
|
||||
|
||||
alert( g(5) ); // запуск функции с новым именем - ошибка при выполнении!
|
||||
```
|
||||
|
||||
Ошибка возникла потому что функция из своего кода обращается к своему старому имени `f`. А этой функции уже нет, `f = null`.
|
||||
|
||||
Для того, чтобы функция всегда надёжно работала, объявим её как Named Function Expression:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
let f = function *!*factorial*/!*(n) {
|
||||
return n ? n**!*factorial*/!*(n-1) : 1;
|
||||
};
|
||||
|
||||
let g = f; // скопировали ссылку на функцию-факториал в g
|
||||
f = null;
|
||||
|
||||
*!*
|
||||
alert( g(5) ); // 120, работает!
|
||||
*/!*
|
||||
```
|
||||
|
||||
[warn header="В браузере IE8- создаются две функции"]
|
||||
|
||||
Как мы говорили выше, в браузере IE до 9 версии имя NFE видно везде, что является ошибкой с точки зрения стандарта.
|
||||
|
||||
...Но на самом деле ситуация ещё забавнее. Старый IE создаёт в таких случаях целых две функции: одна записывается в переменную `f`, а вторая -- в переменную `factorial`.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
let f = function factorial(n) { /*...*/ };
|
||||
|
||||
// в IE8- false
|
||||
// в остальных браузерах ошибка, т.к. имя factorial не видно
|
||||
alert( f === factorial );
|
||||
```
|
||||
|
||||
Все остальные браузеры полностью поддерживают именованные функциональные выражения.
|
||||
[/warn]
|
||||
|
||||
|
||||
[smart header="Устаревшее специальное значение `arguments.callee`"]
|
||||
Если вы давно работаете с JavaScript, то, возможно, знаете, что раньше для этой цели также служило специальное значение `arguments.callee`.
|
||||
|
||||
Если это название вам ни о чём не говорит -- всё в порядке, читайте дальше, мы обязательно обсудим его [в отдельной главе](#arguments-callee).
|
||||
|
||||
Если же вы в курсе, то стоит иметь в виду, что оно официально исключено из современного стандарта. А NFE -- это наше настоящее.
|
||||
[/smart]
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
Если функция задана как Function Expression, ей можно дать имя.
|
||||
|
||||
Оно будет доступно только внутри функции (кроме IE8-).
|
||||
|
||||
Это имя предназначено для надёжного рекурсивного вызова функции, даже если она записана в другую переменную.
|
||||
|
||||
Обратим внимание, что с Function Declaration так поступить нельзя. Такое "специальное" внутреннее имя функции задаётся только в синтаксисе Function Expression.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Всё вместе: особенности JavaScript
|
||||
# Всё вместе: особенности JavaScript TODO
|
||||
|
||||
В этой главе приводятся основные особенности JavaScript, на уровне базовых конструкций, типов, синтаксиса.
|
||||
|
||||
|
|
|
@ -65,24 +65,35 @@ For a curious reader who might be interested in a concrete example, check this c
|
|||
[1, 2].forEach(alert)
|
||||
```
|
||||
|
||||
It shows `1` then `2`. What `[...].forEach` means -- does not matter here (we'll get it later). Now let's insert an `alert` without a semicolon before it:
|
||||
It shows `1` then `2`.
|
||||
|
||||
Please don't think about the meaning of `[...].forEach` just for now -- it does not matter here (we'll get it later).
|
||||
|
||||
Now let's prepend an `alert` *without a semicolon* before it:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
alert( "Without a semicolon we get an error here" ) // printed
|
||||
[1, 2].forEach(alert) // not printed
|
||||
alert( "..." ) // works
|
||||
[1, 2].forEach(alert) // doesn't work!
|
||||
```
|
||||
|
||||
Now only the phrase is shown, not the numbers. And we can see an error in the developer console.
|
||||
|
||||
That's exactly because JavaScript engine did not insert a semicolon before square brackets `[...]` and misunderstood the code.
|
||||
|
||||
Everything's fine if we add a semicolon:
|
||||
But everything's fine if we add a semicolon:
|
||||
```js
|
||||
//+ run
|
||||
alert( "With the semicolon everything works" ); // printed
|
||||
[1, 2].forEach(alert) // printed too
|
||||
```
|
||||
|
||||
The error in the former variant occurs because JavaScript engine does not autoinsert a semicolon before square brackets `[...]`, so it was actually treated as a one-line statement:
|
||||
|
||||
```js
|
||||
//+ run no-beautify
|
||||
// without semicolon after alert it becomes
|
||||
alert( "..." )[1, 2].forEach(alert) // doesn't work!
|
||||
```
|
||||
|
||||
[/smart]
|
||||
|
||||
As a conclusion, it's recommended to put semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community.
|
||||
|
@ -147,7 +158,7 @@ alert( 'World' );
|
|||
```
|
||||
[/warn]
|
||||
|
||||
Don't hesitate to comment. They more code is in the project -- the more helpful good comments are.
|
||||
Don't hesitate to comment.
|
||||
|
||||
Comments increase the overall code footprint, but that's not a problem at all, because there are many tools which minify the code before publishing to production server and remove comments in the process.
|
||||
|
||||
|
@ -159,7 +170,8 @@ There are various types of comments, answering different questions:
|
|||
<li>Which counter-intuitive or implicit connections it has with other parts of the script?</li>
|
||||
</ul>
|
||||
|
||||
All these comments are highly valuable.
|
||||
|
||||
Further in the tutorial we'll make more notes about how to write the code better, easier to read and maintain. We'll also talk more about comments.
|
||||
|
||||
[smart header="The good code is inherently readable and self-commenting"]
|
||||
Please note that the first type of comments ("what the code does") should be used to describe a "high-level" action, like the overall architecture, a function or a chunk of code. It's purpose is to give an overview, so a reader doesn't need to delve into the code and figure out.
|
||||
|
@ -167,5 +179,3 @@ Please note that the first type of comments ("what the code does") should be use
|
|||
Novice programmers sometimes tend to elaborate too much. Please don't. The good code is inherently readable. No need to describe what few lines do. Unless it's something hard to grasp, and *then* it's worth to consider rewriting the code at the first place rather than commenting it.
|
||||
[/smart]
|
||||
|
||||
Further in the tutorial we'll make more notes about how to write the code better, easier to read and maintain.
|
||||
|
||||
|
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
@ -1,4 +1,4 @@
|
|||
# Отладка в браузере Chrome
|
||||
# TODO: Отладка в браузере Chrome
|
||||
|
||||
Перед тем, как двигаться дальше, поговорим об отладке скриптов.
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
0
1-js/3-writing-js/1-debugging-chrome/chrome_sources@2x.png → 1-js/3-code-quality/1-debugging-chrome/chrome_sources@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
0
1-js/3-writing-js/1-debugging-chrome/console_error@2x.png → 1-js/3-code-quality/1-debugging-chrome/console_error@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
0
1-js/3-writing-js/1-debugging-chrome/debugging.view/pow.js → 1-js/3-code-quality/1-debugging-chrome/debugging.view/pow.js
Executable file → Normal file
0
1-js/3-writing-js/1-debugging-chrome/error.view/index.html → 1-js/3-code-quality/1-debugging-chrome/error.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/1-debugging-chrome/error.view/pow.js → 1-js/3-code-quality/1-debugging-chrome/error.view/pow.js
Executable file → Normal file
Before Width: | Height: | Size: 522 B After Width: | Height: | Size: 522 B |
0
1-js/3-writing-js/1-debugging-chrome/manage1@2x.png → 1-js/3-code-quality/1-debugging-chrome/manage1@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
0
1-js/3-writing-js/1-debugging-chrome/manage2@2x.png → 1-js/3-code-quality/1-debugging-chrome/manage2@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
0
1-js/3-writing-js/1-debugging-chrome/manage3@2x.png → 1-js/3-code-quality/1-debugging-chrome/manage3@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 210 B |
0
1-js/3-writing-js/1-debugging-chrome/manage4@2x.png → 1-js/3-code-quality/1-debugging-chrome/manage4@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 347 B |
0
1-js/3-writing-js/1-debugging-chrome/manage5@2x.png → 1-js/3-code-quality/1-debugging-chrome/manage5@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 520 B |
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
0
1-js/3-writing-js/1-debugging-chrome/manage6@2x.png → 1-js/3-code-quality/1-debugging-chrome/manage6@2x.png
Executable file → Normal file
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
0
1-js/3-writing-js/4-testing/beforeafter.view/index.html → 1-js/3-code-quality/4-testing/beforeafter.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/beforeafter.view/test.js → 1-js/3-code-quality/4-testing/beforeafter.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/index.html → 1-js/3-code-quality/4-testing/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-1.view/index.html → 1-js/3-code-quality/4-testing/pow-1.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-1.view/test.js → 1-js/3-code-quality/4-testing/pow-1.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-2.view/index.html → 1-js/3-code-quality/4-testing/pow-2.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-2.view/test.js → 1-js/3-code-quality/4-testing/pow-2.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-3.view/index.html → 1-js/3-code-quality/4-testing/pow-3.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-3.view/test.js → 1-js/3-code-quality/4-testing/pow-3.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-4.view/index.html → 1-js/3-code-quality/4-testing/pow-4.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-4.view/test.js → 1-js/3-code-quality/4-testing/pow-4.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-full.view/index.html → 1-js/3-code-quality/4-testing/pow-full.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-full.view/test.js → 1-js/3-code-quality/4-testing/pow-full.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-min.view/index.html → 1-js/3-code-quality/4-testing/pow-min.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-min.view/test.js → 1-js/3-code-quality/4-testing/pow-min.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-nan-assert.view/index.html → 1-js/3-code-quality/4-testing/pow-nan-assert.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-nan-assert.view/test.js → 1-js/3-code-quality/4-testing/pow-nan-assert.view/test.js
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-nan.view/index.html → 1-js/3-code-quality/4-testing/pow-nan.view/index.html
Executable file → Normal file
0
1-js/3-writing-js/4-testing/pow-nan.view/test.js → 1-js/3-code-quality/4-testing/pow-nan.view/test.js
Executable file → Normal file
Before Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 246 KiB |