Конспект по javascript
В разработке. Последнее обновление: 21.11.2015
Краткий конспект учебника learn.javascript.ru для быстрого повторения тонкостей javascript.
Введение
- Компиляция — это когда исходный код программы, при помощи специального инструмента, другой программы, которая называется «компилятор», преобразуется в другой язык, как правило — в машинный код. Этот машинный код затем распространяется и запускается. При этом исходный код программы остаётся у разработчика.
- Интерпретация — это когда исходный код программы получает другой инструмент, который называют «интерпретатор», и выполняет его «как есть». При этом распространяется именно сам исходный код (скрипт). Этот подход применяется в браузерах для JavaScript.
- JavaScript не может читать/записывать произвольные файлы на жесткий диск, копировать их или вызывать программы. Он не имеет прямого доступа к операционной системе.
- JavaScript, работающий в одной вкладке, не может общаться с другими вкладками и окнами, за исключением случая, когда он сам открыл это окно или несколько вкладок из одного источника (одинаковый домен, порт, протокол).
- Mozilla Developer Network — один из основных справочников.
- «Фолдинг» (от англ. folding) — возможность редактора скрыть-раскрыть блок кода.
Основы JavaScript
- Разница между
async
иdefer
: атрибутdefer
сохраняет относительную последовательность скриптов, аasync
— нет. Кроме того,defer
всегда ждёт, пока весь HTML-документ будет готов, аasync
— нет. - Атрибуты
async/defer
работают только в том случае, если назначены на внешние скрипты. - Рекомендуется точку с запятой ставить после каждой инструкции.
- Вложенные комментарии не поддерживаются.
- Директива
use strict
для режима полного соответствия современному стандарту языка. Не поддерживается IE9-. Отменить действие директивы нельзя. Можно указывать в рамках функции. - ES5 shim для IE8-
- Регистр букв в названиях переменных имеет значение. Русские буквы не рекомендуются.
- Всегда объявляем переменные через
var.
- Имена констант большими буквами (var WIDTH_BOX = 200).
- Никакого транслита (плохо: var cena; отлично: var price).
- Использовать короткие имена только для переменных «местного значения».
- Либо только «camelCase» (var totalPrice), либо только через "_" (var total_price).
- Если мы ищем переменную с одним именем, а находим — с другим, то зачастую самый лучший ход — это переименовать переменную, чтобы имя было тем, которое вы искали.
- Есть 5 «примитивных» типов:
number
,string
,boolean
,null
,undefined
и 6-й тип — объектыobject
. Типа «символ» нет. - Существуют специальные числовые значения
Infinity
(бесконечность, при делении на ноль) иNaN
(ошибка вычислений, "нечисло"*5). - Оператор
typeof x
(или typeof(x)) позволяет выяснить, какой тип находится вx
, возвращая его в виде строки. - Результат
typeof null == "object"
— это официально признанная ошибка в языке, которая сохраняется для совместимости. На самом делеnull
— это не объект, а отдельный тип данных. typeof
выделяет функции отдельно, возвращая для них"function"
- Особенность исключительно бинарного оператора "+" - приведение к строке.
- «Побочный эффект» унарного "+" — преобразование значения в число (+"5"+1 = 6).
- Оператор
"="
возвращает значение (var c=2+(a=b+1)) - Разница между «постфиксной»(i++) и «префиксной»(++i) формами инкремента/декремента:
var x=1; var y=x++; //y=1
- Оператор
',':
каждое из выражений вычисляется и отбрасывается, за исключением последнего, которое возвращается (for (a=1, b=2, c=a+b; c<100; c++) {...}) - Строки сравниваются побуквенно, лексикографически. Кодировка Unicode. Для корректного сравнения символы должны быть в одинаковом регистре. "2">"14" - true, +"2">+"14" - false
- Для проверки равенства без преобразования типов используются операторы строгого равенства
===
(тройное равно) и!==
. Если тип разный, то они всегда возвращаютfalse.
- Значения
null
иundefined
равны==
друг другу и не равны чему бы то ни было ещё. Это жёсткое правило буквально прописано в спецификации языка. При преобразовании в числоnull
становится0
, аundefined
становитсяNaN
. - Побитовые операторы
^
,&
,|
выполняются после сравнений== (if ((a==b)^0))
- Округление: ~~12.345 или 12.345 ^ 0 // 12
- Проверка на -1: if (~str.indexOf(s))
- Оператор
a << b
, сдвигая биты, по сути умножаетa
на2
в степениb
. Оператор сдвига в другую сторонуa >> b
, производит обратную операцию — целочисленное делениеa
на2b
. - Safari 5.1+ не возвращает
null
при отмене ввода вres=prompt(info,default)
. Всегда указывайтеdefault ('').
- Тернарный оператор
(age>20) ? x=100: x=0;
- var res= x || y || z;
- Не используйте
&&
вместоif
- Три преобразование типов: строковое, числовое, к логическому типу. String(), Number(), Boolean() (или !!). Boolean(
"0")
-true.
- Чтобы организовать бесконечный цикл, используют конструкцию
while(true)
. - Нельзя использовать break/continue справа от оператора '?'
- Синтаксические конструкции, которые не возвращают значений, нельзя использовать в операторе
'?'
- Метка outer: . Переход к метке break outer; Метки не позволяют прыгнуть в произвольное место кода, в JavaScript нет такой возможности.
- В
case
могут быть любые выражения, в том числе включающие в себя переменные и функции. Несколько значений case можно группировать. Еслиbreak
нет, то выполнение пойдёт ниже по следующимcase
, при этом остальные проверки игнорируются. Операторswitch
предполагает строгое равенство===.
- Функция может содержать локальные переменные, объявленные через
var
. Такие переменные видны только внутри функции. Блокиif/else
,switch
,for
,while
,do..while
не влияют на область видимости переменных. Параметры функции копируются в локальные переменные функции. - Функцию можно вызвать с любым количеством аргументов. Если параметр не передан при вызове — он считается равным
undefined
. - Когда функция не вернула значение или
return
был без аргументов, считается что она вернулаundefined
. После выполнения return оставшийся код в функции не выполняется. - Одна функция - одно действие.
- Основное отличие между ними Function Declaration и Function Expression: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода. Еще отличие: в условном объявлении if. Если нет явной причины использовать Function Expression — предпочитайте Function Declaration.
- Функциональное выражение, которое не записывается в переменную, называют анонимной функцией.
- У каждого вызова функции есть свой «контекст выполнения» (execution context). Максимальная глубина рекурсии в браузерах ограничена, точно можно рассчитывать на
10000
вложенных вызовов. При любом вложенном вызове JavaScript запоминает текущий контекст выполнения в специальной внутренней структуре данных — «стеке контекстов». Любая рекурсия может быть переделана в цикл. Как правило, вариант с циклом будет эффективнее. - «Named Function Expression» доступно только изнутри самой функции (недоступно IE8-), нельзя перезаписать. Внутреннее имя позволяет функции надёжно обращаться к самой себе, где бы она ни находилась. Специальное значение
arguments.callee
устарело.
Качество кода
- Брейкпойнт (breakpoint) — точка останова. Это то место в коде, где отладчик будет автоматически останавливать выполнение JavaScript, как только оно до него дойдёт. Останов можно инициировать из кода командой
debugger.
- Общее правило именования: имя переменной — существительное, имя функции — глагол или начинается с глагола. Бывает, что имена функций для краткости делают существительными, но глаголы понятнее.
- Уровней вложенности должно быть немного. Лучше быстро обработать простые случаи, вернуть результат, а дальше разбираться со сложным, без дополнительного уровня вложенности. Функции должны быть небольшими. Если функция большая — желательно разбить её на несколько. Как правило, лучше располагать функции под кодом, который их использует.
- Должен быть минимум комментариев, которые отвечают на вопрос «что происходит в коде?». Точнее: архитектурный комментарий — «как оно, вообще, устроено», справочный комментарий (синтаксис JSDoc) перед функцией — о том, что именно она делает, какие параметры принимает и что возвращает. «Если вам кажется, что нужно добавить комментарий для улучшения понимания, это значит, что ваш код недостаточно прост, и, может, стоит переписать его» Р. Мартин
- Автоматизированное тестирование — это когда тесты написаны отдельно от кода, и можно в любой момент запустить их и проверить все важные случаи использования. Тесты BDD — это три в одном: тесты, документация, примеры использования одновременно.
- С точки зрения BDD, ошибка при проходящих тестах — вина спецификации. Один тест тестирует ровно одну вещь.
Структуры данных
- Обращение к методам чисел: 12.34.toFixed(2); Ошибка: 12.toFixed(2); Решение: 12..toFixed(2).
- Infinity больше любого числа, Infinity+5 == Infinity.
- NaN не равно ничему, включая себя. isNaN(0/0) - true. NaN+1 == NaN. Функция
isFinite(n)
преобразует аргумент к числу и возвращаетtrue
, если это неNaN/Infinity/-Infinity.
- Функция
parseInt
и ее аналогparseFloat
преобразуют строку символ за символом, пока это возможно. - Метод
toFixed
не эквивалентенMath.round
- Проблема неточных вычислений (результат сложения
0.1 + 0.2
немного больше, чем0.3). Решение: +res.toFixed(10).
- x.toLocaleString(); // 123 456 777 (для IE10- Intl.js)
- Экранирование символов в строке (\', \", \$$.
- Разница между s[] и s.
charAt()
заключается в том, что если символа нет —charAt
выдает пустую строку, а скобки —undefined.
- Содержимое строки в JavaScript нельзя изменять.
- Из трёх методов (slice, substring, substr) для использования в большинстве ситуаций —
slice
: поддерживает отрицательные аргументы и работает наиболее очевидно. - При сравнении строк следует иметь в виду, что буквы сравниваются по их кодам. Поэтому большая буква меньше маленькой, а буква
ё
вообще вне основного алфавита.Правильное сравнение строк: s.localeCompare(s2) (для IE10- Intl.js). - Для проверки существования свойства в объекте есть оператор
in:
if ("name" in obj) {...}. Или проверка на undefined: if (obj.name===undefined) {...} (при условии, что не используем undefined для свойств объекта). - При создании множества объектов одного и того же вида (с одинаковыми полями) интерпретатор выносит описание полей в отдельную структуру. А сам объект остаётся в виде непрерывной области памяти с данными.
- При переборе
for(key in codes)
ключиkey
не обязаны перечисляться именно в том порядке, в котором заданы. Но есть соглашение: если имя свойства — нечисловая строка, то такие ключи всегда перебираются в том же порядке, в каком присваивались. Если имя свойства — число или числовая строка, то все современные браузеры сортируют такие свойства в целях внутренней оптимизации. - Хак сделать все ключи нечисловыми - добавить в начало дополнительный символ
'+': var x={'+7': 'A','+2':'B');
- Фундаментальным отличием объектов от примитивов, является их хранение и копирование «по ссылке». Если переменная-объект скопирована или передана в функцию, то копируется именно эта ссылка, а объект остаётся один в памяти.
- “Глубокое” клонирование - клонирование и подобъектов.
- При “раскрытии” свойств объекта в консоли — браузер всегда выводит их текущие (на момент раскрытия) значения.
- Длина
length
— не количество элементов массива, апоследний индекс + 1
. При уменьшенииlength
массив укорачивается (потерянные значения не восстанавливаются). Очистить массив: a.length=0; - В массивах методы
push
иunshift
могут добавлять сразу по несколько элементов. Методыpush/pop
выполняются быстро, аshift/unshift
— медленно. - Не используйте
for..in
для массивов. - Не мешать внутренней оптимизации массивов: не назначать свойств, заполнять непрерывно.
- new Array + join = Повторение строки
- sort(compareNumeric());// не сработает
- Свойства объекта в виде массива: Object.keys(obj) (не для IE8-)
- В JavaScript нет “перегрузки” функций. Доступ к аргументам можно осуществлять через “псевдомассив”(!) arguments.
- x = x || 100; // если не указано x, то x станет равным 100
- Значения по умолчанию при передаче параметров в функцию - реализация через объект ("именованные аргументы")
- В Date не getYear(), а getFullYear(). Автоисправление даты. Результат вычитания объектов
Date
— их временная разница (в мс) - (кроме IE9-) вызов performance.now() возвращает количество миллисекунд, прошедшее с самого начала, до того, как загрузился HTML-файл, если точнее — с момента выгрузки предыдущей страницы из памяти.
console.time(метка)
иconsole.timeEnd(метка)
- Внутренняя оптимизация: браузеры автоматически выносят инвариант, то есть постоянное в цикле значение типа
arr.length
, за пределы цикла. - Date.now() быстрее, чем +new Date
Замыкания, область видимости
- Вообще, замыкание — это функция вместе со всеми внешними переменными, которые ей доступны. Но в javascript подразумевают не саму эту функцию, а именно внешние переменные.
- Присваивая или читая глобальную переменную, мы, фактически, работаем со свойствами объекта
window
. Функции, объявленные как Function Declaration, создаются сразу работающими, а переменные — равнымиundefined
. Всеvar
будут обработаны один раз, на фазе инициализации. - Все переменные внутри функции — это свойства специального внутреннего объекта (“лексическое окружение” или просто “объект переменных”)
LexicalEnvironment
, который создаётся при её запуске. - При создании функция получает скрытое свойство
[[Scope]]
, закрытое от прямого доступа, которое ссылается на лексическое окружение, в котором она была создана. - Внутри функции можно объявлять не только локальные переменные, но и другие функции.
- Иногда свойства, привязанные к функции, называют “статическими переменными”.
- Исключение из общего правила: при создании функции с использованием
new Function
, её свойство[[Scope]]
ссылается не на текущийLexicalEnvironment
, а наwindow
. Полезно, так как минификатор переименовывает локальные переменные. - Модуль при помощи замыканий — это оборачивание пакета функционала в единую внешнюю функцию, которая тут же выполняется. Объявление модуля: (function(){...}()); Можно через операторы (+,!) вместо скобок.
- Библиотека lodash.js.
- В начале кода находится точка с запятой
;
— защита от ошибки, когда несколько JS-файлы объединены в один и программист забыл поставить точку с запятой. - Принцип достижимости (reachability) при управлении памятью. Для очистки памяти от недостижимых значений в браузерах используется автоматический Сборщик мусора (Garbage collection, GC), встроенный в интерпретатор, который наблюдает за объектами и время от времени удаляет недостижимые.
- Оптимизация: деление объектов на “старые” и “новые”. Для каждого типа выделяется своя область памяти. Каждый объект создаётся в “новой” области и, если прожил достаточно долго, мигрирует в старую. “Новая” область обычно небольшая. Она очищается часто. “Старая” — редко.
- Побочный эффект в V8 (Chrome, Opera) — удалённая переменная станет недоступна и при отладке.
- Вместо устаревшей конструкции with — временную переменную.
Методы объектов и контекст вызова
- Для доступа к текущему объекту из метода и для передачи объекта используется ключевое слово
this
. Чтобыthis
передался, нужно вызвать функцию именно через точку (или квадратные скобки). - Любая функция может иметь в себе
this (
объявлена ли она в объекте или отдельно от него). Значениеthis
называется контекстом вызова и будет определено в момент вызова функции. Если одну и ту же функцию запускать в контексте разных объектов, она будет получать разныйthis.
- Любой объект в логическом контексте —
true
- Если в объекте присутствует метод
toString
, который возвращает любой примитив, то он используется для преобразования. Для численного преобразования объекта используется методvalueOf
, а если его нет — тоtoString.
- Исключение: у объектов
Date
есть иvalueOf
— возвращает количество миллисекунд, иtoString
— возвращает строку с датой. Но оператор+
дляDate
использует именноtoString
(хотя должен быvalueOf
). - В JavaScript вызовы
new Boolean/String/Number
не используются, а используются простые вызовы соответствующих функций, они преобразуют значение в примитив нужного типа, напримерBoolean(val) === !!val
. - {} - не объект, а пустой блок кода.
- Функции, задуманные как конструкторы, называют с большой буквы. По умолчанию возвращает this. Вызов
return
с объектом вернёт объект, а с чем угодно, кроме объекта — возвратитthis
. - Метод для управления свойствами Object.defineProperty(obj, prop, descriptor).
- Методы и свойства, которые не привязаны к конкретному экземпляру объекта, называют “статическими”. Их записывают прямо в саму функцию-конструктор. “Фабричный статический метод” — статический метод, который служит для создания новых объектов.
func.call(context, a, b...). “Одалживание метода” (method borrowing). Вызов функции при помощи func.apply работает аналогично func.call, но принимает массив аргументов вместо списка.
- Math.max.apply(null, a); // поиск максимума в массиве
- this: 1) obj.func(...) // this = obj; 2) func(...) // this=window (ES3)/undefined(ES5); 3) new func(...) // this={}; 4) func.apply(cnt, ...); func.call(cnt,....) // this=cnt
- При потере контекста: а) сделать обертку анонимной функцией; б) написать функцию bind для привязки контекста; в) встроенная функция bind (
bind
не вызывает функцию, только возвращает “обёртку”) - Карринг (currying) или каррирование — термин функционального программирования, который означает создание новой функции путём фиксирования аргументов существующей.
- Декоратор — приём программирования, который позволяет взять существующую функцию и изменить/расширить ее поведение.
Некоторые другие возможности
ООП в функциональном стиле
ООП в прототипном стиле
Современные возможности ES-2015