live extended function что это
Несколько интересностей и полезностей от веб-разработчика *
* Надеюсь, ilusha_sergeevich не обвинит меня в плагиате.
Если что, пост переименую.
[Тут была картинка для привлечения внимания]
Всем привет! За время работы постепенно накапливаются наработки, которыми можно было бы поделиться с сообществом. Но ни одна из этих наработок не тянет на большой полноценный пост. Поэтому я собрал все мелочи, что вспомнил, в одной статье: несколько простых опен-сорц проектов, пара советов и находок. Каждый из предложенных скриптов в этой статье поставляется как есть, под лицензией WTFPL (кроме Балалайки). С радостью приму пулл реквесты с исправлением багов или изменениями в README.
donut.js — микро-библиотека, рисующая бубликовые (donut) и круговые диаграммы
Во время работы над очередным проектом, появилась задача нарисовать много информативных бубликов на карте мира, и не просто нарисовать, а еще и поддержать ИЕ8, который, как известно, не умет SVG, а только безобразный VML. Первое, что приходит в голову, это Raphael. Порывшись некоторое время, я нашел это решение. К сожалению, автор проявил изобретательность простым хаком: на круговой диаграмме (pie chart) он нарисовал белый круг. Это решение не подошло, так как дырка бублика должна быть прозрачной. Изучение возможности рисования при помощи Raphael таких диаграмм мне показалось чересчур трудоёмким. Остальные скрипты на просторах интернета мне так же не подошли. Пришлось писать свой костыль, взяв за основу математику рисования арок этого проекта. Арки для VML версии нарисованы используя элемент arc.
Если толщина арки равна половине диаметра бублика, получается круговая диаграмма:
У функции donut есть два метода:
setColor(arc, color) — установка цвета для арки
data(arc[, data]) — получение или установка данных для арки
Для того, чтоб добавить текст, просто вставьте узел с текстом в возвращаемый блок:
Позицию можно задать в CSS или используя свойство style.
Решение работает в восьмом осле. В устаревших ослах не проверялось, но у меня нет причин полагать, что в них не сработает. Скрипт не зависит от наличия других библиотек. Удобную реализацию вставки текста, тултипов и пр. оставляю за вами.
Балалайка
Балалайка — крошечная jQuery-подобная DOM библиотека, ужатая до предела, имеющая малый, но достаточный для vanilla.js-пасанов набор методов. В её основу вложена идея того, что для «получения элемента по ID» не нужно подключать jQuery. Крошечный размер позволяет встраивать её куда угодно.
Например, вместо подключения jQuery, как основную библиотеку:
(похоже на вставку какого-то счетчика, только немного массивнее)
Как известно, обычный GET запрос требует время и немалого трафика. Инлайновая вставка позволит сэкономить драгоценные миллисекунды до полной загрузки страницы.
Или в качестве локальной переменной для вашего скрипта:
Такой, на первый взгляд, скудный набор методов, позволяет делать многое. А из-за простоты кода и ванильных функций, балалайка сильно выигрывает у массивных библиотек в производительности.
Несколько примеров
Поиск одного элемента в другом:
Я не предлагаю отказываться от jQuery, просто не забывайте, что есть микро-библиотека со смешным названием. Балалайка используется во фреймворке Матрешка (статьи о Матрешке)
Библиотека поддерживается всеми браузерами, начиная с ИЕ9.
Ссылка на репозиторий
Функция procrastinate
Представьте себе следующую ситуацию (взята из моей практики). У вас есть форма с некими текстовыми полями: чекбосами и пр. Когда меняется значение одного из элементов формы, приложение должно отправить запрос на сервер, который, в свою очередь, возвращает данные для рендеринга трёх графиков. Рисование графиков — дело тяжелое для процессора и на слабом компьютере занимает полсекунды (Highcharts он такой). Теперь представьте пользователя, которому скучно и он решил многократно кликнуть на чекбокс. Что произойдет? Отправится куча запросов, вернется куча ответов, которые так же многократно отрисуют график. Что обычно делают в таком случае? Отменяют запрос на сервер. Спрашивается: зачем было этот запрос посылать, если можно было дождаться, пока тот угомонится? 🙂
Для решения этой задачи я использовал простейшую функцию (возможно, велосипед), которая принимает другую функцию в качестве аргумента и возвращает её модификацию, которая может быть запущена только однажды за определенный промежуток времени. Пример:
Метод, кроме «прокрастинирующей» функции, принимает задержку, и контекст в качестве аргументов. Задержка отвечает за то, на сколько миллисикунд будет отложен реальный вызов функции при очередной попытке её вызова.
А вот пример случая, когда функция никогда не будет вызвана (для лучшего понимания).
Состояние чекбокса indeterminate
А знали ли вы о том, что чекбокс имеет «среднее» состояние? Я не знал. Таки да, имеет! Оно задаётся исключительно с помощью JavaScript:
А обратиться к чекбоксу из CSS с таким состоянием можно используя псевдокласс :indeterminate.
Подробнее опсевдоклассе
Статья на английском (даже если не знаете языка, код говорит сам за себя)
Противный зловред
Код зловреда был таков:
Как попробовать зловред у себя в браузере? Создайте локально HTML файл со слудующим содержимым:
Откройте страницу в Хроме (в Файерфоксе зловред не такой зловредный) и наслаждайтесь. Когда надоест, удалите или переименуйте файл.
Идея: mousedown вместо click
Совет: меньше используйте CDN
Запрос библиотеки — это сложный HTTP запрос. Разработчики с опытом это знают и кладут весь JS код в один файл с помощью Grunt, r.js или другого сборщика. Это позволит сэкономить время на лишних запросах и, часто, трафик, так как GET даже с ответом 304 весит немало. Кроме этого, CDN может не отвечать. Программисты часто решают это failback-ом, но это плохой вариант, так как браузер не сразу поймет, что сервер лежит, а пользователю прийдется наслаждаться загружающейся страницей несколько драгоценных секунд.
Редактор SuitUp и чистка WYSIWYG от мусора
Есть такой редактор, написанный мной, который сделан в виде jQuery плагина, называется SuitUp. Самым большим минусом редактора явлется отсутствие чистки результирующего HTML от вставляемого форматированного текста, например, из Врода: от стилей, спанов, служебных комментариев и прочей ерунды.
Простейший полифил для addEventListener
Репорт об опечатках на сайте
Есть такая замечательная система оповещений об опечатках на сайте, называемая Orphus. Пользователь, найдя опечатку или неточность может выделить соответствующий текст и нажать Ctrl+Enter. Открывается окно, в котором юзер может ввести комментарий к ошибке, а разработчик, затем, логинясь на сайте Орфуса видит все оповещения и, если хочет, исправляет ошибки. Мне захотелось написать свою оповещалку для опечаток для страницы документации Матрешки. За основу я взял известный хак отправки формы на страницу гуглоформы, который не требует от разработчика поднятия собственного сервера.
Как подключить?
1. Создайте Гугл-форму с тремя полями: текст с ошибкой, комментарий пользователя, ссылка на страницу. Пример.
2. Используя веб инспектор, исследуйте форму. Нам нужно значение атрибута формы action и имена инпутов (атрибут name)
3. Подключите скрипт перед
4. Запустите функцию:
В идеале, скрипту нужно дожидаться готовности DOM и кастомное окошко (у самого руки не доходят). Если это кто-то сделает, бросайте ссылку, с удовольствием форкну 🙂
Скрипт не зависит от наличия сторонних библиотек.
vanillatree — ванильная замена jstree
jstree — jQuery плагин, создающий вложенное дерево списков. Меня этот плагин не устроил сложным, громоздким API. vanillatree — компактная замена jstree, не требующая подключения какой-либо сторонней библиотеки. В список фич входит: кастомное контекстное меню для каждой ветки, выбор ветки открытие или закрытие дерева под веткой по клику (или используя методы), перемещение ветки, удаление ветки и соответствующие события.
Как использовать?
Подключите соответствующий JS и CSS файл и, когда DOM дерево будет готово, создайте инстанс VanillaTree
Первым аргументом должен быть элемент или селектор элемента, вторым — необязательные опции:
placeholder (String) — показывается, когда дерево пустое
contextmenu (Array) — список объектов, отвечающих за контекстное меню (
Обратите внимание, что vanillatree использует Балалайку в качестве «локальной библиотеки». Взгляните на 197 строку.
Урок №163. Виртуальные функции и Полиморфизм
Обновл. 15 Сен 2021 |
На предыдущем уроке мы рассматривали ряд примеров, в которых использование указателей или ссылок родительского класса упрощало логику и уменьшало количество кода.
Виртуальные функции и Полиморфизм
Тем не менее, мы сталкивались с проблемой, когда родительский указатель или ссылка вызывали только родительские методы, а не дочерние. Например:
rParent is a Parent
На этом уроке мы рассмотрим, как можно решить эту проблему с помощью виртуальных функций.
Виртуальная функция в языке С++ — это особый тип функции, которая, при её вызове, выполняет «наиболее» дочерний метод, который существует между родительским и дочерними классами. Это свойство еще известно, как полиморфизм. Дочерний метод вызывается тогда, когда совпадает сигнатура (имя, типы параметров и является ли метод константным) и тип возврата дочернего метода с сигнатурой и типом возврата метода родительского класса. Такие методы называются переопределениями (или «переопределенными методами»).
Чтобы сделать функцию виртуальной, нужно просто указать ключевое слово virtual перед объявлением функции. Например:
rParent is a Child
Рассмотрим пример посложнее:
Как вы думаете, какой результат выполнения этой программы?
Рассмотрим всё по порядку:
Сначала создается объект c класса C.
Вызов rParent.GetName() приводит к вызову A::getName(). Однако, поскольку A::getName() является виртуальной функцией, то компилятор ищет «наиболее» дочерний метод между A и C. В этом случае — это C::getName().
Обратите внимание, компилятор не будет вызывать D::getName(), поскольку наш исходный объект был класса C, а не класса D, поэтому рассматриваются методы только между классами A и C.
Результат выполнения программы:
Более сложный пример
Рассмотрим класс Animal из предыдущего урока, добавив тестовый код:
Результат выполнения программы:
А теперь рассмотрим тот же класс, но сделав метод speak() виртуальным:
Результат выполнения программы:
Matros says Meow
Barsik says Woof
Обратите внимание, мы не сделали Animal::GetName() виртуальной функцией. Это из-за того, что GetName() никогда не переопределяется ни в одном из дочерних классов, поэтому в этом нет необходимости.
Аналогично со следующим примером с массивом животных:
Matros says Meow
Barsik says Woof
Ivan says Meow
Tolik says Woof
Martun says Meow
Tyzik says Woof
Несмотря на то, что эти два примера используют только классы Cat и Dog, любые другие дочерние классы также будут работать с нашей функцией report() и с массивом животных, без внесения дополнительных модификаций! Это, пожалуй, самое большое преимущество виртуальных функций — возможность структурировать код таким образом, чтобы новые дочерние классы автоматически работали со старым кодом, без необходимости внесения изменений со стороны программиста!
Предупреждение: Сигнатура виртуального метода дочернего класса должна полностью соответствовать сигнатуре виртуального метода родительского класса. Если у дочернего метода будет другой тип параметров, нежели у родительского, то вызываться этот метод не будет.
Использование ключевого слова virtual
Если функция отмечена как виртуальная, то все соответствующие переопределения тоже считаются виртуальными, даже если возле них явно не указано ключевое слова virtual. Однако, наличие ключевого слова virtual возле методов дочерних классов послужит полезным напоминанием о том, что эти методы являются виртуальными, а не обычными. Следовательно, полезно указывать ключевое слово virtual возле переопределений в дочерних классах, даже если это не является строго необходимым.
Типы возврата виртуальных функций
Типы возврата виртуальной функции и её переопределений должны совпадать. Рассмотрим следующий пример:
В этом случае Child::getValue() не считается подходящим переопределением для Parent::getValue(), так как типы возвратов разные (метод Child::getValue() считается полностью отдельной функцией).
Не вызывайте виртуальные функции в теле конструкторов или деструкторов
Вот еще одна ловушка для новичков. Вы не должны вызывать виртуальные функции в теле конструкторов или деструкторов. Почему?
Помните, что при создании объекта класса Child сначала создается родительская часть этого объекта, а затем уже дочерняя? Если вы будете вызывать виртуальную функцию из конструктора класса Parent при том, что дочерняя часть создаваемого объекта еще не была создана, то вызвать дочерний метод вместо родительского будет невозможно, так как объект child для работы с методом класса Child еще не будет создан. В таких случаях, в языке C++ будет вызываться родительская версия метода.
Аналогичная проблема существует и с деструкторами. Если вы вызываете виртуальную функцию в теле деструктора класса Parent, то всегда будет вызываться метод класса Parent, так как дочерняя часть объекта уже будет уничтожена.
Правило: Никогда не вызывайте виртуальные функции в теле конструкторов или деструкторов.
Недостаток виртуальных функций
«Если всё так хорошо с виртуальными функциями, то почему бы не сделать все методы виртуальными?» — спросите Вы. Ответ: «Это неэффективно!». Обработка и выполнение вызова виртуального метода занимает больше времени, чем обработка и выполнение вызова обычного метода. Кроме того, компилятор также должен выделять один дополнительный указатель для каждого объекта класса, который имеет одну или несколько виртуальных функций.
Какой результат выполнения следующих программ? Не нужно запускать/выполнять следующий код, вы должны определить результат, без помощи своих IDE.
Почему пора перестать использовать JavaScript IIFE
Немедленно вызываемая функция (Immediately Invoked Function Expression — IIFE) в JavaScript — это конструкция, позволяющая вызывать функцию непосредственно после ее определения.
В этой статье мы разберемся, почему стоит отказаться от использования IIFE, несмотря на ее былые заслуги.
Мы можем объявлять переменные внутри автономных блоков
С тех пор как появился стандарт ES6, мы можем объявлять переменные и константы внутри блока с помощью let и const. Вместе с этим стандартом также появилась возможность выделять переменные и константы в автономные блоки, недоступные извне.
Тогда x не будет доступен извне. Это явно лучше, чем:
Теперь, когда ES6 поддерживают практически все современные браузеры, мы должны прекратить использование IIFE для отделения переменных от внешнего мира. Другой способ изолировать переменные — это модули, с поддержкой которых теперь тоже нет никаких проблем. Пока мы не экспортируем их, они не будут доступны для других модулей.
Мы можем избавиться почти от всех замыканий
Замыкание — это механизм, при котором функция запоминает свои внешние переменные и может получить к ним доступ. Если внутри функции мы создадим еще одну и вернем ее, то возвращаемая функция сможет обращаться к внешним переменным, которые являются внутренними для внешней функции.
Например, здесь мы можем получить некоторые побочные эффекты:
Опять же, теперь нет смысла городить весь этот огород, поскольку у нас есть автономные блоки и модули для изоляции данных. Мы можем просто поместить все это в свой собственный модуль, тогда нам не нужно будет беспокоиться о доступе к данным.
Замыкания вызывают побочные эффекты, что не очень хорошо, так как лучшие практики и здравый смысл предписывают нам избегать их, когда это возможно. Они затрудняют тестирование функций, которые в этом случае не являются чистыми.
Кроме того, не стоит плодить вложенные функции, когда этого можно избежать: с ними код становится более запутанным, чем без них.
Лучшая альтернатива — заменять их на модули:
В приведенном выше коде мы имеем такое же объявление переменной count и экспортируем функцию id (), чтобы она была доступна для других модулей. Так мы скрываем count и открываем id (), как и хотели, только без использования IIFE. В результате мы получаем меньшую вложенность и избавляемся от необходимости определять другую функцию и запускать ее.
Мы можем по-другому создавать алиасы для переменных
Мы могли бы написать такое:
Но теперь нет необходимости использовать IIFE для создания алиасов. Используя модули, мы просто можем импортировать переменную под другим именем, тем самым создавая для нее алиас.
И тогда достаточно написать:
Кроме того, не стоит добавлять объекту window новые свойства, так как это загрязняет глобальную область видимости.
Мы можем легко получить глобальный объект
С появлением globalThis нам не нужно беспокоиться об имени глобального объекта в различных окружениях, поскольку он становится стандартом.
Мы могли бы использовать IIFE для захвата глобального объекта:
Но теперь в этом нет необходимости. Да и раньше можно было обойтись без этого, просто написав следующую строку:
Если быть более точным, то можно написать вот что:
И тогда можно не добавлять дополнительный вызов функции и вложенность, которые появляются при использовании IIFE.
Мы можем проще выполнять минификацию
С модулями JavaScript нам больше не нужно отделять остальной код от IIFE, чтобы выполнить минификацию наших файлов должным образом.
Webpack, Browserify, Parcel, Rollup и так далее могут работать с модулями должным образом, поэтому мы должны использовать их для создания более чистого кода.
Вывод
Пора перестать использовать IIFE в нашем коде. Это добавляет лишние функции и избыточную вложенность.
Кроме того, сейчас это анахронизм: IIFE применялись еще до появления и широкого распространения практики использования модулей в JavaScript. В 2020 году для разделения кода мы должны использовать модули и автономные блоки.
Для предотвращения доступа извне к переменным, находящимся внутри модуля, мы можем использовать блочные области видимости.
Карманная книга по TypeScript. Часть 4. Подробнее о функциях
Я продолжаю серию публикаций адаптированного и дополненного перевода «Карманной книги по TypeScript «.
Обратите внимание: для большого удобства в изучении книга была оформлена в виде прогрессивного веб-приложения.
Функции — это основные строительные блоки любого приложения, будь то функции, импортируемые из другого модуля, или методы класса. В TS существует несколько способов описания того, как фукнции вызываются.
Тип функции в форме выражения (function type expressions)
Простейшим способом описания типа функции является выражение. Такие типы похожи на стрелочные функции:
Обратите внимание: название параметра является обязательным. Тип функции (string) => void означает «функция с параметром string типа any «!
Разумеется, для типа функции можно использовать синоним:
Сигнатуры вызова (call signatures)
В JS функции, кроме того, что являются вызываемыми (callable), могут иметь свойства. Однако, тип-выражение не позволяет определять свойства функции. Для описания вызываемой сущности (entity), обладающей некоторыми свойствами, можно использовать сигнатуру вызова (call signature) в объектном типе:
Сигнатуры конструктора (construct signatures)
Общие функции или функции-дженерики (generic functions)
Часто тип данных, возвращаемых функцией, зависит от типа передаваемого функции аргумента или же два типа возвращаемых функцией значений зависят друг от друга. Рассмотрим функцию, возвращающую первый элемент массива:
В TS общие типы или дженерики (generics) используются для описания связи между двумя значениями. Это делается с помощью определения параметра Type в сигнатуре функции:
Добавив параметр Type и использовав его в двух местах, мы создали связь между входящими данными функции (массивом) и ее выходными данными (возвращаемым значением). Теперь при вызове функции возвращается более конкретный тип:
Предположение типа (inference)
Мы можем использовать несколько параметров типа. Например, самописная версия функции map может выглядеть так:
Ограничения (constraints)
Ограничение, как следует из названия, используется для ограничения типов, принимаемых параметром типа.
Типы longerArr и longerStr были выведены на основе аргументов. Запомните, дженерики определяют связь между двумя и более значениями одного типа!
Работа с ограниченными значениями
Вот пример распространенной ошибки, возникающей при работе с ограничениями дженериков:
Определение типа аргументов
Обычно, TS делает правильные выводы относительно типов аргументов в вызове дженерика, но так бывает не всегда. Допустим, мы реализовали такую функцию для объединения двух массивов:
При обычном вызове данной функции с несовпадающими по типу массивами возникает ошибка:
Руководство по написанию хороших функций-дженериков
Используйте параметры типа без ограничений
Рассмотрим две похожие функции:
Правило: по-возможности, используйте параметры типа без ограничений.
Используйте минимальное количество параметров типа
Вот еще одна парочка похожих функций:
Правило: всегда используйте минимальное количество параметров типа.
Параметры типа должны указываться дважды
Иногда мы забываем, что функция не обязательно должна быть дженериком:
Вот упрощенная версия данной функции:
Запомните, параметры типа предназначены для связывания типов нескольких значений.
Правило: если параметр типа появляется в сигнатуре функции только один раз, то, скорее всего, он вам не нужен.
Опциональные параметры (optional parameters)
Функции в JS могут принимать произвольное количество аргументов. Например, метод toFixed принимает опциональное количество цифр после запятой:
Мы также можем указать «дефолтный» параметр (параметр по умолчанию):
Опциональные параметры в функциях обратного вызова
При написании функций, вызывающих «колбеки», легко допустить такую ошибку:
В действительности, это означает, что колбек может быть вызван с одним аргументом. Другими словами, определение функции говорит, что ее реализация может выглядеть так:
Поэтому попытка вызова такой функции приводит к ошибке:
В JS при вызове функции с большим (ударение на первый слог) количеством аргументов, чем указано в определении фукнции, дополнительные параметры просто игнорируются. TS ведет себя аналогичным образом. Функции с меньшим количеством параметров (одного типа) могут заменять функции с большим количеством параметров.
Правило: при написании типа функции для колбека, не указывайте опциональные параметры до тех пор, пока не будете вызывать функцию без передачи этих параметров.
Перегрузка функции (function overload)
В TS такую функцию можно реализовать с помощью сигнатур перегрузки (overload signatures). Для этого перед телом функции указывается несколько ее сигнатур:
В приведенном примере мы реализовали две перегрузки: одну, принимающую один аргумент, и вторую, принимающую три аргумента. Первые две сигнатуры называются сигнатурами перегрузки.
Затем мы реализовали функцию с совместимой сигнатурой (compatible signature). Функции имеют сигнатуру реализации (implementation signature), но эта сигнатура не может вызываться напрямую. Несмотря на то, что мы написали функцию с двумя опциональными параметрами после обязательного, она не может вызываться с двумя параметрами!
Сигнатуры перегрузки и сигнатура реализации
Предположим, что у нас имеется такой код:
Почему в данном случае возникает ошибка? Дело в том, что сигнатура реализации не видна снаружи (за пределами тела функции). Поэтому при написании перегруженной функции всегда нужно указывать две или более сигнатуры перегрузки перед сигнатурой реализации.
Кроме того, сигнатура реализации должна быть совместима с сигнатурами перегрузки. Например, при вызове следующих функций возникают ошибки, поскольку сигнатура реализации не совпадает с сигнатурами перегрузки:
Правила реализации хороших перегрузок функции
Рассмотрим функцию, возвращающую длину строки или массива:
На первый взгляд кажется, что все в порядке. Мы можем вызывать функцию со строками или массивами. Однако, мы не можем вызывать ее со значением, которое может быть либо строкой, либо массивом, поскольку TS ассоциирует вызов функции с одной из ее перегрузок:
Поскольку обе перегрузки имеют одинаковое количество аргументов и один и тот же тип возвращаемого значения, мы можем реализовать такую «неперегруженную» версию данной функции:
Так намного лучше! Теперь мы можем вызывать функцию с любым значением и, кроме того, нам не нужно предварительно определять правильную сигнатуру реализации функцию.
Правило: по-возможности используйте объединения вместо перегрузок функции.
Определение this в функциях
Обратите внимание: в данном случае мы не можем использовать стрелочную функцию.
Другие типы, о которых следует знать
void представляет значение, возвращаемое функцией, которая ничего не возвращает. Если в теле функции отсутствует оператор return или после этого оператора не указано возвращаемого значения, предполагаемым типом возвращаемого такой функцией значения будет void :
object
unknown
Это бывает полезным для описания типа функции, поскольку таким способом мы можем описать функцию, принимающую любое значение без использования типа any в теле функции. Другими словами, мы можем описать функцию, возвращающую значение неизвестного типа:
never
Некоторые функции никогда не возвращают значений:
Тип never представляет значение, которого не существует. Чаще всего, это означает, что функция выбрасывает исключение или останавливает выполнение программы.
never также появляется, когда TS определяет, что в объединении больше ничего не осталось:
Function
Оставшиеся параметры и аргументы
Оставшиеся параметры (rest parameters)
Кроме использования опциональных параметров или перегрузок для создания функций, принимающих разное или фиксированное количество аргументов, мы можем определять функции, принимающие произвольное количество аргументов с помощью синтаксиса оставшихся параметров.
Оставшиеся аргументы (rest arguments)
Синтаксис распространения (синонимы: расширение, распаковка) (spread syntax) позволяет передавать произвольное количество элементов массива. Например, метод массива push принимает любое количество аргументов:
Обратите внимание: TS не считает массивы иммутабельными. Это может привести к неожиданному поведению:
Самым простым решением данной проблемы является использование const :
Деструктуризация параметров (parameter destructuring)
Деструктуризация параметров используется для распаковки объекта, переданного в качестве аргумента, в одну или более локальную переменную в теле функции. В JS это выглядит так:
Аннотация типа для объекта указывается после деструктуризации:
Для краткости можно использовать именованный тип:
Возможность присвоения функций переменным
Использование void в качестве типа возвращаемого функцией значения может приводить к необычному, но вполне ожидаемому поведению.
Все приведенные ниже реализации типа () => void являются валидными:
Когда возвращаемое любой из этих функций значение присваивается переменной, она будет хранить тип void :
Поэтому следующий код является валидным, несмотря на то, что Array.prototype.push возвращает число, а Array.prototype.forEach ожидает получить функцию с типом возвращаемого значения void :
Облачные серверы от Маклауд быстрые и безопасные.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!