-
Notifications
You must be signed in to change notification settings - Fork 46
...введение
Скрипты, которые должны подгрузиться до отрисовки основных элементов страницы, добавляются
в тэг head. Остальные скрипты чаще всего помещают в конец тэга body, чтобы они не перегружали
основной поток и не мешали отрисовки других элементов.
Специальные атрибуты async и defer используются для того, чтобы внешний скрипт загружался асинхронно, не препятствуя показу оставшейся части страницы.
Разница между async и defer:
- defer сохраняет относительную последовательность скриптов, а async – нет.
- defer всегда ждёт, пока весь HTML-документ будет готов, а async – нет.
Переменная состоит из имени и выделенной области памяти, которая ему соответствует.
Имя переменной может содержать буквы, цифры, $, _.
Регистр имеет значение.
Константы называют следующим образом: ANY_NAME.
Есть 5 примитивных типов и объекты:
- number // 1, 2.17
- string // 'str', "str"
- boolean // true, false
- null
- undefined
- object // { ... }
Значение null не является «ссылкой на нулевой адрес/объект» или чем-то подобным.
Значение null специальное и имеет смысл «ничего» или «значение неизвестно».
Значение undefined означает «переменная не присвоена».
Оператор typeof возвращает тип аргумента. Его результатом являетя строка.
typeof undefined // "undefined"
typeof 0 // "number"
typeof true // "boolean"
typeof "foo" // "string"
typeof {} // "object"
typeof null // "object" (ошибка в языке)
typeof function(){} // "function"Функции не являются отдельным базовым типом в JavaScript, а подвидом объектов. Но typeof
выделяет функции отдельно, потому что легко определить функцию.
Операнд (аргумент оператора) – то, к чему применяется оператор.
Унарным называется оператор, который применяется к одному операнду.
Бинарным называется оператор, который применяется к двум операндам.
Бинарный "+" приводит выражение к строке, если хотя бы один его операнд является строкой.
Унарный "+" приводит операнд к числу.
Ассоциативность определяет порядок, в котором обрабатываются операторы с одинаковым приоритетом.
Левая ассоциативность (слева направо) означает, что оно обрабатывается как (a OP b) OP c.
Правая ассоциативность (справа налево) означает, что они интерпретируются как a OP (b OP c).
Пример: операторы присваивания являются право-ассоциативными
Приоритет операторов:
- 20 - группировка (не определено)
- 19 - доступ к свойствам (.), доступ к свойствам ([]), new c аргументами, вызов функции
- 18 - new без аргументов
- 17 - постфиксные инкремент и декремент
- 16 - унарный плюс/минус, отрицание, await, typeof, delete (справа налево)
- 15 - возведение в степень (**, справа налево)
- 14 - умножение, деление, остаток (слева направо)
- 13 - сложение, вычитание (слева направо)
- 12 - побитовые сдвиги (слева направо)
- 11 - сравнения (слева направо)
- 10 - равенства и неравенства (слева направо)
- 7-9 - побитовые операторы (слева направо)
- 6 - логическое && (слева направо)
- 5 - логическое || (слева направо)
- 4 - тернарный (справа налево)
- 3 - присваивание (=, справа налево)
- 2 - yeld (справа налево)
- 1 - запятая (слева направо)
Все операторы без исключения возвращают значение.
Оператор запятая позволяет перечислять выражения, разделяя их запятой ','. Каждое из них
вычисляется и отбрасывается, за исключением последнего, которое возвращается.
У запятой приоритет ниже, чем у оператора присваивания, поэтому:
a = 5, 6 --> a = 5
b = (5, 6) --> b = 6Операторы сравнения возвращают значение логического типа.
Сравнение строк производится побуквенно и называется лексикографическим.
Для сравнения строк используются численные коды символов.
При сравнении разных типов происходит приведение операндов к числу.
Для проверки равенства без преобразования типов используются **операторы строгого **
равенства === и !==.
При преобразовании в число null становится 0, а undefined становится NaN.
При проверке равенства значения null и undefined равны друг другу, но не равны чему-то ещё.
В остальных случаях сравнение с undefined всегда даст false.
parseInt("11000", 2) – переводит строку с двоичной записью числа в число
n.toString(2) – получает для числа n запись в двоичной системе в виде строки
Можно использовать битовые маски для того, чтобы хранить информацию о доступе пользователя к
ресурсам в виде последовательности нулей и единиц (101101 = 45), что экономит память.
Оператор if (...) вычисляет и преобразует выражение в скобках к логическому типу.
В логическом контексте число 0, пустая строка "", null и undefined,
а также NaN являются false, остальные значения – true.
Вопросительный знак – единственный оператор, у которого есть три аргумента, поэтому
его называют «тернарный оператор»
Логические операторы || (ИЛИ), && (И) и ! (НЕ) могут применяться к значениям любого типа
и возвращают также значения любого типа.
Для экономии ресурсов используется короткий цикл вычислений:
|| запинается на «правде», && запинается на «лжи».
То есть || возвращает первое правдивое или последнее значение, && возвращает первое ложное
или последнее значение.
Приоритет у && больше, чем у ||.
Оператор ! сперва приводит аргумент к логическому типу, а затем возвращает **противоположное **
значение.
По этой причине !! используют для преобразования значений к логическому типу.
...циклы, switch, функции
Модальная функция, модальное окно называются так, потому что не позволяют посетителю
взаимодействовать со страницей, пока он не ответит.
Директива 'use strict' нужна для того, чтобы интерпретатор работал в режиме масимального
соответствия современному стандарту (строгом режиме). Есть возможность указать её в начале функции.
Пример: можно просто пофиксив один баг, создать новый и не заметить этого. Тесты нужны, чтобы
этого избежать.
Автоматизированное тестирование – это когда тесты написаны отдельно от кода, и можно в любой
момент запустить их и проверить все важные случаи использования.
Behavior Driven Development (разработка через поведение) - это методология разработки ПО,
являющаяся ответвлением от методологии разработки через тестирование (TDD).
Пусть, нужно написать функцию возведения в степень.
Мы можем ещё до разработки представить, что функция будет делать и описать это по методике BDD.
Это описания называется спецификацией (спекой).
У спецификации есть три основных строительных блока:
-
describe(название, function() { ... })
Задаёт, что именно мы описываем, используется для группировки блоков it. -
it(название, function() { ... })
В названии блока it человеческим языком описывается, что должна делать функция, далее следует тест,
который проверяет это. -
assert.equal(<результат выполнения функции>, <ожидаемый ответ>)
Код внутри it, если реализация верна, должен выполняться без ошибок.
Пример:
describe("pow", function() {
it("возводит в n-ю степень", function() {
assert.equal(pow(2, 3), 8);
assert.equal(pow(3, 4), 81);
});
it("при возведении в отрицательную степень результат NaN", function() {
assert(isNaN(pow(2, -1)));
});
});Примечание: Лучше делать только одну проверку в каждом it, потому что иначе после ошибки код не
будет проверен.
Примечание: Блоки describe могут быть вложенными, что может пригодиться, чтобы определить
функцию для вычисления ожидаемого значения. Вложенный describe объявляет новую подгруппу тестов.
assert(value) – проверяет что value является true в логическом контексте.
Алгоритм:
- Пишется спецификация, которая описывает самый базовый функционал.
- Делается начальная реализация.
- Для проверки соответствия спецификации мы задействуем фреймворк Mocha, который запускает все
тесты it и выводит ошибки, если они возникнут. При ошибках вносятся исправления. -
Спецификация расширяется, в неё добавляются новые возможности, которые могут быть ещё не
реализованы. - Снова делается реализация. Так образуется цикл.
Разработка ведётся итеративно: один проход за другим, пока спецификация и реализация не будут
завершены.
Mocha – эта библиотека содержит общие функции для тестирования, включая describe и it.
Chai – библиотека поддерживает разнообразные функции для проверок.
Sinon – библиотека для эмуляции и хитрой подмены функций «заглушками».
Все значения за исключением null и undefined содержат набор вспомогательных функций и значений,
доступных через оператор точка. Такие функции называют методами, а значения - свойствами.
Все числа (целые и дробные) имеют тип Number и хранятся в 64-битном формате double precision.
Infinity – особенное численное значение, которое ведет себя в точности как математическая
бесконечность ∞.
Её можно получить с помощью деления на ноль.
Для проверки на конечность числа можно использовать isFinite(x), где x приводится к числу.
Для проверки на число можно использовать:
const isNumeric = x => !isNaN(parseFloat(x)) && isFinite(x);Значение NaN используется для обозначения математической ошибки.
Его можно получить при делении 0 / 0.
Это единственное значение, которое не равно ничему, включая себя.
Для проверки можно использовать isNaN(x), где x приводится к числу.
Для приведения к числу используется унарный +, при этом если приводится строка и она не
является в точности числом (исключая пробельные символы), то результатом будет NaN.
Функции parseInt и parseFloat преобразуют строку символ за символом, пока это возможно.
Они возвращают NaN, если первый символ не является числом.
Также можно указать систему счисления:
parseInt('FF', 16) --> 255Дробные числа дают ошибку вычислений.
При необходимости ее можно отсечь округлением до нужного знака c помощью x.toFixed(<знак>).
Внутренний формат строк вне зависимости от кодировки страницы - Unicode.
Cтроки могут содержать специальные символы, которые нужно экранировать с помощью ****.
Содержимое строки в JavaScript нельзя изменять. Как только строка создана – она такая навсегда.
Строки сравниваются в лексикографическом порядке (посимвольно).
Символы сравниваются не по алфавиту, а по их числовому коду в Unicode.
Если на каком-то шаге символы не совпали, то поиск прекращается и выдаётся результат.
Если символы до момента окончания первого слова совпали со вторым, а второе слово не закончилось,
тогда оно больше первого.
Объект в JS - ассоциативный массив.
Ассоциативный массив - структура данных, в которой можно хранить любые данные в формате ключ-значение.
... объекты, массивы
Глобальными называют переменные и функции, которые не находятся внутри конструкции function.
В JS все глобальные переменные и функции являются свойствами специального объекта, который
называется «глобальный объект» (global object). В браузере это window.
Интерпритация происходит в две фазы:
-
Инициализация, подготовка к запуску.
Во время инициализации скрипт ищет объявления функций вида Function Declaration, затем ищет объявления переменных var. Каждое объявление добавляется в window. -
Выполнение.
Построчное присваивание значений переменным (до этого они undefined).
// window = { f: function, a: undefined, g: undefined }
var a = 5;
// window = { f: function, a: 5, g: undefined }
function f(arg) { /*...*/ }
// window = { f: function, a: 5, g: undefined } без изменений, f обработана ранее
var g = function(arg) { /*...*/ };
// window = { f: function, a: 5, g: function }Переменную var можно объявлять сколько угодно раз, но она будет обработана только один раз
при инициализации.
Циклы и условные операторы не влияют на видимость переменных var.
Все переменные внутри функции – это свойства специального внутреннего объекта LexicalEnvironment, который создаётся при её запуске. Его называют «лексическим окружением», объектом переменных».
При запуске функция создает объект LexicalEnvironment, записывает туда аргументы, функции и переменные.
Процесс инициализации выполняется в том же порядке, что и для глобального объекта, который является
частным случаем лексического окружения.
В отличие от window, объект LexicalEnvironment является внутренним, он скрыт от прямого доступа.
При вызове функции:
- интерпретатор создаёт пустой объект лексического окружения и заносит туда нужные переменные
- во время выполнения функции значения переменных присваиваются/изменяются
- в конце выполнения функции объект обычно выбрасывается и память очищается
Интерпретатор, при доступе к переменной, сначала пытается найти переменную в текущем LexicalEnvironment,
а затем, если её нет – ищет во внешнем объекте переменных. Самым верхним является window.
Такой порядок поиска возможен потому, что ссылка на внешний объект переменных хранится в специальном
внутреннем свойстве функции Scope, закрытым от прямого доступа.
При создании функция получает скрытое свойство Scope, которое ссылается на лексическое окружение,
в котором она была создана.
Это свойство никогда не меняется.
Значение переменной из внешней области берётся всегда текущее.
Оно может быть уже не то, что было на момент создания функции.
Внутри функций можно объявлять другие функции, а также возвращать их.
Так как функция в JS является объектом, ей можно присваивать свойства (статические переменные):
function f() {};
f.something = "smth";Принципиальное отличие статической переменной от переменной из замыкания: к свойству функции есть
доступ у всех, кто имеет объект функции.
Замыкание – это функция вместе со всеми внешними переменными, которые ей доступны.
Обычно, говоря «замыкание функции», подразумевают именно внешние переменные функции.
Иногда говорят «переменная берётся из замыкания» (из внешнего объекта переменных).
При создании функции с использованием new Function, её свойство Scope ссылается не на текущий LexicalEnvironment, а на window.
Поэтому такие функции не могут использовать замыкания.
Это связано с особенностями минификации кода: минификатор переименовывает локальные переменные, которые
могли бы использоваться в объявленной функции, что вызвало бы ошибку: искомых переменных нет.
Устаревшая конструкция with позволяет использовать в качестве области видимости для переменных
произвольный объект.
with(obj) { /***/ }Приём проектирования «Модуль» позволяет скрыть внутренние детали реализации.
Он наделяет скрипт собственной областью видимости.
Реализовать модуль можно, поместив всё содержимое скрипта в новую функцию, которая сразу же будет вызвана:
(function() { /.../ }());Скобки вокруг нужны для того, чтобы показать интерпретатору, что это Function Expression, а не Function
Declaration, что поможет избежать ошибки: вызывать на месте можно только Function Expression.
Все функции модуля имеют доступ к другим переменным и внутренним функциям этого же модуля через замыкание.
Снаружи можно обращаться лишь к экспортированным из модуля переменным и функциям.
... управление памятью в JS