Defront — про фронтенд-разработку и не только
24.9K subscribers
21 photos
1.09K links
Ламповый канал про фронтенд и не только. Всё самое полезное для опытных web-разработчиков

Обсуждение постов @defrontchat

Также советую канал @webnya
Download Telegram
Андреа Гиммарачи написал статью о том, почему некорректно называть JavaScript-классы синтаксическим сахаром для прототипного наследования — "JS classes are not 'just syntactic sugar'".

В статье говорится о том, что хотя многие фичи JavaScript-классов можно эмулировать в синтаксисе ES5, они будут уступать в скорости и безопасности. Кроме того существуют вещи, которые нельзя эмулировать с помощью традиционного прототипного наследования. Например, с помощью обычных функций нельзя отнаследоваться от встроенных типов, также невозможно полностью эмулировать поведение super().

Таким образом использование понятия "синтаксический сахар" по отношению к классам может привести к неправильным выводам. Принцип прототипного наследования на функциях и классах похож, но это не одно и то же.

#js

https://webreflection.medium.com/js-classes-are-not-just-syntactic-sugar-28690fedf078
https://www.reddit.com/r/javascript/comments/mq9upa/js_classes_are_not_just_syntactic_sugar/ (обуждение на Reddit)
Николас Закас написал статью про ленивый доступ к свойствам объекта — "The lazy-loading property pattern in JavaScript".

Если в объекте есть свойство, значением которого является результат выполнения тяжёлого вычисления, то имеет смысл отложить это вычисление до того момента, пока не произойдёт обращение к свойству. Николас предлагает использовать паттерн, который позволяет не только откладывать вычисление, но и кеширует результат его выполнения:

const object = {
get data() {
const actualData = someExpensiveComputation();

Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false,
enumerable: false
});

return actualData;
}
};


Этот подход можно использовать с любыми объектами и классами.

#js #performance

https://humanwhocodes.com/blog/2021/04/lazy-loading-property-pattern-javascript/
Карло Пиовесан — разработчик Cheerp — попробовал выяснить, можно ли в автоматическом режиме преобразовать JavaScript-код в эквивалентный более производительный JavaScript-код и поделился результатами своего исследования в статье "A JavaScript optimizing compiler".

Карло написал очень ограниченный транспилятор из JavaScript в C++. Прогнал через него код бенчмарков. Затем получившийся C++-код скомпилировал обратно в JavaScript-код с помощью Cheerp с применением оптимизаций на уровне компилятора (Cheerp — это компилятор, построенный на базе LLVM, аналог Emscripten).

В результате такого преобразования кода, большинство бенчмарков показали прирост производительности. Особенно хорошо показал себя код для проверки простых чисел — он стал быстрее на 80%.

Этот подход нельзя использовать в продакшене, так как транспилятор из JavaScript в C++ очень хрупкий и покрывает только небольшое подмножество JavaScript. Тем не менее проведённый эксперимент говорит о том, что у этой стратегии оптимизации есть право на жизнь.

#experimental #js #performance

https://medium.com/leaningtech/a-javascript-optimizing-compiler-3fd3f49bd071
Том МакРайт рассказал о своём подходе к работе с зависимостями — "Vendor by default".

Том использует вендоринг для небольших зависимостей, то есть копирует их исходный код к себе в проект. При копировании кода он его читает, рефакторит и удаляет неиспользуемые части. Такой подход позволяет лучше разобраться в библиотеке и понять её ограничения. Иногда бывает так, что он начинает рефакторить код зависимости и понимает, что будет проще написать её самому или что нужно найти альтернативу.

Интересная статья. Похоже на какую-то крайность, но почему бы и нет, если это решает проблему раздувания бандла приложения.

#npm #js #musings

https://macwright.com/2021/03/11/vendor-by-default.html
Лин Кларк опубликовала статью, посвящённую оптимизации работы JavaScript-кода в WebAssembly-окружении — “Making JavaScript run fast on WebAssembly”.

Некоторые платформы ограничивают набор оптимизаций, которые могут быть применены к JavaScript-коду. Например, на iOS и игровых консолях нельзя использовать JIT-компилятор, поэтому JS-движки там могут работать только в режиме интерпретатора. Это ограничивает спектр задач, который может быть решён с помощью JS. Участники Bytecode Alliance (Fastly, Mozilla, Igalia) работают над решением на базе WebAssembly, которое позволит ускорить выполнение JS-кода на таких платформах и достичь уровня производительности JIT-компиляторов ранних версий JavaScript-движков.

В разрабатываемом решении в качестве первой оптимизации будет использоваться снапшот памяти, который позволит сократить время инициализации программы до нескольких микросекунд. В качестве второй оптимизации будет использоваться AOT-компиляция с генерацией стабов для внутренних кешей.

В статье говорится о том, что подобный подход может использоваться не только с JavaScript, но и с Python, Ruby, Lua.

#js #internals #webassembly

https://bytecodealliance.org/articles/making-javascript-run-fast-on-webassembly
Сегодня Стэфан Джудис твитнул про то, что в Chrome 91 появилась поддержка JSON-модулей. Это новая фича JavaScript, с помощью которой можно использовать import с JSON-файлами. Твит Стэфана дополнил Аксель Раушмайер ссылкой на статью про Import Assertions.

Синтаксис для импорта JSON немного отличается от стандартного импорта:

// статический импорт
import config from './data/config.json' assert { type: 'json' };

// динамический импорт
import('./data/config.json', { assert: { type: 'json' } })


Добавление assert декларирует намерение разработчика импортировать файл заданного типа. Это нужно делать явно, потому что на расширение имени файла в мире веба полагаться нельзя.

Import Assertions находится в статусе пропозала на stage 3. Он открывает дорогу для импорта не только JSON, но и WebAssembly-модулей и CSS-файлов.

#js #proposal #chrome

https://2ality.com/2021/01/import-assertions.html
Мэттью Гауде — разработчик SpiderMonkey — написал статью про опыт имплементации приватных полей класса в JavaScript-движке — "Implementing Private Fields for JavaScript".

Мэттью пишет о том, что при добавлении новой фичи в движок нужно учитывать три аспекта: ментальную модель, спецификацию и реализацию. Иногда они совпадают друг с другом, и имплементация сводится к пошаговой реализации алгоритма из спецификации. Иногда они расходятся, и имплементация начинает сильно отличаться от спецификации, сохраняя только семантику. Реализация приватных полей попала во вторую категорию.

Также в статье разбираются нюансы работы c приватными полями. Оказывается, приватные поля могут быть добавлены к любому объекту, даже если он был явно закрыт от изменений с помощью Oblect.seal(). Насколько я понимаю, это "побочный эффект" спецификации, и его не стоит использовать для решения своих задач.

Очень интересная статья. Рекомендую почитать.

#js #internals #spec #firefox

https://www.mgaudet.ca/technical/2021/5/4/implementing-private-fields-for-javascript
Анализ JS-бандла с помощью Lighthouse Treemap

Сиа Кармаленгос написала статью про новый инструмент анализа JS-бандлов, доступный в Lighthouse — "Explore JavaScript Dependencies With Lighthouse Treemap".

В последних версиях Lighthouse появилась новая фича — визуализация JS-бандлов с помощью Treemap. Если вы знакомы с webpack-bundle-analyzer, то уже можете представить себе новый инструмент. Основное отличие Treemap в Lighthouse — возможность анализа бандлов любых сборщиков. Если сборка происходит с генерацией сорс-мапов, то в Treemap будут отображаться названия модулей. Но самая интересная функция — оценка покрытия кода. Если включить опцию "unused bytes", то можно оценить сколько кода было загружено, но не выполнено.

Поддержка Lighthouse Treemap уже доступна в сервисе PageSpeed Insights, Lighthouse Node CLI и Chrome Canary.

#tool #js #bundle #performance

https://sia.codes/posts/lighthouse-treemap/
Влияние потребления памяти на производительность

Тим Кадлек попробовал сформировать критерии оптимального потребления памяти страницы — "Benchmarking JavaScript Memory Usage".

В Chrome 83 появилась возможность измерения потребляемой памяти страницы с помощью метода performance.measureMemory. В январе 2021 года он был переименован в performance.measureUserAgentSpecificMemory. Несмотря на то что API существует уже довольно долго, разработчики ещё не полностью понимают, как его использовать для анализа производительности страницы. Основная проблема — недостаток данных, чтобы понимать какой объём потребляемой памяти считать приемлемым, а какой избыточным.

Тим пытается ответить на этот вопрос с помощью сбора метрик с 10000 популярных сайтов и распределения результатов по перцентилям. В итоге он предложил следующее распределение:
— хорошо: <4.8MB
— нуждается в улучшении: >4.8MB и <19.4MB
— плохо: >19.4MB

Также в статье есть исследование влияния используемых JS-фреймворков на объём потребляемой памяти. Больше всего памяти потребляют React и Angular. Это объясняется бо́льшим количеством доставляемого кода по сравнению с Vue и jQuery.

#js #performance

https://blog.webpagetest.org/posts/benchmarking-javascript-memory-usage/
Partytown — запуск сторонних скриптов в веб-воркере

Автор фреймворка Ioinic Адам Бредли представил новую библиотеку для запуска сторонних скриптов в веб-воркере — "Introducing Partytown: Run Third-Party Scripts From a Web Worker".

Разработчики JS-фреймворков вкладывают много сил, чтобы пользовательские приложения были быстрыми и отзывчивыми. Эти усилия могут нивелировать сторонние скрипты. Например, добавление на сайт Google аналитики срезает 20 баллов производительности в Lighthouse, так как увеличивается время инициализации страницы.

Partytown позволяет вынести сторонние скрипты в веб-воркер, разгружая основной поток выполнения. У скриптов в воркере появляется синхронный доступ к DOM, который реализуется с помощью Proxy и блокирующих XHR-запросов.

На данный момент доступна альфа-версия библиотеки. Простестирован сандбоксинг скриптов Google Analytics, Google Tag Manager, Amplitude и нескольких других сервисов.

Очень полезная библиотека, попробую её у себя в проекте.

#js #library #performance

https://dev.to/adamdbradley/introducing-partytown-run-third-party-scripts-from-a-web-worker-2cnp
Поиск необработанных промисов

Свизек Теллер написал статью про ошибки, приводящие к возникновению необработанных промисов — "Finding unresolved promises in JavaScript".

Необработанные промисы — это большой источник проблем, который может привести к крешу программы. Они возникают при забытом перехвате исключения с помощью catch, при отсутствующем вызове resolve / rejet или при забытом return в цепочке промисов.

Свизек нашёл научную статью "Finding broken promises in asynchronous JavaScript programs", в которой авторы попробовали автоматизировать поиск подобных ошибок и создали утилиту PromiseKeeper. Идея интересная, но похоже, что работу над проектом остановили после публикации статьи.

#async #js #experimental

https://swizec.com/blog/finding-unresolved-promises-in-javascript/
Утечка исходных кодов из source maps (сорс мапов)

Попалась на глаза статья про потенциальные проблемы сорс мапов — "Source-maps could be revealing your private project files".

Сорс мапы — это служебные файлы, содержащие исходный проекта. Они используются для облегчения отладки транспилированного кода. Если сорс мапы доступны в проде, то любой человек может посмотреть исходный код проекта. Для бизнеса это может быть большой проблемой.

В общем, статья про довольно известные вещи. Я бы не стал про неё писать, если бы не вспомнил про ещё одну статью — "Топ тупых лазеек для исследования конкурентов: source map". В этой статье автор рассказывает про доступ к исходным кодам фронтенда Яндекс.Еды с помощью сорс мапов. Также он настроил переодическую автоматическую выгрузку кода в GitHub, чтобы следить за процессом разработки фронта.

Как бы то ни было, статьи полезные. И на всякий случай ещё разок проверьте, не доступны ли в проде сорс мапы для ваших проектов.

#js #security

https://levelup.gitconnected.com/automatically-generated-source-maps-could-be-revealing-your-private-projects-files-17b2d13d7da4
https://teletype.in/@opendevcast/SyYaunCHB
Не пишите квадраты

Степан Зубашев критикует современные тренды написания JavaScript-кода — "Обращение к Javascript-сообществу: перестаньте писать квадраты".

Популярность функциональных методов для работы с массивами и современные фичи JavaScript открыли двери для очень лаконичного кода. Но иногда чрезмерное увлечение лаконичностью приводит к падению производительности. В статье на примере использования .concat() внутри .reduce() рассказывается, почему это может оказаться серьёзной проблемой.

source.reduce(
(acc, item) => acc.concat(func(item)),
[]
);

В данном случае на каждую итерацию происходит копирование всех элементов старого массива в новый, который возвращается .concat(). Создание нового массива происходит для каждого элемента source, таким образом сложность кода получается квадратичной. С подобной проблемой столкнулись разработчики из вчерашней статьи.

Степан призывает не ставить в абсолют краткость кода и задумываться о производительности.

#js #algorithm #performance

https://habr.com/ru/post/590663/
Глубокое клонирование объектов с помощью structuredClone

Сурма из Google рассказал про новое API для глубокого клонирования JavaScript-объектов — "Deep-copying in JavaScript using structuredClone".

Самым популярным решением для глубокого клонирования объектов был JSON-хак:

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));

Он стал настолько популярен, что в браузерах были добавлены специальные оптимизации для этого хака. На данный момент он остаётся самым быстрым способом для глубокого клонирования небольших объектов. Однако он не поддерживает объекты с циклическими ссылками, Map, Set, Date, RegExp и ArrayBuffer.

Структурное клонирование — это другое название для глубокого клонирования. Оно использовалось браузерами неявно при передаче объектов с помощью postMessage и сохранении объектов в IndexedDB. Новый метод structuredClone открывает удобный доступ к этому механизму клонирования без недостатков JSON-хака:

const myDeepCopy = structuredClone(myOriginal);

Поддержка strucutredClone на данный момент есть в Firefox 94, в nightly-версии Chrome и Safari TP.

#js #api

https://web.dev/structured-clone/
Группировка элементов массива с помощью groupBy

На последней встрече TC39 пропозал groupBy перешёл на stage 3. Лаури Барт рассказала о том, как он работает — "Array Grouping Explainer".

Группировка элементов массива распространённая операция. Она поддерживается всеми популярными утилитарными библиотеками (lodash, ramda). Пропозал реализует похожий алгоритм из этих библиотек. Метод groupBy принимает коллбек, в параметрах которого передаются текущий элемент, текущий индекс и сам массив. Элементы массива разбиваются на группы на основе строки, которая возвращается коллбеком. В результате получается объект с распределёнными элементами массива:

const names = ['vasya', 'vasilisa', 'oleg'];
const groupedNames = names.groupBy(name => {
return name.charAt(0);
}
// результат:
// {v: ['vasya', 'vasilisa'], o: ['oleg']}

Также в пропозале есть второй метод groupByToMap, который работает точно также как groupBy, но возвращает не объект, а Map.

#js #proposal

https://laurieontech.com/posts/array-grouping/
Возможно, вам не нужен Rust и WASM, если у вас есть JavaScript

Увидел в канале @ufostation ссылку на статью Вячеслава Егорова про анализ проблем производительности библиотеки source-map — "Maybe you don't need Rust and WASM to speed up your JS".

Авторы source-map переписали основную логику библиотеки на Rust и WebAssembly, чтобы улучшить производительность. Егор решил проверить оригинальный код на предмет возможных оптимизаций. Там были найдены и исправлены проблемы, связанные с неоптимальной сортировкой, была уменьшена нагрузка на сборщик мусора заменой большого числа объектов типизированным массивом с ссылками на нужные данные, была испралвена проблема с деоптимизацией кода, связанной с передачей двух аргументов в функцию, которая ожидает на вход три аргумента.

В результате всех оптимизаций JavaScript-код стал уступать по скорости Rust и WebAssembly всего лишь на 15%.

Крутая статья. Рекомендую почитать всем.

#performance #js #internals #webassembly #rust

https://mrale.ph/blog/2018/02/03/maybe-you-dont-need-rust-to-speed-up-your-js.html
https://habr.com/ru/post/350018/ (перевод на русский)
Пропозал "await.ops"

Недавно узнал про пропозал "await.ops" — расширение await операторами await.all, await.any, await.race и await.allSettled. Они работают точно также как одноимённые методы у Promise.

Благодаря новым операторам код получается немного короче, так как отпадает необходимость в написании Promise:

// до
await Promise.all(users.map(async x => fetchProfile(x.id)))

// после
await.all users.map(async x => fetchProfile(x.id))

На данный момент "await.ops" находится на Stage 1, и его поддержки в браузерах нет. Авторы ищут дополнительные сценарии использования предложения для его продвижения на Stage 2.

#js #proposal

https://github.com/tc39/proposal-await.ops
Абсолютные импорты в JavaScript

Евгений Карагодин написал статью про настройку абсолютных импортов — "Абсолютные импорты в JavaScript".

Относительные пути в спецификаторах импортов могут быть неудобны в проектах с глубокой вложенностью директорий. Поэтому были придуманы разные способы для импорта файлов от корня проекта. В статье рассказывается про основные способы упрощения работы с импортами. Про сложность настройки абсолютных импортов в Node.js-проектах c TypeScript и тест-раннерами.

Процесс настройки абсолютных импортов должен стать проще после имплементации спецификация import maps, с помощью которой можно управлять резолвингом модулей. На данный момент import maps поддерживаются только в Deno.

#js #esm

https://blog.ekaragodin.com/TH2jgliMXOO
Cовременные возможности для работы с JavaScript-модулями

Аксель Раушмайер написал статью про современные возможности для работы с JavaScript-модулями — "Publishing and consuming ECMAScript modules via packages – the big picture".

Последние версии Node.js поддерживают package exports и package imports. Package exports используются авторами библиотек для настройки путей, по которым будет доступен импорт модулей. Package imports позволяют создавать альясы на пакеты или модули. Их можно использовать для подмены реализации модуля в зависимости от окружения. Package exports и package imports настраиваются в package.json.

В браузерах скоро появится поддержка import maps. С их помощью можно создавать альясы, относительно которых будут резолвиться спецификаторы модулей в браузере. Это полезно при доставке кода пользователям в чистом ESM, когда в названии файлов модулей есть хеши. Import maps уже используют в проде авторы почтового клиента hey.

#js #esm #nodejs #npm

https://2ality.com/2022/01/esm-specifiers.html
Сравнение производительности Array.push и Array.concat

Ши Лин сравнила производительноcть методов .push и .concat у массивов — "Javascript Array.push is 945x faster than Array.concat".

Ши разрабатывает фреймворк для автоматического тестирования. У одного пользователя фреймворка скорость выполнения тестов снизилась в несколько раз. Выяснилось, что для работы с DOM-деревом фреймворк собирал все элементы страницы в одномерный массив с помощью .concat. Так как каждый вызов .concat создаёт новый массив, это негативно сказывалось на производительности прогона тестов.

В статье есть несколько бенчмарков, которые показывают, что объединение массивов с помощью .push(...arr) на два порядка быстрее .concat(arr).

Добавлю свои пять копеек. Заменять .concat(arr) на .push(...arr) стоит только там, где есть проблема, так как на очень больших массивах .push(...arr) выкинет ошибку переполнения стека.

#js #performance

https://dev.to/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki