Доброго времени суток, друзья!
Прежде чем мы начнем, позвольте мне немного рассказать о коде, который я пишу. Почти весь мой код состоит из Javascript, плюс немного HTML и CSS. Я пишу как клиентский, так и серверный JS. Я тестирую свой код. Я создаю и распространяю библиотеки с открытым исходным кодом, которые используются тысячами разработчиков по всему миру. Для фронтенда я использую React, для бэкенда — Express или бессерверные вычисления.
Вот 5 особенностей JS, без которых я не мог бы писать код. В произвольном порядке. Разумеется, «без которых я не мог бы писать код» — это гипербола. Это «фичи», которые мне по-настоящему нравятся и используются мной постоянно.
Я использую эту фичу почти во всех файлах. Неважно, о React-компоненте идет речь или о функции, принимающей аргументы, деструктуризация — прикольная штука.
Вот несколько примеров:
Вот как это выглядит в React:
Эта фича позволяет делать некоторые интересные вещи:
Это еще одна фича, которую я использую практически в каждом файле. До того как модули стали частью языка, приходилось использовать странные библиотеки и инструменты для работы с большими проектами. С модулями (и сборщиками, такими как Rollup или Webpack) у нас появилась отличная возможность делиться кодом с другими.
Вот парочка примеров:
Если Вы хотите узнать больше о модулях, можете посмотреть мое видео на youtube — «More than you want to know about ES6 Modules».
Я люблю и использую эту фичу все время. Это относится как к аргументам функции, так и к деструктуризации. Вот как это используется при деструктуризации объекта:
Обратите внимание, что у объекта bench нет свойства legs. Без использования синтаксиса параметров по умолчанию значением legs будет undefined.
Вы также можете использовать деструктурирующее присваивание с этой фичей:
Вот как это выглядит в списке параметров:
Эта фича также позволяет делать некоторые довольно интересные вещи, поскольку значение справа от знака "=" вычисляется только при необходимости. Это означает, что Вы можете использовать ее для проверки наличия обязательных параметров:
Некоторые считают этот код слишком сложным. Возможно, они правы. Но понимать, как это работает, очень круто!
О, а Вы заметили, что мы можем использовать предыдущие аргументы как часть параметров по умолчанию для следующих аргументов (как в случае с upperKind)? Здорово, правда?
Я часто использую стрелочные функции. Мне нравятся функциональные выражения, но если, например, мне нужна анонимная функция обратного вызова (которой я не хочу придумывать имя), или я хочу получить неявные возвращаемые значения, то стрелочные функции — это как раз то, что мне нужно.
Вот несколько примеров использования стрелочных функций:
JS является однопоточным и построен на системе событий (стеке вызовов). Я большой поклонник разговоров типа Что такое event loop? (с русскими субтитрами). Промисы и async/await являются отличными инструментами для управления этим. Большая часть моего кода является асинхронной и названные инструменты значительно упрощают мою работу. Честно говоря, промисы являются серьезной темой и требуют некоторого «привыкания», но они классные.
Я часто использую async/await в тестах и бэкенде. Вот пример асинхронного теста:
А вот пример использования async\await в Express:
Любопытно, что я не часто использую async/await в своем React-коде (по крайней мере, напрямую). Так происходит потому, что я стараюсь делать большую часть «асинхронной логики» вне моих компонентов. Поэтому если я, например, делаю что-то асинхронное при вызове useEffect в React, я ограничиваюсь одним вызовом асинхронной функции, поскольку считаю, что с промисами работать легче:
Советую прочитать статью Anthony Chu «Async/Await in Node».
Существует множество других фич, которые я регулярно использую и которые могли бы войти в этот список. Приведенные особенности — мои любимые, я все время к ним обращаюсь. Имеются также некоторые свежие дополнения к языку, которые еще не вошли в мою мышечную память. Сейчас самое время стать JS-разработчиком! Надеюсь, эта статья была Вам полезной! Удачи!
Прежде чем мы начнем, позвольте мне немного рассказать о коде, который я пишу. Почти весь мой код состоит из Javascript, плюс немного HTML и CSS. Я пишу как клиентский, так и серверный JS. Я тестирую свой код. Я создаю и распространяю библиотеки с открытым исходным кодом, которые используются тысячами разработчиков по всему миру. Для фронтенда я использую React, для бэкенда — Express или бессерверные вычисления.
Вот 5 особенностей JS, без которых я не мог бы писать код. В произвольном порядке. Разумеется, «без которых я не мог бы писать код» — это гипербола. Это «фичи», которые мне по-настоящему нравятся и используются мной постоянно.
1. Деструктуризация
Я использую эту фичу почти во всех файлах. Неважно, о React-компоненте идет речь или о функции, принимающей аргументы, деструктуризация — прикольная штука.
Вот несколько примеров:
const address = { city: 'Salt Lake City', state: 'UT', zip: 84115, coords: { lat: 40.776608, long: -111.920485, }, } // допустим, вы хотите сделать это: const city = address.city const state = address.state const zip = address.zip // используя деструктуризацию, вы можете сделать так: const {city, state, zip} = address
Вот как это выглядит в React:
// без деструктуризации: function UserName(props) { return ( <div> </div> ) } // с использованием деструктуризации: function UserName() { return ( <div> </div> ) } // с использованием вложенной деструктуризации: function UserName({name: {first, last}}) { return ( <div> </div> ) }
Эта фича позволяет делать некоторые интересные вещи:
const info = { title: 'Once Upon a Time', protagonist: { name: 'Emma Swan', enemies: [ {name: 'Regina Mills', title: 'Evil Queen'}, {name: 'Cora Mills', title: 'Queen of Hearts'}, {name: 'Peter Pan', title: `The boy who wouldn't grow up`}, {name: 'Zelena', title: 'The Wicked Witch'}, ], }, } // при правильном форматировании вложенная деструктуризация довольно проста, // хотя не все разделяют мое мнение на этот счет const { title, protagonist: { name: name, enemies: [, , , {title: enemyTitle, name: enemyName}], }, } = info console.log(`${enemyTitle} (${enemyName}) is an enemy to ${name} in "$5 JavaScript Features I Couldn't Code Without"`)
2. Модули
Это еще одна фича, которую я использую практически в каждом файле. До того как модули стали частью языка, приходилось использовать странные библиотеки и инструменты для работы с большими проектами. С модулями (и сборщиками, такими как Rollup или Webpack) у нас появилась отличная возможность делиться кодом с другими.
Вот парочка примеров:
// a.js // экспорт function add(a, b) { return a + b } const foo = 'bar' const theAnswer = 42 const theQuestion = 'who knows' // синтаксис модулей позволяет экспортировать их во время создания, но // мне нравится размещать все "экспорты" в одном месте export default add export {foo, theAnswer, theQuestion} // b.js // импорт // 1. импортируем модуль import './a' // 2. импортируем по умолчанию import add from './a' // 3. импортируем `theAnswer` и `theQuestion` из './a' import {theAnswer, theQuestion} from './a' // 4. импортируем `theAnswer` и переименовываем его в `fortyTwo` import {theAnswer as fourtyTwo} from './a' // 5. импортируем `add` (по умолчанию) и `theQuestion` import {default as add, theQuestion} from './a' // 6. импортируем `add` и `theQuestion` без указания импорта по умолчанию import add, {theQuestion} from './a' // 7. импортируем все в единое "пространство имен" под названием `allTheThings` import * as allTheThings from './a'
Если Вы хотите узнать больше о модулях, можете посмотреть мое видео на youtube — «More than you want to know about ES6 Modules».
3. Параметры по умолчанию
Я люблю и использую эту фичу все время. Это относится как к аргументам функции, так и к деструктуризации. Вот как это используется при деструктуризации объекта:
const bench = {type: 'Piano', adjustable: false} const {legs = 4} = bench // `The bench has ${legs} legs` // -> The bench has 4 legs // bench - скамья, leg - ножка
Обратите внимание, что у объекта bench нет свойства legs. Без использования синтаксиса параметров по умолчанию значением legs будет undefined.
Вы также можете использовать деструктурирующее присваивание с этой фичей:
const bench = {type: 'Piano', adjustable: false} const {legs: legCount = 4} = bench // `The bench has ${legCount} legs` // -> The bench has 4 legs
Вот как это выглядит в списке параметров:
function getDisplayName(firstName = 'Unknown', lastName = 'Unknown') { return `${firstName} ${lastName}` } // getDisplayName() // -> Unknown Unknown // getDisplayName('Andrew') // -> Andrew Unknown // getDisplayName(undefined, 'Yang') // -> Unknown Yang // getDisplayName('Andrew', 'Yang') // -> Andrew Yang
Эта фича также позволяет делать некоторые довольно интересные вещи, поскольку значение справа от знака "=" вычисляется только при необходимости. Это означает, что Вы можете использовать ее для проверки наличия обязательных параметров:
function getCandy( kind = requiredParam('kind'), size = requiredParam('size'), upperKind = kind.toUpperCase(), callback = function noop() {}, ) { const result = {kind, size, upperKind} callback(result) return result } function requiredParam(argName) { throw new Error(`${requiredParam} is required`) } // getCandy('twix', 'king') // -> {kind: 'twix', size: 'king', upperKind: 'TWIX'} // getCandy('twix') // -> ошибка: 'size is required'
Некоторые считают этот код слишком сложным. Возможно, они правы. Но понимать, как это работает, очень круто!
О, а Вы заметили, что мы можем использовать предыдущие аргументы как часть параметров по умолчанию для следующих аргументов (как в случае с upperKind)? Здорово, правда?
4. Стрелочные функции
Я часто использую стрелочные функции. Мне нравятся функциональные выражения, но если, например, мне нужна анонимная функция обратного вызова (которой я не хочу придумывать имя), или я хочу получить неявные возвращаемые значения, то стрелочные функции — это как раз то, что мне нужно.
Вот несколько примеров использования стрелочных функций:
const divide = (a, b) => a / b const getFive = () => 5 const identity = i => i const asArray = (...args) => args // обычно, я не именую стрелочные функции с несколькими аргументами // (в таких случаях я использую обычные функциональные выражения), // но Вы можете их именовать, если хотите: const tryInvoke = (obj, fn, ...args) => { try { return obj[fn](...args) } catch (e) { return undefined } } // для того, чтобы вернуть объект, необходимо заключить его в круглые скобки const getObject = favoriteCandy => () // многострочные JSX (расширение JS в React) также должны быть заключены в круглые скобки const MyComponent = () => ( <div> Hello world! I am a function and I return <strong>JSX!</strong> </div> )
5. Промисы и async/await
JS является однопоточным и построен на системе событий (стеке вызовов). Я большой поклонник разговоров типа Что такое event loop? (с русскими субтитрами). Промисы и async/await являются отличными инструментами для управления этим. Большая часть моего кода является асинхронной и названные инструменты значительно упрощают мою работу. Честно говоря, промисы являются серьезной темой и требуют некоторого «привыкания», но они классные.
Я часто использую async/await в тестах и бэкенде. Вот пример асинхронного теста:
test('Can fill out a form across multiple pages', async () => { mockSubmitForm.mockResolvedValueOnce({success: true}) const testData = {food: 'test food', drink: 'test drink'} const {findByLabelText, findByText} = render(<App />) user.click(await findByText(/fill.*form/i)) user.type(await findByLabelText(/food/i), testData.food) user.click(await findByText(/next/i)) user.type(await findByLabelText(/drink/i), testData.drink) user.click(await findByText(/review/i)) expect(await findByLabelText(/food/i)).toHaveTextContent(testData.food) expect(await findByLabelText(/drink/i)).toHaveTextContent(testData.drink) user.click(await findByText(/confirm/i, {selector: 'button'})) expect(mockSubmitForm).toHaveBeenCalledWith(testData) expect(mockSubmitForm).toHaveBeenCalledTimes(1) user.click(await findByText(/home/i)) expect(await findByText(/welcome home/i)).toBeInTheDocument() })
А вот пример использования async\await в Express:
async function getListItems(req, res) { const listItems = await listItemsDB.query({ownerId: req.user.id}) res.json({listItems: await expandBookDataMultiple(listItems)}) }
Любопытно, что я не часто использую async/await в своем React-коде (по крайней мере, напрямую). Так происходит потому, что я стараюсь делать большую часть «асинхронной логики» вне моих компонентов. Поэтому если я, например, делаю что-то асинхронное при вызове useEffect в React, я ограничиваюсь одним вызовом асинхронной функции, поскольку считаю, что с промисами работать легче:
React.useEffect(() => { getUser().then( user => setState({status: 'success', error: null, user}), error => setState({status: 'error', error, user: null}), ) }, [])
Советую прочитать статью Anthony Chu «Async/Await in Node».
Заключение
Существует множество других фич, которые я регулярно использую и которые могли бы войти в этот список. Приведенные особенности — мои любимые, я все время к ним обращаюсь. Имеются также некоторые свежие дополнения к языку, которые еще не вошли в мою мышечную память. Сейчас самое время стать JS-разработчиком! Надеюсь, эта статья была Вам полезной! Удачи!
