Конспект по javascript

Конспект по javascript

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

другие материалы