Секреты javascript-функций
Содержание:
- Решение 2: привязать контекст с помощью bind
- Сравнение с Function Declaration
- Создание функций
- Потеря «this»
- Встроенные Javascript функции
- Другие варианты применения: ?.(), ?.[]
- Области видимости
- Замыкание
- Summary
- Использование выражений с функциями
- Functions are Objects
- Ключевое слово «this» в методах
- Доступ ко внешним переменным
- 8) getElementsByClass()
- Область видимости переменных. Javascript глобальные и локальные переменные в функции
- Конструкторы
- addEventListener
- Стрелочные функции или лямбда выражения в javascript
Решение 2: привязать контекст с помощью bind
В современном JavaScript у функций есть встроенный метод bind, который позволяет зафиксировать .
Базовый синтаксис :
Результатом вызова является особый «экзотический объект» (термин взят из спецификации), который вызывается как функция и прозрачно передаёт вызов в , при этом устанавливая .
Другими словами, вызов подобен вызову с фиксированным .
Например, здесь передаёт вызов в , фиксируя :
Здесь – это «связанный вариант» , с фиксированным .
Все аргументы передаются исходному методу как есть, например:
Теперь давайте попробуем с методом объекта:
В строке мы берём метод и привязываем его к . Теперь – это «связанная» функция, которая может быть вызвана отдельно или передана в (контекст всегда будет правильным).
Здесь мы можем увидеть, что исправляет только , а аргументы передаются как есть:
Удобный метод:
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
Некоторые JS-библиотеки предоставляют встроенные функции для удобной массовой привязки контекста, например в lodash.
Сравнение с Function Declaration
«Классическое» объявление функции, о котором мы говорили до этого, вида , называется в спецификации языка «Function Declaration».
- Function Declaration – функция, объявленная в основном потоке кода.
- Function Expression – объявление функции в контексте какого-либо выражения, например присваивания.
Несмотря на немного разный вид, по сути две эти записи делают одно и то же:
Оба этих объявления говорят интерпретатору: «объяви переменную , создай функцию с указанными параметрами и кодом и сохрани её в «.
Основное отличие между ними: функции, объявленные как Function Declaration, создаются интерпретатором до выполнения кода.
Поэтому их можно вызвать до объявления, например:
А если бы это было объявление Function Expression, то такой вызов бы не сработал:
Это из-за того, что JavaScript перед запуском кода ищет в нём Function Declaration (их легко найти: они не являются частью выражений и начинаются со слова ) и обрабатывает их.
А Function Expression создаются в процессе выполнения выражения, в котором созданы, в данном случае – функция будет создана при операции присваивания
Как правило, возможность Function Declaration вызвать функцию до объявления – это удобно, так как даёт больше свободы в том, как организовать свой код.
Можно расположить функции внизу, а их вызов – сверху или наоборот.
В некоторых случаях «дополнительное удобство» Function Declaration может сослужить плохую службу.
Например, попробуем, в зависимости от условия, объявить функцию по-разному:
Function Declaration при видны только внутри блока, в котором объявлены. Так как код в учебнике выполняется в режиме , то будет ошибка.
А что, если использовать Function Expression?
Или даже так:
Оба этих варианта работают правильно, поскольку, в зависимости от условия, создаётся именно та функция, которая нужна.
Взглянем ещё на один пример – функцию с тремя параметрами:
- Строка-вопрос
- Функция
- Функция
Она выводит вопрос на подтверждение и, в зависимости от согласия пользователя, вызывает функцию или :
Какой-то очень простой код, не правда ли? Зачем, вообще, может понадобиться такая ?
…Оказывается, при работе со страницей такие функции как раз очень востребованы, только вот спрашивают они не простым , а выводят более красивое окно с вопросом и могут интеллектуально обработать ввод посетителя. Но это всё потом, когда перейдём к работе с интерфейсом.
Здесь же обратим внимание на то, что то же самое можно написать более коротко:
Здесь функции объявлены прямо внутри вызова , даже без присвоения им имени.
Функциональное выражение, которое не записывается в переменную, называют анонимной функцией.
Действительно, зачем нам записывать функцию в переменную, если мы не собираемся вызывать её ещё раз? Можно просто объявить непосредственно там, где функция нужна.
Такого рода код возникает естественно, он соответствует «духу» JavaScript.
Создание функций
Существует 3 способа создать функцию. Основное отличие в результате их работы — в том, что именованная функция видна везде, а анонимная — только после объявления:
function имя(параметры) { … } |
var имя = function(параметры) { … } … var имя = new Function(параметры, ‘…’) |
Именованные функции доступны везде в области видимости | Анонимные — доступны только с момента объявления. Синтаксис используется редко, в основном для получения функции из текста, например, динамически загруженного с сервера в процессе выполнения скриптов. |
/* функция sum определена ниже */ var a = sum(2,2) function sum(x,y) { return x+y } |
/* будет ошибка, т.к sum еще не существует */ var a = sum(2,2) var sum = function(x,y) { return x+y } |
Потеря «this»
Мы уже видели примеры потери . Как только метод передаётся отдельно от объекта – теряется.
Вот как это может произойти в случае с :
При запуске этого кода мы видим, что вызов возвращает не «Вася», а !
Это произошло потому, что получил функцию отдельно от объекта (именно здесь функция и потеряла контекст). То есть последняя строка может быть переписана как:
Метод в браузере имеет особенность: он устанавливает для вызова функции (в Node.js становится объектом таймера, но здесь это не имеет значения). Таким образом, для он пытается получить , которого не существует. В других подобных случаях обычно просто становится .
Задача довольно типичная – мы хотим передать метод объекта куда-то ещё (в этом конкретном случае – в планировщик), где он будет вызван. Как бы сделать так, чтобы он вызывался в правильном контексте?
Встроенные Javascript функции
В javascript достаточно много функций, встроенных в синтаксис языка. Рассмотрим одну из них.
eval(строка)
Функция вычисляет выражение в указанной строке (в качестве параметра); выражение должно быть составлено по правилам языка JavaScript и не содержать тегов HTML:
eval("5 * 7 + 1 / 2")
Рассмотрим пример использования функции eval:
Пример: Рассмотреть пример работы кода с функцией eval.
1 2 3 |
var y = 5; // значение у равно 5 var x = "if (y==5) y*2-3"; // значение х равно строке символов var rezult = eval(x); // rezult равно 7 |
Задание js4_1. В скрипте запрашивать у пользователя ввести в диалоговое окно ввода математическое выражение, затем вычислить значение данного выражения и результат вывести в диалоговом окне.
Другие варианты применения: ?.(), ?.[]
Опциональная цепочка — это не оператор, а специальная синтаксическая конструкция, которая также работает с функциями и квадратными скобками.
Например, используется для вызова потенциально несуществующей функции.
В следующем примере не у всех пользователей есть метод :
В обоих вызовах сначала используем точку (), чтобы получить свойство , потому что объект пользователя точно существует, к нему можно обратиться без какой-либо ошибки.
Затем уже проверяет левую часть: если функция существует, то она выполнится (это так для ). Иначе (для ) вычисление остановится без ошибок.
Также существует синтаксис , если значение свойства требуется получить с помощью квадратных скобок , а не через точку . Как и в остальных случаях, такой способ позволяет защититься от ошибок при доступе к свойству объекта, которого может не быть.
Кроме этого, можно совместно использовать с :
Можно использовать для безопасного чтения и удаления, но не для записи
Опциональная цепочка не имеет смысла в левой части присваивания.
Например:
Она недостаточно «умна» для этого.
Области видимости
Каждая функция, точнее даже каждый запуск функции задает свою индивидуальную область видимости.
Переменные можно объявлять в любом месте. Ключевое слово задает переменную в текущей области видимости. Если его забыть, то переменная попадет в глобальный объект . Возможны неожиданные пересечения с другими переменными окна, конфликты и глюки.
В отличие от ряда языков, блоки не задают отдельную область видимости. Без разницы — определена переменная внутри блока или вне его. Так что эти два фрагмента совершенно эквивалентны:
Заданная через переменная видна везде в области видимости, даже до оператора . Для примера сделаем функцию, которая будет менять переменную, для которой находится ниже.
Например:
function a() { z = 5 // поменяет z локально.. // .. т.к z объявлена через var var z } // тест delete z // очистим на всякий случай глобальную z a() alert(window.z) // => undefined, т.к z была изменена локально
Замыкание
Обычно функция запоминает, где родилась, в специальном свойстве . Это ссылка на лексическое окружение (Lexical Environment), в котором она создана (мы разбирали это в главе Замыкание).
Но когда функция создаётся с использованием , в её записывается ссылка не на внешнее лексическое окружение, в котором она была создана, а на глобальное. Поэтому такая функция имеет доступ только к глобальным переменным.
Сравним это с обычным объявлением:
Эта особенность выглядит странно, но оказывается очень полезной на практике.
Представьте, что нужно создать функцию из строки. Код этой функции неизвестен во время написания скрипта (поэтому не используем обычные функции), а будет определён только в процессе выполнения. Мы можем получить код с сервера или с другого ресурса.
Наша новая функция должна взаимодействовать с основным скриптом.
Что если бы она имела доступ к внешним переменным?
Проблема в том, что перед отправкой JavaScript-кода на реальные работающие проекты код сжимается с помощью минификатора – специальной программы, которая уменьшает размер кода, удаляя комментарии, лишние пробелы, и, что самое главное, локальным переменным даются укороченные имена.
Например, если в функции объявляется переменная , то минификатор изменяет её на (или другую букву, если она не занята) и изменяет её везде. Обычно так делать безопасно, потому что переменная является локальной, и никто снаружи не имеет к ней доступ. И внутри функции минификатор заменяет каждое её упоминание. Минификаторы достаточно умные. Они не просто осуществляют «тупой» поиск-замену, они анализируют структуру кода, и поэтому ничего не ломается.
Так что если бы даже и имела доступ к внешним переменным, она не смогла бы найти переименованную .
Если бы имела доступ к внешним переменным, при этом были бы проблемы с минификаторами.
Кроме того, такой код был бы архитектурно хуже и более подвержен ошибкам.
Чтобы передать что-то в функцию, созданную как , можно использовать её аргументы.
Summary
A function declaration looks like this:
- Values passed to a function as parameters are copied to its local variables.
- A function may access outer variables. But it works only from inside out. The code outside of the function doesn’t see its local variables.
- A function can return a value. If it doesn’t, then its result is .
To make the code clean and easy to understand, it’s recommended to use mainly local variables and parameters in the function, not outer variables.
It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side-effect.
Function naming:
- A name should clearly describe what the function does. When we see a function call in the code, a good name instantly gives us an understanding what it does and returns.
- A function is an action, so function names are usually verbal.
- There exist many well-known function prefixes like , , , and so on. Use them to hint what a function does.
Functions are the main building blocks of scripts. Now we’ve covered the basics, so we actually can start creating and using them. But that’s only the beginning of the path. We are going to return to them many times, going more deeply into their advanced features.
Использование выражений с функциями
Обычное использование javascript функции:
1 2 3 4 5 6 7 |
function sum(arg1,arg2) { var a=arg1+arg2; return a; } var b=sum(1,2); alert(b); |
Функция как составная часть выражения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function sum(arg1,arg2) { var a=arg1+arg2; return a; } var b=sum(1,2); alert(b); function add() { var c=1+sum(1,2); return c; } var d=add(); alert(d); |
Во второй функции () используется выражение, ссылающееся на первую функцию (, в 11-й строке).
Задание js4_6. Создать функцию, возвращающую наибольшее из трех чисел. Аргументами функции являются сами числа.
Фрагмент кода:
function findMax(a,b,c){ ... return ... } var max = ... ... |
Другие варианты использования выражений с функциями
(только для функций, которые возвращают результат):
Рассмотрим примеры для функции:
1 2 3 4 |
function plRectangle(width, height){ var S = width * height; return S } |
Варианты выражений:
⇒ Вызов функции как часть выражения:
1 |
S3 = 0.5 * plRectangle(a, b); |
⇒ Вызов функции в логических выражениях:
1 2 |
if (plRectangle(a, b) > plRectangle(c, d)) alert("Первый прямоугольник больше второго"); |
⇒ Вызов javascript функции в качестве параметра другой функции:
1 2 3 |
var х = "25рх"; var у = 12; var S = plRectangle(parselnt(x), у); |
Задание js4_7. Используйте функцию для модернизации с окончанием слова про количество ворон. Откройте файл с выполненным заданием про ворон. Необходимо вызывать функцию с параметром – количество ворон.
Задание js4_8. Создайте функцию для расчета степени введенного числа.
Вопросы для самоконтроля:
- Что такое аргументы функции и где они указываются?
- Может ли на месте аргумента функции находиться вызов другой функции?
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.
Ключевое слово «this» в методах
Как правило, методу объекта необходим доступ к информации, которая хранится в объекте, чтобы выполнить с ней какие-либо действия (в соответствии с назначением метода).
Например, коду внутри может понадобиться имя пользователя, которое хранится в объекте .
Для доступа к информации внутри объекта метод может использовать ключевое слово .
Значение – это объект «перед точкой», который использовался для вызова метода.
Например:
Здесь во время выполнения кода значением будет являться (ссылка на объект ).
Технически также возможно получить доступ к объекту без ключевого слова , ссылаясь на него через внешнюю переменную (в которой хранится ссылка на этот объект):
…Но такой код будет ненадёжным. Если мы решим скопировать ссылку на объект в другую переменную, например, , и перезапишем переменную чем-то другим, тогда будет осуществлён доступ к неправильному объекту при вызове метода из .
Это показано ниже:
Если мы используем вместо внутри , тогда этот код будет работать.
Доступ ко внешним переменным
Из функции мы можем обратиться не только к локальной переменной, но и к внешней:
Интерпретатор, при доступе к переменной, сначала пытается найти переменную в текущем , а затем, если её нет – ищет во внешнем объекте переменных. В данном случае им является .
Такой порядок поиска возможен благодаря тому, что ссылка на внешний объект переменных хранится в специальном внутреннем свойстве функции, которое называется
Это свойство закрыто от прямого доступа, но знание о нём очень важно для понимания того, как работает JavaScript
При создании функция получает скрытое свойство , которое ссылается на лексическое окружение, в котором она была создана.
В примере выше таким окружением является , так что создаётся свойство:
Это свойство никогда не меняется. Оно всюду следует за функцией, привязывая её, таким образом, к месту своего рождения.
При запуске функции её объект переменных получает ссылку на «внешнее лексическое окружение» со значением из .
Если переменная не найдена в функции – она будет искаться снаружи.
Именно благодаря этой механике в примере выше выводит внешнюю переменную. На уровне кода это выглядит как поиск во внешней области видимости, вне функции.
Если обобщить:
- Каждая функция при создании получает ссылку на объект с переменными, в контексте которого была создана.
- При запуске функции создаётся новый объект с переменными . Он получает ссылку на внешний объект переменных из .
- При поиске переменных он осуществляется сначала в текущем объекте переменных, а потом – по этой ссылке.
Выглядит настолько просто, что непонятно – зачем вообще говорить об этом , об объектах переменных. Сказали бы: «Функция читает переменные снаружи» – и всё. Но знание этих деталей позволит нам легко объяснить и понять более сложные ситуации, с которыми мы столкнёмся далее.
8) getElementsByClass()
Изначально не написана никем конкретно. Многие разработчики писали свои собственные версии и ничья не показала себя лучше остальных.
Следующая функция использует встроенный метод , если он есть, и ищет элементы самостоятельно в тех браузерах, где этого метода нет.
if(document.getElementsByClassName) { getElementsByClass = function(classList, node) { return (node || document).getElementsByClassName(classList) } } else { getElementsByClass = function(classList, node) { var node = node || document, list = node.getElementsByTagName('*'), length = list.length, classArray = classList.split(/\s+/), classes = classArray.length, result = [], i,j for(i = 0; i < length; i++) { for(j = 0; j < classes; j++) { if(list.className.search('\\b' + classArray + '\\b') != -1) { result.push(list) break } } } return result } }
- classList
- Список классов, разделенный пробелами, элементы с которыми нужно искать.
- node
- Контекст поиска, внутри какого узла искать
Например:
var div = document.getElementById("mydiv") elements = getElementsByClass('class1 class2', div)
Область видимости переменных. Javascript глобальные и локальные переменные в функции
Область видимости переменной — область кода, в котором переменная доступна для использования.
- Глобальные переменные — создаются на уровне сценария и сохраняются до конца сценария;— объявляются до описания javascript функции:
var a = 1; function ... ... |
— могут быть причиной сложно находимых ошибок;
Локальные переменные
— создаются внутри фрагментов кода и не видны извне;
for (var i=1;i<5;i++){ // i - локальная ... } if (x<5) { var a=5; // a - локальная } |
— явно объявляются в теле javascript функции;
function findA(){ var a = 2*2 // a - локальная } |
— аргументы (параметры) функции — всегда локальные переменные;
— лучше использовать локальные переменные, так как доступ к ним больше контролируется.
Задание js4_9. Дополните код согласно заданию:
Создать 2 переменные глобальной и локальной области видимости (то есть внутри функции ) с именами: , .
Переменной присвоить текст “Привет, ”, а — “Мир”. Используя переменные, выведите их значения дважды: в основной программе и в теле функции.
function func() { } func(); |
Область видимости переменных
Рассмотрим конкретные примеры области видимости переменных в javascript при использовании глобальных и локальных переменных.
-
1 2 3 4 5 6 7 8
var S = 2; // Глобальная переменная S function plRectangle(width, height){ var S = width * height; return S // Локальная переменная S } z = plRectangle(2, 3); alert(z); alert(S);
Пример: Значение равно 6, а значение осталось равным 2, то есть значению глобальной переменной, определенной во внешней программе
-
1 2 3 4 5 6 7 8 9 10 11
function plRectangle(width, height) { var s = width * height; // аргументы всегда локальны width = width + 10; return s } width = 2; height = 3; z = plRectangle(width, height); alert(z); alert(width);
Пример: Значение равно 6; значение переменной равно 2, то есть осталось без изменений
-
1 2 3 4 5 6 7 8 9 10
var S = 2; // Глобальная переменная S function plRectangle(width, height) { S = width * height; // заменяем глобальную переменную: return S // S - глобальная переменная (т.к. без определения var) } var z = plRectangle(2, 3); alert(z); alert(S);
Пример: Значения и и равны 6; — глобальная переменная
-
1 2 3 4 5 6 7 8
function Plrectangle(width, height){ S = width * height; //глобальная переменная return S } z = Plrectangle(2, 3); S=2; // изменяем глобальную переменную alert(z); alert (S);
Пример: Значение равно 6, а значение равно 2, то есть значению измененной глобальной переменной, определенной во внешней программе
-
1 2 3 4 5 6 7 8 9 10
function plRectangle(width, height) { var S = width * height; var x = 17; return S } z = plRectangle(2,3); alert(z); alert(x); // не определена во внешней программе alert (S); // не определена во внешней программе
Пример: Значение равно 6; переменная во внешней программе не определена; переменная во внешней программе не определена
Задание js4_10. Что выведет на экран следующий код?
1 2 3 4 5 6 7 |
var variable = "Глобальная переменная"; function f() { var variable = "Локальная переменная"; document.write(variable + "<br/>"); } f(); document.write(variable); |
Вопросы для самоконтроля:
- Какова разница между локальными и глобальными переменными?
- Зачем в программировании существует необходимость деления переменных на локальные и глобальные?
Конструкторы
Если добавить перед функцией ключевое слово «new», то получим конструктор. Он позволяет создавать экземпляры объекта, что является основой объектно-ориентированного программирования. Рассмотрим пример с созданием экземпляров «Фрукт».
function Fruit(){ var name, color; // наименование и цвет this.getName = function(){return name;}; this.setName = function(value){name=value}; this.getColor = function(){return color;}; this.setColor = function(value){color=value}; } var apple = new Fruit(); apple.setName("Шафран"); apple.setColor("Красный"); var orange = new Fruit(); orange.setName ("Валенсийские апельсины"); orange.setColor ("Оранжевый"); console.log(orange.getName()); // "Валенсийские апельсины" console.log(apple.getName()); // "Шафран" console.log(orange.getColor()); // "Оранжевый"
addEventListener
Фундаментальный недостаток описанных выше способов назначения обработчика –- невозможность повесить несколько обработчиков на одно событие.
Например, одна часть кода хочет при клике на кнопку делать её подсвеченной, а другая – выдавать сообщение.
Мы хотим назначить два обработчика для этого. Но новое DOM-свойство перезапишет предыдущее:
Разработчики стандартов достаточно давно это поняли и предложили альтернативный способ назначения обработчиков при помощи специальных методов и . Они свободны от указанного недостатка.
Синтаксис добавления обработчика:
- Имя события, например .
- Ссылка на функцию-обработчик.
- Дополнительный объект со свойствами:
- : если , тогда обработчик будет автоматически удалён после выполнения.
- : фаза, на которой должен сработать обработчик, подробнее об этом будет рассказано в главе Всплытие и погружение. Так исторически сложилось, что может быть , это то же самое, что .
- : если , то указывает, что обработчик никогда не вызовет , подробнее об этом будет рассказано в главе Действия браузера по умолчанию.
Для удаления обработчика следует использовать :
Удаление требует именно ту же функцию
Для удаления нужно передать именно ту функцию-обработчик которая была назначена.
Вот так не сработает:
Обработчик не будет удалён, т.к
в передана не та же функция, а другая, с одинаковым кодом, но это не важно
Вот так правильно:
Обратим внимание – если функцию обработчик не сохранить где-либо, мы не сможем её удалить. Нет метода, который позволяет получить из элемента обработчики событий, назначенные через
Метод позволяет добавлять несколько обработчиков на одно событие одного элемента, например:
Как видно из примера выше, можно одновременно назначать обработчики и через DOM-свойство и через . Однако, во избежание путаницы, рекомендуется выбрать один способ.
Обработчики некоторых событий можно назначать только через
Существуют события, которые нельзя назначить через DOM-свойство, но можно через .
Например, таково событие , которое срабатывает, когда завершена загрузка и построение DOM документа.
Так что более универсален. Хотя заметим, что таких событий меньшинство, это скорее исключение, чем правило.
Стрелочные функции или лямбда выражения в javascript
В javascript возможно использование так называемых лямбда выражений: функции, которые сокращают запись выражения. В записи такой функции используется стрелочка , отсюда и название «стрелочные функции». Стрелочку можно трактовать, как «такое, что»:
Пример стрелочной функции с одним аргументом:
var f = x => x + 1; alert(f(5)) |
Расшифровываем так:
функция f равна значению x, такое, что x = x + 1.
То есть в результате в окно выведется 6.
Пример стрелочной функции с двумя аргументами:
var g = (x, y) => x + y; alert(g(5,3)) |
Решение:
Результатом работы скрипта будет вывод числа 8.
Пример: Написать стрелочную функцию для вычисления факториала числа. Функция должна быть рекурсивной.
Решение:
- Назовем функцию factorial.
- Определим рекурсивные правила:
n = factorial(n-1), при n > 0 n = 1, при n = 1
Теперь данные правила опишем в самой рекурсивной функции в скрипте. Для написания функции будем использовать :
var factorial = n => (n > ) ? n * factorial(n-1) 1; |
Выведем значение, например, для 4!:
alert(factorial(4)) |
Протестируйте функцию на других значениях.
Пример: Написать стрелочную функцию для вычисления первых n чисел ряда Фибоначчи. Функция должна быть рекурсивной.
Решение:
- Назовем функцию fib.
- Определим рекурсивные правила:
fib = fib(n-1) + fib(n-2), при n > 2 fib = 1, при n
Теперь данные правила опишем в самой рекурсивной функции в скрипте. Для написания функции будем использовать :
var fib = n => (n > 2) ? fib(n-1) + fib(n-2) 1; |
Выведем значение, например, для первых 6 чисел ряда (результат: 5 + 3 = 8):
alert(fib(6)) |
Протестируйте функцию на других значениях.
Задание js4_13:
Написать лямбда-выражение для вычисления квадрата числа.
Например:
alert(kvadr(5)) // результат должен быть 25
Задание js4_14:
Используя стрелочную функцию, создайте последовательность 1 3 9 27 81 243 729 2187 6561 19683
Вопросы для самоконтроля:
- Какие функции называются стрелочными функциями?
- Есть ли разница между стрелочными функциями и лямбда выражениями в javascript?
- Каков синтаксис стрелочных функций?