- Специальные атрибуты
async
иdefer
используются для того, чтобы пока грузится внешний скрипт – браузер показал остальную (следующую за ним) часть страницы. Без них этого не происходит. - Разница между
async
иdefer
: атрибутdefer
сохраняет относительную последовательность скриптов, аasync
– нет. Кроме того, defer всегда ждёт, пока весь HTML-документ будет готов, аasync
– нет.
- Директива выглядит как строка
"use strict";
или'use strict';
и ставится в начале скрипта.
Например:
"use strict";
// этот код будет работать по современному стандарту ES5
...
use strict
также можно указывать в начале функций, тогда строгий режим будет действовать только внутри функции.- В чем различие между строгим режимом и неограниченным режимом
- Число «number»
var n = 123;
n = 12.345;
Бесконечность Infinity получается при делении на ноль:
alert( 1 / 0 ); // Infinity
но,
alert( 0 / 0 ); // NaN
alert( null / 0 ); // NaN
alert( false / 0 ); // NaN
alert( 'text' / 0 ); // NaN
alert( [] / 0 ); // NaN
alert( underfined / 0 ); // NaN
Ошибка вычислений NaN будет результатом некорректной математической операции, например:
alert( "нечисло" * 2 ); // NaN, ошибка
- Строка «string»
- Булевый (логический) тип «boolean»
- Специальное значение «null»
В JavaScript null не является «ссылкой на несуществующий объект» или «нулевым указателем», как в некоторых других языках. Это просто специальное значение, которое имеет смысл «ничего» или «значение неизвестно».
- Специальное значение «undefined»
Если переменная объявлена, но в неё ничего не записано, то её значение как раз и есть undefined
- Объекты «object» Он используется для коллекций данных и для объявления более сложных сущностей. Объявляются объекты при помощи фигурных скобок {...}, например:
var user = { name: "Вася" };
- Оператор typeof
-
У него есть два синтаксиса: со скобками и без:
- Синтаксис оператора: typeof x.
- Синтаксис функции: typeof(x).
-
Результатом
typeof
является строка, содержащая тип:typeof undefined // "undefined" typeof 0 // "number" typeof Infinity // "number" typeof NaN // "number" typeof true // "boolean" typeof "foo" // "string" typeof {} // "object" typeof [] // "object" typeof null // "object" (1) typeof function(){} // "function" (2)
Последние две строки помечены, потому что typeof ведет себя в них по-особому.
-
Результат
typeof null == "object"
– это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом делеnull
– это не объект, а отдельный тип данных. -
Заметим, что функции не являются отдельным базовым типом в JavaScript, а подвидом объектов. Но typeof выделяет функции отдельно, возвращая для них
"function"
. На практике это весьма удобно, так как позволяет легко определить функцию.
-
-
- Еще..
"" == 0 // true
" " == 0 // true
но, "" == " " // false (каждый символ иммет вес)
и "" < " " // true (каждый символ иммет вес)
- Таблица приоритетов
- Инкремент/декремент можно применить только к переменной. Код
5++
даст ошибку.
-
Сравнение с null и undefined
Они ведут себя по-другому.
- Значения
null
иundefined
равны==
друг другу и не равны чему бы то ни было ещё. Это жёсткое правило буквально прописано в спецификации языка. - При преобразовании в число
null
становится0
, аundefined
становитсяNaN
.
Посмотрим забавные следствия.
- Значения
-
Некорректный результат сравнения null с 0
Сравним
null
с нулём:alert( null > 0 ); // false alert( null == 0 ); // false
Итак, мы получили, что null не больше и не равен нулю. А теперь…
alert(null >= 0); // true
Как такое возможно? Если нечто «больше или равно нулю», то резонно полагать, что оно либо больше, либо равно. Но здесь это не так.
Дело в том, что алгоритмы проверки равенства
==
и сравнения>= > < <=
работают по-разному.Сравнение честно приводит к числу, получается ноль. А при проверке равенства значения
null
иundefined
обрабатываются особым образом: они равны друг другу, но не равны чему-то ещё.В результате получается странная с точки зрения здравого смысла ситуация, которую мы видели в примере выше.
-
Несравнимый undefined
Значение
undefined
вообще нельзя сравнивать:alert( undefined > 0 ); // false (1) alert( undefined < 0 ); // false (2) alert( undefined == 0 ); // false (3)
- Сравнения
(1
) и(2)
даютfalse
потому, чтоundefined
при преобразовании к числу даётNaN
. А значениеNaN
по стандарту устроено так, что сравнения==
,<
,>
,<=
,>=
и даже===
с ним возвращаютfalse
. - Проверка равенства
(3)
даётfalse
, потому что в стандарте явно прописано, чтоundefined
равно лишьnull
или себе и ничему другому.
Желательно не использовать сравнения
>= > < <=
с ними, во избежание ошибок в коде. - Сравнения
-
Вспомогательные функции parseInt, toString
Для удобной работы с примерами в этой статье, если вы захотите протестировать что-то в консоли, пригодятся две функции.
parseInt("11000", 2)
– переводит строку с двоичной записью числа в число.n.toString(2)
– получает для числа n запись в 2-ной системе в виде строки.
Например:
var access = parseInt("11000", 2); // получаем число из строки alert( access ); // 24, число с таким 2-ным представлением var access2 = access.toString(2); // обратно двоичную строку из числа alert( access2 ); // 11000
- А таким способом можно привести число с любой системи числения.
Например:
0b100; // 4, 2-ичная, начинаеться на 0b 0o100; // 64, , 8-ичная начинаеться на 0o 0x100; // 256, 16-ичная, начинаеться на 0x
-
Округление
Так как битовые операции отбрасывают десятичную часть, то их можно использовать для округления. Достаточно взять любую операцию, которая не меняет значение числа.
Например, двойное НЕ (~
):
alert( ~~12.345 ); // 12
Подойдёт и Исключающее ИЛИ (^
) с нулём:
alert( 12.345 ^ 0 ); // 12
Последнее даже более удобно, поскольку отлично читается:
alert(12.3 * 14.5 ^ 0); // (=178) "12.3 умножить на 14.5 и округлить"
У побитовых операторов достаточно низкий приоритет, он меньше чем у остальной арифметики:
alert( 1.1 + 1.2 ^ 0 ); // 2, сложение выполнится раньше округления
- Проверки на равенство
-1
if (~n) { n не -1 }
-
Преобразование к логическому типу Оператор
if (...)
вычисляет и преобразует выражение в скобках к логическому типу.В логическом контексте:
- Число
0
, пустая строка""
,null
иundefined
, а такжеNaN
являютсяfalse
, - Остальные значения –
true
.
- Число
-
|| (ИЛИ)
Оператор
||
вычисляет операнды слева направо до первого «истинного» и возвращает его, а если все ложные – то последнее значение.Иначе можно сказать, что "
||
запинается на правде". -
&& (И) Итак, оператор
&&
вычисляет операнды слева направо до первого «ложного» и возвращает его, а если все истинные – то последнее значение.Иначе можно сказать, что "
&&
запинается на лжи".- Приоритет у && больше, чем у ||
-
! (НЕ) Действия
!
:- Сначала приводит аргумент к логическому типу true/false.
- Затем возвращает противоположное значение.
В частности, двойное НЕ используют для преобразования значений к логическому типу:
alert( !!"строка" ); // true alert( !!null ); // false
-
Nb
alert(5) // underfined
-
Строковое преобразование
Можно осуществить преобразование явным вызовом
String(val)
:alert( String(null) === "null" ); // true
Также для явного преобразования применяется оператор "+", у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например:
alert( true + "test" ); // "truetest" alert( "123" + undefined ); // "123undefined"
-
Численное преобразование
Для преобразования к числу в явном виде можно вызвать
Number(val)
, либо, что короче, поставить перед выражением унарный плюс"+"
:var a = +"123"; // 123 var a = Number("123"); // 123, тот же эффект
-
Значение преобразуется в:
undefined
->NaN
null
->0
true / false
->1 / 0
Строка -> Пробельные символы по краям обрезаются. Далее, если остаётся пустая строка, то
0
, иначе из непустой строки "считывается" число, при ошибке результатNaN
.// после обрезания пробельных символов останется "123" alert( +" \n 123 \n \n" ); // 123
Ещё примеры:
- Логические значения:
alert( +true ); // 1 alert( +false ); // 0
- Сравнение разных типов – значит численное преобразование:
alert( "\n0 " == 0 ); // true
При этом строка "\n0" преобразуется к числу, как указано выше: начальные и конечные пробелы обрезаются, получается строка "0", которая равна 0.
- С логическими значениями:
alert( "\n" == false ); alert( "1" == true );
Здесь сравнение "==" снова приводит обе части к числу. В первой строке слева и справа получается 0, во второй 1.
-
-
Логическое преобразование
-
Преобразование к
true/false
происходит в логическом контексте, таком какif(value)
, и при применении логических операторов. -
Все значения, которые интуитивно «пусты», становятся
false
. Их несколько:0
,пустая строка
,null
,undefined
иNaN
.Остальное, в том числе и любые объекты –
true
. -
Для явного преобразования используется двойное логическое отрицание !!value или вызов Boolean(value).
-
Обратите внимание: строка
"0"
становитсяtrue
. В отличие от многих языков программирования (например PHP),"0"
в JavaScript являетсяtrue
, как и строка из пробелов:alert( !!"0" ); // true alert( !!" " ); // любые непустые строки, даже из пробелов - true!
-
Логическое преобразование интересно тем, как оно сочетается с численным.
Два значения могут быть равны, но одно из них в логическом контексте true, другое – false.
Например, равенство в следующем примере верно, так как происходит численное преобразование:
alert( 0 == "\n0\n" ); // true
… А в логическом контексте левая часть (0) даст false, правая ("\n0\n") – true, так как любая не пустая строка в логическом контексте равна true:
if ("\n0\n") { alert( "true, совсем не как 0!" ); }
С точки зрения преобразования типов в JavaScript это совершенно нормально. При сравнении с помощью
«==»
–численное преобразование
, а вif
–логическое
, только и всего.
-
-
Немного практики
"" + 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)
(1). Оператор "+" в данном случае прибавляет 1 как строку, и затем 0.
(2). Оператор "-" работает только с числами, так что он сразу приводит "" к 0.
(3). null при численном преобразовании становится 0
(4). undefined при численном преобразовании становится NaN
(5). При сравнении == с null преобразования не происходит, есть жёсткое правило: null == undefined и только.
(6). И левая и правая часть == преобразуются к числу 0.
-
Следующая итерация: continue
- Директива
continue/break
позволяет обойтись без скобок
if (i % 2 == 0) continue;
- Нельзя использовать
break/continue
справа от оператора„?“
Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе
'?'
. - Директива
-
Метки для break/continue
Бывает нужно выйти одновременно из нескольких уровней цикла.
Например, внутри цикла по
i
находится цикл поj
, и при выполнении некоторого условия мы бы хотели выйти из обоих циклов сразу:outer: for (var i = 0; i < 3; i++) { for (var j = 0; j < 3; j++) { var input = prompt('Значение в координатах '+i+','+j, ''); // если отмена ввода или пустая строка - // завершить оба цикла if (!input) break outer; // (*) } } alert('Готово!');
Вызов
break outer
ищет ближайший внешний цикл с такой меткой и переходит в его конец.В примере выше это означает, что будет разорван самый внешний цикл и управление перейдёт на
alert
.Директива
continue
также может быть использована с меткой, в этом случае управление перепрыгнет на следующую итерацию цикла с меткой.Заметим, что метки не позволяют прыгнуть в произвольное место кода, в JavaScript нет такой возможности.
-
Вывести простые числа
Натуральное число, большее 1, называется простым, если оно ни на что не делится, кроме себя и
1
.Другими словами,
n>1
– простое, если при делении на любое число от2
доn-1
есть остаток.Создайте код, который выводит все простые числа из интервала от
2
до10
. Результат должен быть:2,3,5,7
.P.S. Код также должен легко модифицироваться для любых других интервалов.
Решение с использованием метки:
nextPrime: for (var i = 2; i <= 10; i++) { for (var j = 2; j < i; j++) { if (i % j == 0) continue nextPrime; } alert( i ); // простое }
Решение без метки из записью в масив:
var simpleArr = []; for (var number = 2; number <= 10; number++) { let isSimply = true; for (var j = 2; j <= Math.floor(number / 2); j++) { if (number % j) continue; isSimply = false; break; } if (isSimply === true) { simpleArr.push(number); } } alert(simpleArr);
- Оператор switch предполагает строгое равенство ===
-
Локальные переменные
Блоки
if/else
,switch
,for
,while
,do..while
не влияют на область видимости переменных.При объявлении переменной в таких блоках, она всё равно будет видна во всей функции.
Например:
function count() { // переменные i,j не будут уничтожены по окончании цикла for (var i = 0; i < 3; i++) { var j = i * 2; } alert( i ); // i=3, последнее значение i, при нём цикл перестал работать alert( j ); // j=4, последнее значение j, которое вычислил цикл }
-
Внешние переменные
Переменные, объявленные на уровне всего скрипта, называют «глобальными переменными».
-
Аргументы по умолчанию
Если параметр не передан при вызове функции – он считается равным
undefined
.Для указания значения «по умолчанию», то есть, такого, которое используется, если аргумент не указан, используется два способа(
ES5
) и один способ(ES6
):-
ES5
Можно проверить, равен ли аргументundefined
, и если да – то записать в него значение по умолчанию. Этот способ продемонстрирован в примере выше.function showMessage(from, text) { if (text === undefined) { text = 'текст не передан'; } alert( from + ": " + text ); } showMessage("Маша", "Привет!"); // Маша: Привет! showMessage("Маша"); // Маша: текст не передан
-
ES5
Использовать оператор ||:function showMessage(from, text) { text = text || 'текст не передан'; ... }
Второй способ считает, что аргумент отсутствует, если передана пустая строка,
0
, или вообще любое значение, которое в логическом контексте являетсяfalse
. -
ES6
Присвоить значение параметру функцииfunction showMessage(from, text = 10) { ... }
-
-
Возврат значения
-
Значение функции без return и с пустым return
В случае, когда функция не вернула значение или return был без аргументов, считается что она вернула undefined:
function doNothing() { /* пусто */ } alert( doNothing() ); // undefined or function doNothing() { return; } alert( doNothing() === undefined ); // true
-
-
Выбор имени функции
-
Сверхкороткие имена функций
Имена функций, которые используются очень часто, иногда делают сверхкороткими.
Например, во фреймворке jQuery есть функция
$
, во фреймворке Prototype – функция$$
, а в библиотеке LoDash очень активно используется функция с названием из одного символа подчеркивания_
.
-
Функции в JavaScript являются значениями. Их можно присваивать, передавать, создавать в любом месте кода.
-
Сравнение Function Declaration с Function Expression
«Классическое» объявление функции вида
function имя(параметры) {...}
, называется в спецификации языка «Function Declaration».- Function Declaration – функция, объявленная в основном потоке кода.
- Function Expression – объявление функции в контексте какого-либо выражения, например присваивания.
Несмотря на немного разный вид, по сути две эти записи делают одно и то же:
// Function Declaration function sum(a, b) { return a + b; } // Function Expression var sum = function(a, b) { return a + b; }
Оба этих объявления говорят интерпретатору: "объяви переменную
sum
, создай функцию с указанными параметрами и кодом и сохрани её вsum
".Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода.
Поэтому их можно вызвать до объявления, например:
sayHi("Вася"); // Привет, Вася function sayHi(name) { alert( "Привет, " + name ); }
А если бы это было объявление Function Expression, то такой вызов бы не сработал:
sayHi("Вася"); // ошибка! var sayHi = function(name) { alert( "Привет, " + name ); }
-
Условное объявление функции
В некоторых случаях «дополнительное удобство» Function Declaration может сослужить плохую службу.
Например, попробуем, в зависимости от условия, объявить функцию
sayHi
по-разному:var age = +prompt("Сколько вам лет?", 20); if (age >= 18) { function sayHi() { alert( 'Прошу вас!' ); } } else { function sayHi() { alert( 'До 18 нельзя' ); } } sayHi();
Function Declaration при
use strict
видны только внутри блока, в котором объявлены. Так как код в учебнике выполняется в режимеuse strict
, то будет ошибка.А что, если использовать Function Expression?
var age = prompt('Сколько вам лет?'); var sayHi; if (age >= 18) { sayHi = function() { alert( 'Прошу Вас!' ); } } else { sayHi = function() { alert( 'До 18 нельзя' ); } } sayHi();
Или даже так:
var age = prompt('Сколько вам лет?'); var sayHi = (age >= 18) ? function() { alert('Прошу Вас!'); } : function() { alert('До 18 нельзя'); }; sayHi();
-
Анонимные функции
function ask(question, yes, no) { if (confirm(question)) yes() else no(); } function showOk() { alert( "Вы согласились." ); } function showCancel() { alert( "Вы отменили выполнение." ); } // использование ask("Вы согласны?", showOk, showCancel);
Здесь же обратим внимание на то, что то же самое можно написать более коротко:
function ask(question, yes, no) { if (confirm(question)) yes() else no(); } ask( "Вы согласны?", function() { alert("Вы согласились."); }, function() { alert("Вы отменили выполнение."); } );
Функциональное выражение, которое не записывается в переменную, называют анонимной функцией.
-
new Function
Существует ещё один способ создания функции, который используется очень редко, но упомянем и его для полноты картины.
Он позволяет создавать функцию полностью «на лету» из строки, вот так:
var sum = new Function('a,b', ' return a+b; '); var result = sum(1, 2); alert( result ); // 3
То есть, функция создаётся вызовом
new Function(params, code)
:Таким образом можно конструировать функцию, код которой неизвестен на момент написания программы, но строка с ним генерируется или подгружается динамически во время её выполнения.
Пример использования – динамическая компиляция шаблонов на JavaScript, мы встретимся с ней позже, при работе с интерфейсами.
Рекурсия - когда функция вызывает сама себя.
-
Степень pow(x, n) через рекурсию
pow(x, n) = x * pow(x, n - 1)
Например, вычислим
pow(2, 4)
, последовательно переходя к более простой задаче:1.
pow(2, 4) = 2 * pow(2, 3)
2.
pow(2, 3) = 2 * pow(2, 2)
3.
pow(2, 2) = 2 * pow(2, 1)
4.
pow(2, 1) = 2
Этот алгоритм на JavaScript:
function pow(x, n) { if (n != 1) { // пока n != 1, сводить вычисление pow(x,n) к pow(x,n-1) return x * pow(x, n - 1); } else { return x; } } alert( pow(2, 3) ); // 8
Говорят, что «функция
pow
рекурсивно вызывает сама себя» доn == 1
.Значение, на котором рекурсия заканчивается, называют базисом рекурсии. В примере выше базисом является
1
.Общее количество вложенных вызовов называют глубиной рекурсии. В случае со степенью, всего будет
n
вызовов.Максимальная глубина рекурсии в браузерах ограничена, точно можно рассчитывать на
10000
вложенных вызовов, но некоторые интерпретаторы допускают и больше.Итак, рекурсию используют, когда вычисление функции можно свести к её более простому вызову, а его – ещё к более простому, и так далее, пока значение не станет очевидно.