Skip to content

Latest commit

 

History

History
535 lines (469 loc) · 28 KB

File metadata and controls

535 lines (469 loc) · 28 KB

Типы данных

Переменные

Переменная состоит из имени и выделенной области памяти, которая ему соответствует.

Для объявления или, другими словами, создания переменной используется ключевое слово var:

var message;
message = 'Hello'; // сохраним в переменной строку

Эти данные будут сохранены в соответствующей области памяти и в дальнейшем доступны при обращении по имени. Для краткости можно совместить объявление переменной и запись данных:

var message = 'Hello';

Можно даже объявить несколько переменных сразу:

var user = 'John',
    age = 25,
    message = 'Hello';

Значение в переменных можно изменять или копировать из других переменных

var hello = 'Hello world!';
var message = hello; // скопировали значение "Hello world!"

Константа — это переменная, которая никогда не меняется. Как правило, их называют большими буквами, через подчёркивание. Например:

var COLOR_ORANGE = "#FF7F00";

var color = COLOR_ORANGE;

Технически, константа является обычной переменной, то есть её можно изменить. Но мы договариваемся этого не делать.

Зачем нужны константы? Почему бы просто не писать var color = "#FF7F00"; Во-первых, константа COLOR_ORANGE — это понятное имя. По присвоению var color="#FF7F00"; непонятно, что цвет — оранжевый. Иными словами, константа COLOR_ORANGE является «понятным псевдонимом» для значения #FF7F00. Во-вторых, опечатка в строке, особенно такой сложной как #FF7F00, может быть не замечена, а в имени константы её допустить куда сложнее.

Типы данных

Встроенные типы данных:

  • null var age = null; В JavaScript null не является «ссылкой на несуществующий объект» или «нулевым указателем», как в некоторых других языках. Это просто специальное значение, которое имеет смысл «ничего» или «значение неизвестно».
  • undefined var x = undefined; Значение undefined, как и null, образует свой собственный тип, состоящий из одного этого значения. Оно имеет смысл «значение не присвоено». Если переменная объявлена, но в неё ничего не записано, то её значение как раз и есть undefined:
var x;
console.log( x ); // выведет "undefined"

В явном виде undefined обычно не присваивают, так как это противоречит его смыслу. Для записи в переменную «пустого» или «неизвестного» значения используется null.

  • boolean var checked = true;
  • number var n = 123;
  • string var str = "Мама мыла раму";
  • object var user = { name: "Вася" };

Оператор typeof

Оператор typeof возвращает тип аргумента.

У него есть два синтаксиса: со скобками и без:

Синтаксис оператора: typeof x. Синтаксис функции: typeof(x). Работают они одинаково, но первый синтаксис короче.

typeof undefined // "undefined"

typeof 0 // "number"

typeof true // "boolean"

typeof "foo" // "string"

typeof {} // "object"

typeof null // "object"  (1)
//Результат typeof null == "object" — это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом деле null — это не объект, а отдельный тип //данных.

typeof function(){} // "function"

typeof typeof 2 //"string"

Основные операторы

Для работы с переменными, со значениями, JavaScript поддерживает все стандартные операторы, большинство которых есть и в других языках программирования. Несколько операторов — это обычные сложение +, умножение *, вычитание и так далее.

Операнд — то, к чему применяется оператор. Например: 5 * 2 — оператор умножения с левым и правым операндами. Другое название: «аргумент оператора». Унарным называется оператор, который применяется к одному выражению. Например, оператор унарный минус "-" меняет знак числа на противоположный:

var x = 1;
x = -x;

Бинарным называется оператор, который применяется к двум операндам. Тот же минус существует и в бинарной форме:

var x = 1, y = 3;
console.log( y - x ); // 2, бинарный минус
var a = "моя" + "строка";
a; // "моястрока"

Важно помнить, что в операции сложения, если хотя бы один из операндов является строкой, то второй будет также преобразован к строке! Причем не важно, справа или слева находится операнд-строка. Например:

console.log( '1' + 2 ); // "12"
console.log( 2 + '1' ); // "21"

В других арифметических операциях (вычитание, умножение, деление), операнды-строки напротив будут преобразованы к числам:

console.log( 2 - '1' ); // 1
console.log( 6 / '2' ); // 3
console.log( '4' - '1' ); // 3

Унарный, то есть применённый к одному значению, плюс ничего не делает с числами:

console.log( +1 ); // 1
console.log( +(1 - 2) ); // -1

С точки зрения математики такое изобилие плюсов может показаться странным. С точки зрения программирования — никаких разночтений: сначала выполнятся унарные плюсы, приведут строки к числам, а затем — бинарный '+' их сложит.

var apples = "2";
var oranges = "3";
//Так писать не рекомендуется, (легко ошибиться)поскольку если забыть пробел, то можно вместо приведения к типу получить инкремент
console.log( +apples + +oranges ); // 5, число, оба операнда предварительно преобразованы в числа

Таблица приоритетов

Присваивание

Обратим внимание, в таблице приоритетов также есть оператор присваивания =.

У него — один из самых низких приоритетов: 3.

Именно поэтому, когда переменную чему-либо присваивают, например, x = 2 * 2 + 1 сначала выполнится арифметика, а уже затем — произойдёт присвоение =.

var x = 2 * 2 + 1; // 5
var a, b, c;
//Такое присваивание работает справа-налево, то есть сначала
//вычислятся самое правое выражение 2+2, присвоится в c,
//затем выполнится b = c и, наконец, a = b.
a = b = c = 2 + 2;
console.log( a, b, c ); // 4 4 4

Все операторы возвращают значение. Вызов x = выражение не является исключением. Он записывает выражение в x, а затем возвращает его. Благодаря этому присваивание можно использовать как часть более сложного выражения:

var a = 1;
var b = 2;
var c = 3 - (a = b + 1);

console.log( a ); // 3
console.log( c ); // 0

Взятие остатка %

Оператор взятия остатка % интересен тем, что, несмотря на обозначение, никакого отношения к процентам не имеет. Его результат a % b — это остаток от деления a на b.

console.log( 5 % 2 ); // 1, остаток от деления 5 на 2

Инкремент/декремент: ++, --

Инкремент ++ увеличивает на 1:

var i = 2;
i++;      // более короткая запись для i = i + 1.
console.log(i); // 3

Декремент -- уменьшает на 1:

var i = 2;
i--;      // более короткая запись для i = i - 1.
console.log(i); // 1

Вызывать эти операторы можно не только после, но и перед переменной: i++ (называется «постфиксная форма») или ++i («префиксная форма»).

Обе эти формы записи делают одно и то же: увеличивают на 1.

Тем не менее, между ними существует разница. Она видна только в том случае, когда мы хотим не только увеличить/уменьшить переменную, но и использовать результат в том же выражении.

var i = 1;
var a = ++i; // (*)
console.log(a); // 2

Постфиксная форма i++ отличается от префиксной ++i тем, что возвращает старое значение, бывшее до увеличения.

var i = 1;
var a = i++; // (*)
console.log(a); // 1

Инкремент/декремент можно использовать в любых выражениях

var i = 1;
console.log( 2 * ++i ); // 4

Побитовые операторы

Побитовые операторы

Сокращённая арифметика с присваиванием

var n = 2;
n = n + 5;// -> n += 5;
n = n * 2;// -> n *= 2

Так можно сделать для операторов +,-,* ,/ и бинарных <<,>>,>>>,&,|,^. Вызов с присваиванием имеет в точности такой же приоритет, как обычное присваивание, то есть выполнится после большинства других операций.

Оператор запятая

Один из самых необычных операторов — запятая ','. Его можно вызвать явным образом, например:

var a = (5, 6);

Запятая позволяет перечислять выражения, разделяя их запятой ','. Каждое из них — вычисляется и отбрасывается, за исключением последнего, которое возвращается. //Запятая// — единственный оператор, приоритет которого ниже присваивания. В выражении a = (5,6) для явного задания приоритета использованы скобки, иначе оператор '=' выполнился бы до запятой ',', получилось бы (a=5), 6. Зачем же нужен такой странный оператор, который отбрасывает значения всех перечисленных выражений, кроме последнего? Обычно он используется в составе более сложных конструкций, чтобы сделать несколько действий в одной строке. Например:

// три операции в одной строке
for (a = 1, b = 3, c = a*b; a < 10; a++) {
 ...
}

Операторы сравнения

Многие операторы сравнения знакомы нам из математики:

  • Больше/меньше: a > b, a < b.
  • Больше/меньше или равно: a >= b, a <= b.
  • Равно a == b. Для сравнения используется два символа равенства '='. Один символ a = b означал бы присваивание.
  • «Не равно». В математике он пишется как ≠, в JavaScript — знак равенства с восклицательным знаком перед ним !=.
  • Оператор возвращает истинну в том случае, если операнды строго равны. В отличие от оператора равенства, данный оператор не приводит операнды к одному типу.
var a = 2;
var b = 3;
a == b; //false
a === b; //false
a > b;  //false
a >= b;  //false
a < b; //true
a <= b; //true
a != b;  //true
a !== b;  //true Оператор строгого неравенства возвращает истину в том случае, если операнды не равны, или их типы отличаются друг от друга.

Аналогом «алфавита» во внутреннем представлении строк служит кодировка, у каждого символа — свой номер (код). JavaScript использует кодировку Unicode. При этом сравниваются численные коды символов. В частности, код у символа Б больше, чем у А, поэтому и результат сравнения такой.

В кодировке Unicode обычно код у строчной буквы больше, чем у прописной.

Поэтому регистр имеет значение:

'Вася' > 'Ваня'; //true т.к. 'c' > 'н'
var a = 3;
var b = 3;
var c = a === b ? a + b : a - b; // 6
var a = 3;
var b = 3;
var c;
if (a === b) {
  c = a + b;
} else {
  c = a - b;
}
c; // 6

"==" vs "==="

==

===

Number

В js есть только один тип чисел - number. JS так же руководствуется стандартом IEEE 754, называющейся «плавающей точкой» , использует двойную точность. Кому интересно могут прочитать про этот стандарт, но в этом нет строгой необходимости.

var n = 123;

n = 12.345;

Если целая часть равна 0, то необязательно его писать

var a = 0.42;
var b = .42;

То же самое и для десятичной части

var a = 42.0;
var b = 42.;

Но это плохая идея - потому как можете запутать других людей Обычно по-умолчанию, большенство чисел с десятичными значениями удаляют последние нули

var a = 42.300;
var b = 42.0;
console.log(a); // 42.3
console.log(b); // 42

Очень большие/маленькие числа будут выводиться через экспоненту

var a = 5E10;
a; // 50000000000
a.toExponential(); // "5e+10"
var b = a * a;
b; // 2.5e+21
var c = 1 / a;
c; // 2e-11
var d = 1E3; // 1*10^3

По умолчанию JavaScript переводит в экспоненциальную запись любые значения с плавающей точкой, содержащие как минимум шесть нулей после точки:

0.0000005; //5e-7
0.000005; //0.000005

Нельзя так просто взять и вызвать методы у числа

// Не валидный синтаксис:
42.toFixed( 3 ); // SyntaxError
// Все следующие примеры валидны
(42).toFixed( 3 ); // "42.000"
0.42.toFixed( 3 ); // "0.420"
42..toFixed( 3 ); // "42.000"

Числа можно так же записывать в других системах исчесления:

0xf3; // Шестнадцатиричная: 243
0Xf3; // то же самое
010; // восьмиричная: 8
08; // десятеричная: 8

Восьмиричная система определяется тем что первым числом идет 0 затем числ от 0 до 7 иначе будет считаться что система десятеричная

в ES6 + strict 0363 больше не разрешается, есть новая форма записи. Всегда используйте нижний регистр, что бы не запутать других людей

0xf3; // Шестнадцатиричная: 243
0Xf3; // то же самое
0o10; // восьмиричная: 8
0O10; // восьмиричная: 8
0b10; // двоичная: 2
0B10; // двоичная: 2

Ошибки округления вещественных чисел

0.1 + 0.2 === 0.3; // false
0.1 + 0.2; // 0.30000000000000004

Всё дело в том, что в стандарте IEEE 754 на число выделяется ровно 8 байт(=64 бита), не больше и не меньше.

Число 0.1 (одна десятая) записывается просто в десятичном формате, а в двоичной системе счисления это бесконечная дробь (перевод десятичной дроби в двоичную систему). Также бесконечной дробью является 0.2 (=2/10). Двоичное значение бесконечных дробей хранится только до определенного знака, поэтому возникает неточность. Её даже можно увидеть:

console.log( 0.1.toFixed(20) ); // 0.10000000000000000555

Есть несколько способов этого избежать:

  1. Привести к целым числам провести операции и вернуться обратно

    console.log( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
  2. Использование эпсилон окрестности В ES6 есть константа

    Number.EPSILON; //2.220446049250313e-16
    //Можно заполифилить
    if (!Number.EPSILON) { Number.EPSILON = Math.pow(2,-52); }
    //Функци сравнения с учетом погрешности
    function numbersCloseEnoughToEqual(n1,n2) { return Math.abs( n1 - n2 ) < Number.EPSILON; }
    var a = 0.1 + 0.2;
    var b = 0.3;
    numbersCloseEnoughToEqual( a, b ); // true
    numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false

Интересный пример

`9999999999999999; //10000000000000000`

Причина та же — потеря точности.

Из 64 бит, отведённых на число, сами цифры числа занимают до 52 бит, остальные 11 бит хранят позицию десятичной точки и один бит — знак. Так что если 52 бит не хватает на цифры, то при записи пропадут младшие разряды.

Интерпретатор не выдаст ошибку, но в результате получится «не совсем то число», что мы и видим в примере выше. Как говорится: «как смог, так записал». Math

Специальные значения

NaN
Если математическая операция не может быть совершена, то возвращается специальное значение NaN (Not-A-Number).

Например, деление 0/0 в математическом смысле неопределено, поэтому его результат NaN:

0/0; // NaN
2/"foo"; // NaN

Значение NaN — единственное, в своем роде, которое не равно ничему, включая себя.

NaN === NaN; // false
NaN == NaN; //false

Есть специальный метод который поможет проверить

var n = 0/0;
isNaN(n); // true
isNaN('12'); // false, строка преобразована к числу
isNaN({}); // true и это правда
//Можно заполифилить
if (!Number.isNaN) { Number.isNaN = function(n) { return ( typeof n === "number" && window.isNaN( n ) ); }; }
//Или еще короче
if (!Number.isNaN) { Number.isNaN = function(n) { return n !== n; }; }

infinity
Что должно происходить при попытке деления на ноль?

Как правило, ошибка в программе… Во всяком случае, в большинстве языков программирования это именно так.

Но создатель JavaScript решил пойти математически правильным путем. Ведь чем меньше делитель, тем больше результат. При делении на очень-очень маленькое число должно получиться очень большое. В математическом анализе это описывается через пределы, и если подразумевать предел, то в качестве результата деления на 0 мы получаем «бесконечность», которая обозначается символом ∞ или, в JavaScript: "Infinity".

1/0; // Infinity
-1/0; // -Infinity
var a = Number.MAX_VALUE; // 1.7976931348623157e+308
a + a; // Infinity
a + Math.pow( 2, 970 ); // Infinity
a + Math.pow( 2, 969 ); // 1.7976931348623157e+308
Infinity / Infinity; // NaN

А что будет 0/ -infinity ?

Нули
Javascript 2 нуля, как бы это странно не звучало

var a = 0 / -3; // -0
var b = 0 * -3; // -0
a.toString(); // "0"
+"-0"; // -0
Number( "-0" ); // -0

var a = 0;
var b = 0 / -3;
a == b; // true
-0 == 0; // true
a === b; // true
-0 === 0; // true
 0 > -0; // false
a > b; // false

//Проверка на отрицательный 0
function isNegZero(n) { n = Number( n ); return (n === 0) && (1 / n === -Infinity); }

А нужно это в приложениях, где например в качестве направления берется знак числа, и если подставить 0, то знак не поменяется на противоположный

Array

В отличии от строго типизированных языков, массивы в js могут содержать значения разных типов: объекты, строки, числа и даже другие массивы(многомерные массивы)

var a = [ 1, "2", [3] ];
a.length; // 3
a[0] === 1; // true
a[2][0] === 3; // true

Необязательно указывать размер массива, нужно только лишь объявить его

var a = [ ];
a.length; // 0
a[0] = 1;
a[1] = "2";
a[2] = [ 3 ];
a.length; // 3

Однако, если мы в качестве ключа будете использовать строковое значение, которое может быть приведено к 10-значному числу, то обработчик подумает, что используется числовое значение в качестве ключа.

var a = [ ];
a.length; // 0
a['13'] = 1;
a.length; // 14

Метод Pop

var fruits = ["Яблоко", "Апельсин", "Груша"];
fruits.pop(); // удалили "Груша"
fruits; // Яблоко, Апельсин

Метод Push

var fruits = ["Яблоко", "Апельсин"];
fruits.push("Груша"); // удалили "Груша"
fruits; // Яблоко, Апельсин, Груша

Длина length — не количество элементов массива, а последний индекс + 1.

var arr = [1, 2, 3, 4, 5];

arr.length = 2; // укоротить до 2 элементов
arr ; // [1, 2]
arr.length = 5; // вернуть length обратно, как было
arr[3]; // undefined: значения не вернулись

Object как ассоциативные массивы

Массив содержит численную индексацию, но так же могут иметь строковые ключи, но тогда длинна массива не будет учитывать элементы со строковыми значениями.

var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; // 1
a["foobar"]; // 2
a.foobar; // 2

Другой способ создания массива

var arr = new Array(2, 3);
console.log( arr[0] ); // 2, создан массив [2, 3], всё ок

arr = new Array(2); // создаст массив [2] ?
console.log( arr[0] ); // undefined! у нас массив без элементов, длины 2

Источники