This commit is contained in:
Ilya Kantor 2015-08-27 19:59:45 +03:00
parent 337a15594a
commit cd86e9993b
10 changed files with 303 additions and 345 deletions

View file

@ -36,6 +36,8 @@ It is recommended to use figure brackets every time with `if`, even if there's o
The `if (…)` operator evaluates the condition in brackets and converts it to boolean type.
Let's remember the rules.
In the logical context:
<ul>
<li>A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`,</li>

View file

@ -1,34 +0,0 @@
```js
//+ no-beautify
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9\n" + 5 = " -9\n5"
" -9\n" - 5 = -14
5 && 2 = 2
2 && 5 = 5
5 || 0 = 5
0 || 5 = 5
null + 1 = 1 // (3)
undefined + 1 = NaN // (4)
null == "\n0\n" = false // (5)
+null == +"\n0\n" = true // (6)
```
<ol>
<li>Оператор `"+"` в данном случае прибавляет `1` как строку, и затем `0`.</li>
<li>Оператор `"-"` работает только с числами, так что он сразу приводит `""` к `0`.</li>
<li>`null` при численном преобразовании становится `0`</li>
<li>`undefined` при численном преобразовании становится `NaN`</li>
<li>При сравнении `==` с `null` преобразования не происходит, есть жёсткое правило: `null == undefined` и только.</li>
<li>И левая и правая часть `==` преобразуются к числу `0`.</li>
</ol>

View file

@ -1,30 +0,0 @@
# Вопросник по преобразованиям, для примитивов
[importance 5]
Подумайте, какой результат будет у выражений ниже. Тут не только преобразования типов. Когда закончите -- сверьтесь с решением.
```js
//+ no-beautify
"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
4 + 5 + "px"
"$" + 4 + 5
"4" - 2
"4px" - 2
7 / 0
" -9\n" + 5
" -9\n" - 5
5 && 2
2 && 5
5 || 0
0 || 5
null + 1
undefined + 1
null == "\n0\n"
+null == +"\n0\n"
```

View file

@ -1,212 +0,0 @@
# Преобразование типов для примитивов
Система преобразования типов в JavaScript очень проста, но отличается от других языков. Поэтому она часто служит "камнем преткновения" для приходящих из других языков программистов.
[cut]
Всего есть три преобразования:
<ol>
<li>роковое преобразование.</li>
<li>Числовое преобразование.</li>
<li>Преобразование к логическому значению.</li>
</ol>
**Эта глава описывает преобразование только примитивных значений, объекты разбираются далее.**
## Строковое преобразование
Строковое преобразование происходит, когда требуется представление чего-либо в виде строки. Например, его производит функция `alert`.
```js
//+ run
var a = true;
alert( a ); // "true"
```
Можно также осуществить преобразование явным вызовом `String(val)`:
```js
//+ run
alert( String(null) === "null" ); // true
```
Как видно из примеров выше, преобразование происходит наиболее очевидным способом, "как есть": `false` становится `"false"`, `null` -- `"null"`, `undefined` -- `"undefined"` и т.п.
Также для явного преобразования применяется оператор `"+"`, у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например:
```js
//+ run
alert( true + "test" ); // "truetest"
alert( "123" + undefined ); // "123undefined"
```
## Численное преобразование
Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений `===`, `!==`).
Для преобразования к числу в явном виде можно вызвать `Number(val)`, либо, что короче, поставить перед выражением унарный плюс `"+"`:
```js
var a = +"123"; // 123
var a = Number("123"); // 123, тот же эффект
```
<table>
<thead>
<tr><th>Значение</th><th>Преобразуется в...</th></tr>
</thead>
<tbody>
<tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr>
<tr><td>`true / false`</td><td>`1 / 0`</td></tr>
<tr><td>Строка</td><td>Пробельные символы по краям обрезаются.<br>Далее, если остаётся пустая строка, то `0`, иначе из непустой строки "считывается" число, при ошибке результат `NaN`.</td></tr>
</tbody>
</table>
Например:
```js
//+ run
// после обрезания пробельных символов останется "123"
alert( +" \n 123 \n \n" ); // 123
```
Ещё примеры:
<ul>
<li>Логические значения:
```js
//+ run
alert( +true ); // 1
alert( +false ); // 0
```
</li>
<li>Сравнение разных типов -- значит численное преобразование:
```js
//+ run
alert( "\n0 " == 0 ); // true
```
При этом строка `"\n0"` преобразуется к числу, как указано выше: начальные и конечные пробелы обрезаются, получается строка `"0"`, которая равна `0`.</li>
</li>
<li>С логическими значениями:
```js
//+ run
alert( "\n" == false );
alert( "1" == true );
```
Здесь сравнение `"=="` снова приводит обе части к числу. В первой строке слева и справа получается `0`, во второй `1`.
</li>
</ul>
### Специальные значения
Посмотрим на поведение специальных значений более внимательно.
**Интуитивно, значения `null/undefined` ассоциируются с нулём, но при преобразованиях ведут себя иначе.**
Специальные значения преобразуются к числу так:
<table class="bordered">
<tr><th>Значение</th><th>Преобразуется в...</th></tr>
<tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr>
</table>
Это преобразование осуществляется при арифметических операциях и сравнениях `> >= < <=`, но не при проверке равенства `==`. Алгоритм проверки равенства для этих значений в спецификации прописан отдельно (пункт [11.9.3](http://es5.github.com/x11.html#x11.9.3)). В нём считается, что `null` и `undefined` равны `"=="` между собой, но эти значения не равны никакому другому значению.
Это ведёт к забавным последствиям.
Например, `null` не подчиняется законам математики -- он "больше либо равен нулю": `null>=0`, но не больше и не равен:
```js
//+ run
alert( null >= 0 ); // true, т.к. null преобразуется к 0
alert( null > 0 ); // false (не больше), т.к. null преобразуется к 0
alert( null == 0 ); // false (и не равен!), т.к. == рассматривает null особо.
```
Значение `undefined` вообще "несравнимо":
```js
//+ run
alert( undefined > 0 ); // false, т.к. undefined -> NaN
alert( undefined == 0 ); // false, т.к. это undefined (без преобразования)
alert( undefined < 0 ); // false, т.к. undefined -> NaN
```
**Для более очевидной работы кода и во избежание ошибок лучше не давать специальным значениям участвовать в сравнениях `> >= < <=`.**
Используйте в таких случаях переменные-числа или приводите к числу явно.
## Логическое преобразование
Преобразование к `true/false` происходит в логическом контексте, таком как `if(value)`, и при применении логических операторов.
Все значения, которые интуитивно "пусты", становятся `false`. Их несколько: `0`, пустая строка, `null`, `undefined` и `NaN`.
Остальное, в том числе и любые объекты -- `true`.
Полная таблица преобразований:
<table class="bordered">
<tr><th>Значение</th><th>Преобразуется в...</th></tr>
<tr><td>`undefined`, `null`</td><td>`false`</td></tr>
<tr><td>Числа</td><td>Все `true`, кроме `0`, `NaN` -- `false`.</td></tr>
<tr><td>Строки</td><td>Все `true`, кроме пустой строки `""` -- `false`</td></tr>
<tr><td>Объекты</td><td>Всегда `true`</td></tr>
</table>
**Для явного преобразования используется двойное логическое отрицание `!!value` или вызов `Boolean(value)`.**
[warn header="Обратите внимание: строка `\"0\"` становится `true`"]
В отличие от многих языков программирования (например PHP), `"0"` в JavaScript является `true`, как и строка из пробелов:
```js
//+ run
alert( !!"0" ); // true
alert( !!" " ); // любые непустые строки, даже из пробелов - true!
```
[/warn]
Логическое преобразование интересно тем, как оно сочетается с численным.
**Два значения могут быть равны, но одно из них в логическом контексте `true`, другое -- `false`**.
Например, равенство в следующем примере верно, так как происходит численное преобразование:
```js
//+ run
alert( 0 == "\n0\n" ); // true
```
...А в логическом контексте левая часть даст `false`, правая -- `true`:
```js
//+ run
if ("\n0\n") {
alert( "true, совсем не как 0!" );
}
```
С точки зрения преобразования типов в JavaScript это совершенно нормально. При равенстве -- численное преобразование, а в `if` -- логическое, только и всего.
## Итого
В JavaScript есть три преобразования:
<ol>
<li>Строковое: `String(value)` -- в строковом контексте или при сложении со строкой. Работает очевидным образом.</li>
<li>Численное: `Number(value)` -- в численном контексте, включая унарный плюс `+value`. Происходит при сравнении разных типов, кроме строгого равенства.</li>
<li>Логическое: `Boolean(value)` -- в логическом контексте, можно также сделать двойным НЕ: `!!value`.</li>
</ol>
Точные таблицы преобразований даны выше в этой главе.
Особым случаем является проверка равенства с `null` и `undefined`. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.

View file

@ -80,7 +80,7 @@ In JavaScript, there is only one type: `string`.
We'll cover strings more thoroughly in the chapter [](/string).
## Boolean (logical) type
## A boolean (logical)
The boolean type has only two values in it: `true` and `false`.
@ -94,7 +94,15 @@ var checked = true; // the form field is checked
checked = false; // the form field is not checked
```
We'll cover booleans while discussing logical operators.
Boolean values usually originate from the comparisons:
```js
//+ run
var isGreater = 4 > 1;
alert(isGreater); // true
```
We'll cover booleans more deeply while discussing logical operators.
## The "null" value
@ -140,72 +148,6 @@ alert( x ); // "undefined"
To write an "empty" or an "unknown" value into the variable, use `null`.
## Symbols and objects
The "symbol" type is used to create unique identifiers.
```js
//+ run
var s = Symbol();
```
Symbols are mainly used for objects and thus we'll cover
```js
//+ run
var s = Symbol("id");
x = undefined;
alert( x ); // "undefined"
```
## Object
The `object` type is special.
All other types are called "primitive", because their values can contain only a single thing (be it a string or a number or whatever).
In contrast, objects are used to store collections of various data and more complex entities.
An object is defined with the figure brackets `{…}`.
For instance, here we create a `user` object with the name and the age:
```js
var user = {
name: "John",
age: 30
};
```
We can access the data form the object via the dot notation:
```js
alert( user.name ); // John
alert( user.age ); // 30
```
We'll cover working with objects in the chapter [](/object).
## Symbol
The `symbol` type is used to create unique identifiers for objects.
```js
var id = Symbol("id");
```
There are other programming languages with a "symbol" type, namely Ruby.
Let's just say that JavaScript symbols are not the same.
We list symbols here for completeness, their in-depth study goes after covering objects.
## The typeof operator [#type-typeof]
The `typeof` operator returns the type of the argument.
@ -250,6 +192,177 @@ Please note the last two lines, because `typeof` behaves specially there.
<li>Functions are yet to be covered. As of now let's just note that functions is a kind of objects. But `typeof` treats them separately returning `"function"`. That's very convenient in practie.</li>
</ol>
## Type conversions
A variable in JavaScript can contain any data. The same variable can get a string and, a little bit later, be used to store a number:
```js
// perfectly fine
var message = "hello";
message = 123456;
```
But sometimes we need to convert a value of one type to another. That is mostly useful because each type has it's own features, so we are really going to benefit from storing a number as a number, not a string with it.
There are many type conversions in JavaScript, fully listed in [the specification](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-type-conversion).
Three conversions that are required most often are:
<ol>
<li>String conversion.</li>
<li>Numeric conversion.</li>
<li>Boolean conversion.</li>
</ol>
### String conversion
The string conversion happens when we need a string from a value.
For example, `alert` does it:
```js
//+ run
var a = true;
alert( a ); // "true"
```
We can also use a call `String(value)` function for that:
```js
//+ run
var a = true;
a = String(a); // now: a = "true"
alert(typeof a); // string
```
The string conversion is the most obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
### Numeric conversion
Numeric conversion happens in mathematical functions and expressions automatically.
For example, a mathematical operation like division '/' can be applied to non-numbers:
```js
//+ run
alert( "6" / "2" ); // 3, strings become numbers
```
Although if we want to ensure that the value is a number, we can use a `Number(value)` function to do it explicitly:
```js
//+ run
var n = Number("6");
alert(typeof n); // number
```
We can use that to ensure that a user-supplied value is a number.
If the string is not a number, the result is `NaN`.
For example:
```js
//+ run
var age = Number("a user-supplied string");
alert(age); // NaN, conversion failed
alert(age); // number, because NaN belongs to the "number" type
```
The rules of transformation:
<table>
<thead>
<tr><th>Value</th><th>Becomes...</th></tr>
</thead>
<tbody>
<tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr>
<tr><td>`true / false`</td><td>`1 / 0`</td></tr>
<tr><td>A string</td><td>Whitespaces from the start and the end are cut.<br>Afterwards, if we have an empty string, then `0`, otherwise -- "read" aиначе из непустой строки "считывается" число, при ошибке результат `NaN`.</td></tr>
</tbody>
</table>
Other examples:
```js
//+ run
alert( Number(" 123 ") ); // 123
alert( Number("123z") ); // NaN (not a number because of "z")
alert( Number(true) ); // 1
alert( Number(false) ); // 0
```
Please note that `null` and `undefined` are similar in many aspects, but here they are not. A `null` becomes a zero, but `undefined` becomes `NaN`.
## Boolean conversion
Boolean conversion is happens automatically in some operations, but also can be performed manually with the call of `Boolean(value)`.
All values that are intuitively "empty" become `false`. These are: `0`, an empty string, `null`, `undefined` and `NaN`.
Other values become `true`.
[warn header="Please note: a string `\"0\"` is `true`"]
Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `false`, no matter what is in it.
```js
//+ run
alert( Boolean("0") ); // true
alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
```
[/warn]
## Object and Symbol
The `object` type is special.
All other types are called "primitive", because their values can contain only a single thing (be it a string or a number or whatever).
In contrast, objects are used to store collections of various data and more complex entities.
### Object
An object is defined with the figure brackets `{…}`.
For instance, here we create a `user` object with the name and the age:
```js
var user = {
name: "John",
age: 30
};
```
We can access the data form the object via the dot notation:
```js
alert( user.name ); // John
alert( user.age ); // 30
```
We'll cover working with objects in the chapter [](/object).
### Symbol
The `symbol` type is used to create unique identifiers.
```js
var id = Symbol("id");
```
...And then we could use `id` as a special kind of identifier for object properties. We'll see more about object properties in the following chapters.
As of now, let's just say that JavaScript symbols is a separate primitive type. And they are different from symbols in Ruby language (just in case you are familiar with it, please don't get trapped by the same word).
We list symbols here for completeness, their in-depth study will follow after objects.
## Summary
<ul>
@ -257,4 +370,12 @@ Please note the last two lines, because `typeof` behaves specially there.
<li>Use `typeof x` to see which type is stored in `x`, but note that `typeof null` is mistakingly returned as undefined.</li>
</ul>
Now as we know which types exist, let's move on to operators and compute something using these types.
Type conversions usually happen automatically, but there are also functions for the manual conversion:
<ul>
<li>String</li>
<li>Number</li>
<li>Boolean</li>
</ul>
Now let's move on to operators and compute something using these types.

View file

@ -0,0 +1,26 @@
```js
//+ no-beautify
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9\n" + 5 = " -9\n5"
" -9\n" - 5 = -14
null + 1 = 1 // (3)
undefined + 1 = NaN // (4)
```
<ol>
<li>The plus `"+"` operator in this case first converts `1` to a string: `"" + 1 = "1"`, and then adds `0`.</li>
<li>The minus `"-"` operator only works with numbers, it converts an empty string `""` to zero immediately.</li>
<li>`null` becomes `0` after the numeric conversion.</li>
<li>`undefined` becomes `NaN` after the numeric conversion.</li>
</ol>

View file

@ -0,0 +1,26 @@
# Type conversions
[importance 5]
Let's recap type conversions in the context of operators.
What will be the result for expressions?
```js
//+ no-beautify
"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
4 + 5 + "px"
"$" + 4 + 5
"4" - 2
"4px" - 2
7 / 0
" -9\n" + 5
" -9\n" - 5
null + 1
undefined + 1
```

View file

@ -0,0 +1,20 @@
```js
//+ no-beautify
5 > 4 → true
"apple" > "pineapple" → false
"2" > "12" → true // (1)
undefined == null → true // (2)
undefined === null → false // (3)
null == "\n0\n" → false // (4)
```
Some of the reasons:
<ol>
<li>The string `"2"` is indeed greater than `"12"`, because strings are compared character-by-character. The first character of `"2"` is greater than the first character of `"12"` (that is `"1"`).</li>
<li>Values `null` and `undefined` equal each other only.</li>
<li>Strict equality is strict. Different types from both sides lead to false.</li>
<li>See (2) for the reason.</li>
</ol>

View file

@ -0,0 +1,17 @@
# Comparisons
[importance 5]
What will be the result for expressions?
```js
//+ no-beautify
5 > 4
"apple" > "pineapple"
"2" > "12"
undefined == null
undefined === null
null == "\n0\n"
null === +"\n0\n"
```

View file

@ -110,6 +110,28 @@ alert( false == 0 ); // true
Rules for numeric conversion are to be discussed in more details in the chapter [](/types-conversion).
### Equality of non-equal
It is quite possible for two *equal* values that one value is `true`, while the other one is `false`.
For example:
```js
//+ run
var a = 0;
alert( Boolean(a) ); // false
var b = "0";
alert( Boolean(b) ); // true
alert(a == b); // true!
```
From JavaScript standpoint that's quite normal. An equality converts using the numeric conversion, while `Boolean` uses logical rules.
## Strict equality
A regular equality `==` has a "problem": it cannot differ `0` from `false`: