Секреты javascript-функций
Содержание:
- 3, 2 и 1) getCookie(), setCookie(), deleteCookie()
- Основы бэкенд
- Свойство «length»
- Named Function Expression
- Синтаксис
- JavaScript Функции
- Остаточные параметры (…)
- Function Expressions
- Функции-«колбэки»
- Выбор имени function
- Functions are Objects
- More Examples
- Parameter Values
- Вложенные функции
- JS Учебник
- Итоги
- Итого: замыкания
3, 2 и 1) getCookie(), setCookie(), deleteCookie()
В javascript нет способа нормально работать с cookie без дополнительных функций. Не знаю, кто проектировал , но сделано на редкость убого.
Поэтому следующие функции или их аналоги просто необходимы.
// возвращает cookie если есть или undefined function getCookie(name) { var matches = document.cookie.match(new RegExp( "(?:^|; )" + name.replace(/(\\\/\+^])/g, '\\$1') + "=(*)" )) return matches ? decodeURIComponent(matches) : undefined } // уcтанавливает cookie function setCookie(name, value, props) { props = props || {} var exp = props.expires if (typeof exp == "number" && exp) { var d = new Date() d.setTime(d.getTime() + exp*1000) exp = props.expires = d } if(exp && exp.toUTCString) { props.expires = exp.toUTCString() } value = encodeURIComponent(value) var updatedCookie = name + "=" + value for(var propName in props){ updatedCookie += "; " + propName var propValue = props if(propValue !== true){ updatedCookie += "=" + propValue } } document.cookie = updatedCookie } // удаляет cookie function deleteCookie(name) { setCookie(name, null, { expires: -1 }) }
Аргументы:
- name
- название cookie
- value
- значение cookie (строка)
- props
-
Объект с дополнительными свойствами для установки cookie:
- expires
- Время истечения cookie. Интерпретируется по-разному, в зависимости от типа:
- Если число — количество секунд до истечения.
- Если объект типа Date — точная дата истечения.
- Если expires в прошлом, то cookie будет удалено.
- Если expires отсутствует или равно 0, то cookie будет установлено как сессионное и исчезнет при закрытии браузера.
- path
- Путь для cookie.
- domain
- Домен для cookie.
- secure
- Пересылать cookie только по защищенному соединению.
Основы бэкенд
Как уже упоминалось, еще 10 лет назад JS использовался только для фронтенд-разработки. Теперь, благодаря Node.js, JavaScript работает и на серверной стороне.
SSR, CSR, изоморфные приложения
SSR — Server-Side Rendering – формирование страницы на стороне сервера.
CSR — Client Side Rendering – формирование страницы на стороне клиента (в браузере).
Какие проблемы имеются у этих способов?
SSR имеет ряд ограничений (скорость интернета у клиента, количество одновременных пользователей и т.д.). Это заметно снижало скорость, но это годилось для SEO оптимизации (так как при запросе к серверу робот получал сформированную страницу).
CSR – избавился от проблем скорости SSR (быстрый рендеринг, кеширование), появились одностраничные Single Page Application (SPA). Но для таких одностраничных приложений CSR SEO оптимизация не годилась, так как весь контент передается клиенту и формируется в браузере (после инициализации начинается загрузка контента), а поисковый робот при запросе на сервер получает лишь пустую страницу.
Node.js
Node — это среда для выполнения JS на стороне сервера. Вам не нужно будет изучать новый синтаксис, а только лишь научиться импортировать и экспортировать файлы, разбивать код на модули и использовать менеджер пакетов npm.
Теперь с помощью NodeJS стало возможным написать логику на серверной стороне, и она будет работать и на сервере (при первом обращении посетителя или поискового робота генерировался HTML с контентом страницы) и в браузере (последующие переходы посетителя). Это и называется изоморфное, универсальное приложение.
Схема функционирования простая: при первом заходе посетитель отправляет запрос на сервер NodeJS, который обращается к API-серверу, берёт данные в виде JSON и формирует страницу HTML, возвращая её клиенту. Теперь приложение работает на клиентской стороне, в браузере: при переходе на другие страницы приложение обращается за данными к API-серверу, и отрисовывает страницу уже в браузере.
В React (см. дальше) реализация этой схемы осуществляется разными и сложными путями. В качестве готовых решений есть для этого, например, фреймворк Next.js. В документации Vue (см. дальше) есть целый раздел, посвященный SSR. Там указан фреймворк Nuxt — Vue + SSR, при помощи которого можно быстро создавать универсальные приложения.
Серверы, HTTP, Express.js
Изучив Node, можно более глубже изучить бэкенд-разработку и разобраться в серверах и маршрутизации. Акцент делать на портах, протоколах HTTP. Затем можно просмотреть Express-Node-библиотеку для обработки запросов.
Асинхронный JavaScript
Асинхронность является одной из главных фишек (фич) JavaScript, хотя и создаёт некий раскол среди разработчиков: некоторые её любят, а некоторые — ненавидят. Нужно разобраться в её плюсах и минусах. Начните со стека вызовов, цикла событий и коллбеков, а затем перейдите к изучению промисов здесь или здесь.
Базы данных, СУБД, схемы, модели и ORM
БД — один из важнейших элементов веб-разработки. Чтобы статичные данные, а также новые данные, сформированные при взаимодействии пользователя со страницей, не терялись при обновлении страницы, любому приложению нужно их загружать откуда-то или хранить их где-то. Для этого используется БД.
Нужно научиться различать реляционные и нереляционные базы данных и разобраться в типах связей. Затем изучить SQL и быть в курсе разных систем управления базами данных. Знание ORM тоже не помешает.
Веб-сокеты
Не стоит пренебрегать этой темой. Веб-сокеты очень полезны. В отличие от протокола HTTP WebSocket позволяет работать с двунаправленным потоком данных. Самой распространённой реализацией является библиотека socket.io .
Также нужно разобраться в механизме взаимодействия приложения с пользователем, обработки им входа в учетную запись, отслеживания личности при помощи cookies — небольших текстовых файлов, которые передаются от сервера браузеру по HTTP-запросу. Связь между БД и страницей авторизации использует библиотеку express-session.
Свойство «length»
Ещё одно встроенное свойство «length» содержит количество параметров функции в её объявлении. Например:
Как мы видим, троеточие, обозначающее «остаточные параметры», здесь как бы «не считается»
Свойство иногда используется для интроспекций в функциях, которые работают с другими функциями.
Например, в коде ниже функция принимает в качестве параметров вопрос и произвольное количество функций-обработчиков ответа .
Когда пользователь отвечает на вопрос, функция вызывает обработчики. Мы можем передать два типа обработчиков:
- Функцию без аргументов, которая будет вызываться только в случае положительного ответа.
- Функцию с аргументами, которая будет вызываться в обоих случаях и возвращать ответ.
Чтобы вызвать обработчик правильно, будем проверять свойство .
Идея состоит в том, чтобы иметь простой синтаксис обработчика без аргументов для положительных ответов (наиболее распространённый случай), но также и возможность передавать универсальные обработчики:
Это частный случай так называемого – обработка аргументов в зависимости от их типа или, как в нашем случае – от значения . Эта идея имеет применение в библиотеках JavaScript.
Named Function Expression
Named Function Expression или NFE – это термин для Function Expression, у которого есть имя.
Например, давайте объявим Function Expression:
И присвоим ему имя:
Чего мы здесь достигли? Какова цель этого дополнительного имени ?
Для начала заметим, что функция всё ещё задана как Function Expression. Добавление после не превращает объявление в Function Declaration, потому что оно все ещё является частью выражения присваивания.
Добавление такого имени ничего не ломает.
Функция все ещё доступна как :
Есть две важные особенности имени , ради которого оно даётся:
- Оно позволяет функции ссылаться на себя же.
- Оно не доступно за пределами функции.
Например, ниже функция вызывает себя с , если не передан параметр :
Почему мы используем ? Почему просто не использовать для вложенного вызова?
Вообще, обычно мы можем так поступить:
Однако, у этого кода есть проблема, которая заключается в том, что значение может быть изменено. Функция может быть присвоена другой переменной, и тогда код начнёт выдавать ошибки:
Так происходит, потому что функция берёт из внешнего лексического окружения. Так как локальная переменная отсутствует, используется внешняя. И на момент вызова эта внешняя равна .
Необязательное имя, которое можно вставить в Function Expression, как раз и призвано решать такого рода проблемы.
Давайте используем его, чтобы исправить наш код:
Теперь всё работает, потому что имя локальное и находится внутри функции. Теперь оно взято не снаружи (и недоступно оттуда). Спецификация гарантирует, что оно всегда будет ссылаться на текущую функцию.
Внешний код все ещё содержит переменные и , но теперь – это «внутреннее имя функции», таким образом она может вызвать себя изнутри.
Это не работает с Function Declaration
Трюк с «внутренним» именем, описанный выше, работает только для Function Expression и не работает для Function Declaration. Для Function Declaration синтаксис не предусматривает возможность объявить дополнительное «внутреннее» имя.
Зачастую, когда нам нужно надёжное «внутреннее» имя, стоит переписать Function Declaration на Named Function Expression.
Синтаксис
Синтаксис для объявления функции:
Функция создаётся с заданными аргументами и телом .
Это проще понять на конкретном примере. Здесь объявлена функция с двумя аргументами:
А вот функция без аргументов, в этом случае достаточно указать только тело:
Главное отличие от других способов объявления функции, которые были рассмотрены ранее, заключается в том, что функция создаётся полностью «на лету» из строки, переданной во время выполнения.
Все предыдущие объявления требовали от нас, программистов, писать объявление функции в скрипте.
Но позволяет превратить любую строку в функцию. Например, можно получить новую функцию с сервера и затем выполнить её:
Это используется в очень специфических случаях, например, когда мы получаем код с сервера для динамической компиляции функции из шаблона, в сложных веб-приложениях.
JavaScript Функции
Функция (function) – это самостоятельный фрагмент кода, решающий определенную задачу. Каждой функции присваивается уникальное имя, по которому ее можно идентифицировать и «вызвать» в нужный момент.
Функции в языке JavaScript являются объектами, и следовательно ими можно манипулировать как объектами. Например, функции могут присваиваться переменным, элементам массива, свойствам объектов, передавать в качестве аргумента функциям и возвращать в качестве значения из функций.
В JavaScript есть встроенные функции, которые можно использовать в программах, но код которых нельзя редактировать или посмотреть.
Примеры встроенных функций вы уже видели – это , , и . Кроме использования встроенных функций, вы можете создать свои собственные, так называемые пользовательские функции.
Остаточные параметры (…)
Вызывать функцию можно с любым количеством аргументов независимо от того, как она была определена.
Например:
Лишние аргументы не вызовут ошибку. Но, конечно, посчитаются только первые два.
Остаточные параметры могут быть обозначены через три точки . Буквально это значит: «собери оставшиеся параметры и положи их в массив».
Например, соберём все аргументы в массив :
Мы можем положить первые несколько параметров в переменные, а остальные – собрать в массив.
В примере ниже первые два аргумента функции станут именем и фамилией, а третий и последующие превратятся в массив :
Остаточные параметры должны располагаться в конце
Остаточные параметры собирают все остальные аргументы, поэтому бессмысленно писать что-либо после них. Это вызовет ошибку:
должен всегда быть последним.
Function Expressions
A JavaScript function can also be defined using an expression.
A function expression can be stored in a variable:
Example
var x = function (a, b) {return a * b};
After a function expression has been stored in a variable, the variable can
be used as a function:
Example
var x = function (a, b) {return a * b};
var z = x(4, 3);
The function above is actually an anonymous function (a function without a
name).
Functions stored in variables do not need function names. They are always
invoked (called) using the variable name.
The function above ends with a semicolon because it is a part of an executable statement.
Функции-«колбэки»
Рассмотрим ещё примеры функциональных выражений и передачи функции как значения.
Давайте напишем функцию с тремя параметрами:
- Текст вопроса
- Функция, которая будет вызываться, если ответ будет «Yes»
- Функция, которая будет вызываться, если ответ будет «No»
Наша функция должна задать вопрос и, в зависимости от того, как ответит пользователь, вызвать или :
На практике подобные функции очень полезны. Основное отличие «реальной» функции от примера выше будет в том, что она использует более сложные способы взаимодействия с пользователем, чем простой вызов . В браузерах такие функции обычно отображают красивые диалоговые окна. Но это уже другая история.
Аргументы функции ещё называют функциями-колбэками или просто колбэками.
Ключевая идея в том, что мы передаём функцию и ожидаем, что она вызовется обратно (от англ. «call back» – обратный вызов) когда-нибудь позже, если это будет необходимо. В нашем случае, становится колбэком’ для ответа «yes», а – для ответа «no».
Мы можем переписать этот пример значительно короче, используя Function Expression:
Здесь функции объявляются прямо внутри вызова . У них нет имён, поэтому они называются анонимными. Такие функции недоступны снаружи (потому что они не присвоены переменным), но это как раз то, что нам нужно.
Подобный код, появившийся в нашем скрипте выглядит очень естественно, в духе JavaScript.
Функция – это значение, представляющее «действие»
Обычные значения, такие как строки или числа представляют собой данные.
Функции, с другой стороны, можно воспринимать как «действия».
Мы можем передавать их из переменной в переменную и запускать, когда захотим.
Выбор имени function
При выборе имён в function применяют те же правила, что и когда выбирают имена переменной. Однако следует помнить, что именем должен быть глагол, ведь функция — это действие. Как правило, в качестве имени используют глагольные префиксы, плюс уточнения.
Функцию, которая что-то показывает, называют show (это лучший вариант для имени):
showMessage(..) // "показать" сообщение
Запуск function, которая что-то получает, называют get, вычисляет — calc и т. д.
Примеры, каким именем можно назвать функцию:
getAge(..) // get, "получает" возраст calcD(..) // calc, "вычисляет" дискриминант createForm(..) // create, "создаёт" форму checkPermission(..) // check, "проверяет" разрешение, возвращает true/false
Использование данного метода названия — удобная штука, ведь, взглянув на функцию, мы примерно поймём, что она делает, какое значение возвращает. И даже если эту функцию написал кто-то другой, вы будете иметь представление о ней.
Остаётся запомнить, что вне зависимости от метода названия, одна функция — это одно действие. Если функция подразумевает поддействия, желательно выделить их в отдельные функции.
Functions are Objects
The operator in JavaScript returns «function» for
functions.
But, JavaScript functions can best be described as objects.
JavaScript functions have both properties and
methods.
The property returns the number of arguments received when
the function was invoked:
Example
function myFunction(a, b) { return arguments.length;}
The method returns the function as a string:
Example
function myFunction(a, b) { return a * b;}var txt = myFunction.toString();
A function defined as the property of an object, is called a method to the object.
A function designed to create new objects, is called an object constructor.
More Examples
Example
Return the value of PI:
function myFunction() { return Math.PI;}
Example
Return the product of a and b:
function myFunction(a, b) { return a * b;}
Example
With functions, you can use the same code many times with different arguments, to produce different
results.
Convert Fahrenheit to Celsius:
function toCelsius(fahrenheit) {
return (5/9) * (fahrenheit-32);
}
Example
Functions can be used as variables.
Instead of:
temp = toCelsius(32);text = «The temperature is » + temp + » Centigrade»;
You can use:
text = «The temperature is » + toCelsius(32) + » Centigrade»;
Example
JavaScript functions have a built-in object called the arguments object.
The arguments.length property returns the number of arguments received when
the function was invoked:
function myFunction(a, b) { return arguments.length;}
Example
Click on a button to call a function, which will output «Hello World» in an
element with id=»demo»:
<button onclick=»myFunction()»>Click me</button><p id=»demo»></p><script>function myFunction() { document.getElementById(«demo»).innerHTML = «Hello World»;}</script>
Example
A JavaScript function can also be defined using an expression.
A function expression can be stored in a variable:
var x = function (a, b) {return a * b};
Example
After a function expression has been stored in a variable, the variable can
be used as a function:
var x = function (a, b) {return a * b};
var z = x(4, 3);
Parameter Values
Parameter | Description |
---|---|
functionName | Required. Specifies the name of the function, which can be «saved for later use». Function names can contain letters, digits, underscores, and dollar signs (same rules as variables) |
parameters | Optional. Specifies a set of zero or more parameter names, separated by commas. Function parameters are the names listed in the function definition.Function arguments are the real values received by the function when it is invoked. Inside the function, the arguments are used as local variables.Note: If a function is called with a missing argument, the value of the missing argument is set to undefined |
Вложенные функции
Функция называется «вложенной», когда она создаётся внутри другой функции.
Это очень легко сделать в JavaScript.
Мы можем использовать это для упорядочивания нашего кода, например, как здесь:
Здесь вложенная функция создана для удобства. Она может получить доступ к внешним переменным и, значит, вывести полное имя. В JavaScript вложенные функции используются очень часто.
Что ещё интереснее, вложенная функция может быть возвращена: либо в качестве свойства нового объекта (если внешняя функция создаёт объект с методами), либо сама по себе. И затем может быть использована в любом месте
Не важно где, она всё так же будет иметь доступ к тем же внешним переменным
Например, здесь, вложенная функция присваивается новому объекту в конструкторе:
А здесь мы просто создаём и возвращаем функцию «счётчик»:
Давайте продолжим с примером . Он создаёт функцию «counter», которая возвращает следующее число при каждом вызове. Несмотря на простоту, немного модифицированные варианты этого кода применяются на практике, например, в генераторе псевдослучайных чисел и во многих других случаях.
Как же это работает изнутри?
Когда внутренняя функция начинает выполняться, начинается поиск переменной изнутри-наружу. Для примера выше порядок будет такой:
- Локальные переменные вложенной функции…
- Переменные внешней функции…
- И так далее, пока не будут достигнуты глобальные переменные.
В этом примере будет найден на шаге . Когда внешняя переменная модифицируется, она изменится там, где была найдена. Значит, найдёт внешнюю переменную и увеличит её значение в лексическом окружении, которому она принадлежит. Как если бы у нас было .
Теперь рассмотрим два вопроса:
- Можем ли мы каким-нибудь образом сбросить счётчик из кода, который не принадлежит ? Например, после вызова в коде выше.
- Если мы вызываем несколько раз – нам возвращается много функций . Они независимы или разделяют одну и ту же переменную ?
Попробуйте ответить на эти вопросы перед тем, как продолжить чтение.
…
Готовы?
Хорошо, давайте ответим на вопросы.
- Такой возможности нет: – локальная переменная функции, мы не можем получить к ней доступ извне.
- Для каждого вызова создаётся новое лексическое окружение функции, со своим собственным . Так что, получившиеся функции – независимы.
Вот демо:
Надеюсь, ситуация с внешними переменными теперь ясна. Для большинства ситуаций такого понимания вполне достаточно, но в спецификации есть ряд деталей, которые мы, для простоты, опустили. Далее мы разберём происходящее ещё более подробно.
JS Учебник
JS ГлавнаяJS ВведениеJS Что? Где? Куда?JS ВыводJS ЗаявленияJS СинтаксисJS КомментарииJS ПеременныеJS ОператорыJS АрифметикаJS ПрисваиванияJS Типы данныхJS ФункцииJS ОбъектыJS СобытияJS СтрокиJS Методы строкJS ЧислаJS Методы чиселJS МассивыJS Методы массиваJS Сортировка массиваJS Итерация массиваJS Объекты датJS Формат датJS Метод получения датJS Метод набора датJS Математические…JS Случайные числаJS БулевыJS Сравнение…JS Заявления if…elseJS Заявление switchJS Цикл forJS Цикл whileJS Заявление break…JS Преобразование…JS Битовые…JS Регулярные выраженияJS ОшибкиJS ОбластьJS ПодъемныйJS СтрогийJS Ключевое слово thisJS Ключевое слово letJS КонстантыJS Функция стрелкиJS КлассыJS ОтладчикJS Руководство стиляJS ПрактикаJS Распространенные ошибкиJS ЭффективностьJS Зарезервированные словаJS ВерсииJS Версия ES5JS Версия ES6JS Версия 2016JS Версия 2017JS JSON
Итоги
- «Классическое» объявление функции («Function Declaration») обрабатывается раньше остального кода. Такую функцию можно вызвать до её объявления.
- Функциональные выражения («Function Expression») обрабатываются вместе с остальным кодом. Такую функцию нельзя вызвать до её объявления.
- Функции в JavaScript являются значениями. Их можно присваивать, передавать, создавать в любом месте кода. В отличие от других видов значений функцию можно вызвать для выполнения кода, содержащегося в ее теле.
- Процесс вызова функции, созданной по объявлению («Function Declaration»), не отличается от процесса вызова функции, созданной по выражению («Function Expression»).
- Ссылки на функции могут храниться в переменных.
- Передаваемые при вызове функции аргументы копируются в параметры функции и становятся локальными переменными.
- Функция может возврвщать значение с помощью инструкции .
- После инструкции происходит выход из функции.
- Если оператор вызван без значения, или в теле функции не указан , то её результат равен .
- Функция представляет из себя определённое действие, поэтому в качестве имен функций выбираются глаголы или фразы, начинающиеся с глаголов.
Итого: замыкания
Замыкание – это функция вместе со всеми внешними переменными, которые ей доступны.
Таково стандартное определение, которое есть в Wikipedia и большинстве серьёзных источников по программированию. То есть, замыкание – это функция + внешние переменные.
Тем не менее, в JavaScript есть небольшая терминологическая особенность.
Обычно, говоря «замыкание функции», подразумевают не саму эту функцию, а именно внешние переменные.
Иногда говорят «переменная берётся из замыкания». Это означает – из внешнего объекта переменных.
Что это такое – «понимать замыкания?»
Иногда говорят «Вася молодец, понимает замыкания!». Что это такое – «понимать замыкания», какой смысл обычно вкладывают в эти слова?
«Понимать замыкания» в JavaScript означает понимать следующие вещи:
- Все переменные и параметры функций являются свойствами объекта переменных . Каждый запуск функции создаёт новый такой объект. На верхнем уровне им является «глобальный объект», в браузере – .
- При создании функция получает системное свойство , которое ссылается на , в котором она была создана.
- При вызове функции, куда бы её ни передали в коде – она будет искать переменные сначала у себя, а затем во внешних с места своего «рождения».
В следующих главах мы углубим это понимание дополнительными примерами, а также рассмотрим, что происходит с памятью.