Автор заметки, перевод которой мы сегодня публикуем, говорит, что в JavaScript, как и в любом другом языке программирования, можно обнаружить множество маленьких хитростей, предназначенных для решения самых разных задач, как простых, так и довольно сложных. Некоторые из подобных приёмов широко известны, а другие, не такие распространённые, могут приятно удивить тех, кто о них не знает. Сейчас мы рассмотрим 7 полезных приёмов программирования на JavaScript.



1. Получение уникальных значений массива


В JavaScript сформировать массив, содержащий лишь уникальные значения из другого массива, вероятно, проще, чем вы думаете:

var j = [...new Set([1, 2, 3, 3])]
// [1, 2, 3]

Мне нравится то, как эту задачу можно решить, совместно используя оператор ... и тип данных Set.

2. Массивы и логические значения


Вам когда-нибудь нужно было убрать из массива значения, приведение которых к логическому типу даёт false? Например, это такие значения, как 0, undefined, null, false. Возможно, вы не знали, что для того, чтобы это сделать, можно поступить так:

myArray
    .map(item => {
        // ...
    })
    // Избавляемся от ненужных значений
    .filter(Boolean);

Как видите, для того, чтобы избавиться от всех подобных значений, достаточно передать Boolean методу массивов .filter().

3. Создание по-настоящему пустых объектов


Уверен в том, что вы можете создать объект, который кажется пустым, воспользовавшись синтаксисом объектного литерала: {}. Но такому объекту будет назначен прототип (__proto__), у него будет метод hasOwnProperty() и другие методы объектов. Для того чтобы создать по-настоящему пустой объект, который можно, например, использовать в качестве «словаря», можно поступить так:

let dict = Object.create(null);

// dict.__proto__ === "undefined"
// У объекта нет никаких свойств до тех пор, пока вы их в явном виде не добавите к нему

В объекте, созданном таким способом, нет свойств и методов, которые не добавлены в него самим программистом.

4. Слияние объектов


Тем, кто пишет на JavaScript, всегда было нужно создавать такие объекты, которые включали бы в себя содержимое других объектов. Особенно актуальной эта задача стала тогда, когда в JavaScript появились классы, тогда, когда программистам приходится работать с чем-то вроде программных представлений виджетов. Вот как создать новый объект на основе нескольких других объектов:

const person = { name: 'David Walsh', gender: 'Male' };
const tools = { computer: 'Mac', editor: 'Atom' };
const attributes = { handsomeness: 'Extreme', hair: 'Brown', eyes: 'Blue' };

const summary = {...person, ...tools, ...attributes};
/*
Object {
  "computer": "Mac",
  "editor": "Atom",
  "eyes": "Blue",
  "gender": "Male",
  "hair": "Brown",
  "handsomeness": "Extreme",
  "name": "David Walsh",
}
*/

Оператор ... значительно упрощает решение задачи слияния объектов.

5. Обязательные параметры функций


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

const isRequired = () => { throw new Error('param is required'); };

const hello = (name = isRequired()) => { console.log(`hello ${name}`) };

// Тут будет выдана ошибка, функции не передан аргумент name
hello();

// Здесь тоже будет ошибка
hello(undefined);

// Эти варианты вызова функции будут работать нормально
hello(null);
hello('David');

Перед нами — дополнительный уровень проверки того, что передаётся функциям.

6. Деструктурирующее присваивание и новые имена извлечённых свойств объектов


Деструктурирование — это новая полезная возможность JavaScript, но иногда свойствам, извлекаемым из объектов, нужно назначать имена, отличающиеся от тех, которые они имеют в этих объектах. Вот как это сделать:

const obj = { x: 1 };

// Теперь мы можем работать с obj.x как с x
const { x } = obj;

// А теперь obj.x для нас выглядит как otherName
const { x: otherName } = obj;

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

7. Разбор строк запросов


Многие годы мы, для разбора строк запросов, писали регулярные выражения, но эти времена прошли. Теперь для решения этой задачи можно воспользоваться замечательным API URLSearchParams:

// Предполагается, что мы работаем с "?post=1234&action=edit"

var urlParams = new URLSearchParams(window.location.search);

console.log(urlParams.has('post')); // true
console.log(urlParams.get('action')); // "edit"
console.log(urlParams.getAll('action')); // ["edit"]
console.log(urlParams.toString()); // "?post=1234&action=edit"
console.log(urlParams.append('active', '1')); // "?post=1234&action=edit&active=1"

Использовать API URLSearchParams куда легче, чем решать те же задачи, применяя регулярные выражения.

Итоги


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

Уважаемые читатели! Знаете какие-нибудь полезные приёмы JS-программирования? Если так — просим ими поделиться.

Комментарии (14)


  1. storm_r1der
    30.04.2019 12:51
    -1

    Можно выделить только URLSearchParams, остальное — без комментариев. Очень полезные мелочи — деструктурирующее присваивание и spread-оператор, только не мелочи, и не полезные, а обязательные к использованию. Ну, про обязательные параметры вообще молчу — имхо, я бы гнал "программистов", которые такое практикуют, ссаными тряпкями.


    Улыбнуло про пустые объекты — в js и так нормального ооп нету (особенности интерпретации — все, сюрприз, превращается в функциональщину и императивщину).


    И да, зачем нужны "массивы с уникальными значениями" если уже есть Set? Плохой костыль, с учетом того, что дальше мы можем, оперируя памятью (или через консоль, лол, если это незащищенный код на клиенте) запушить туда любые угодные нам значения.


    Продолжайте переводить подобный кал, не обращая внимания на то, что переводите. Удачи.


    1. JustDont
      30.04.2019 13:55
      +1

      Очень полезные мелочи — деструктурирующее присваивание и spread-оператор, только не мелочи, и не полезные, а обязательные к использованию.

      Всегда приятно смотреть на то, как приходит новый программист на ES6, и первым делом подключает lodash, потому что ему видите ли надо пару объектов слить вместе, а руками он не умеет ^_^
      Но вообще деструктурирующее присваивание — это «мелкий» сахар, в том плане, что я бы его обязательным не назвал. С ним красивше, но без него тоже читаемо. А вот spread — это «глубокий» сахар, с его помощью сокращаются целые блоки унылого кода по переприсвоению; и читать код от того, кто его просто не знает — иногда очень мучительно.


      1. storm_r1der
        02.05.2019 21:16

        Совершенно с вами согласен. У меня только один вопрос к ru_vds: каким образом подобная чушь, под которой куча гневных комментариев:

        1) Набирает 10-15+ рейтинга
        2) Практически все отрицательные (по отношению к содержимому статьи) комментарии в диких минусах.

        При этом всем, в их недавнем посте про консоли в комментах были ссылки на 3 (3, Карл!) поста с практически ИДЕНТИЧНЫМ содержимым.

        *негодует*


  1. AndreasCag
    30.04.2019 12:53

    Одна половина советов из 2015, за другую вас погонят тряпками из компании


  1. CoolCmd
    30.04.2019 13:30

    сомневаюсь, что применяют isRequired() на практике. для простой проверки количества параметров проще arguments.length, а для проверки, что в параметре есть что-то осмысленное, нужен как минимум typeof.


  1. VolCh
    30.04.2019 14:38

    Если дошло до isRequired не пора ли посмотреть в сторону статанализа, тайпчекеров и вообще компиляторов статически типизированных языков в JS?


  1. Cerberuser
    30.04.2019 14:41

    С isRequired — приём не для прода, но забавный, стоит запомнить, пожалуй (и да, статическая типизация — не панацея, хотя я сам пишу на TypeScript). Остальное… вот кто из более-менее освоившихся в теме этого не знает, скажите на милость (кроме filter(Boolean), которое с непривычки выглядит как адский хак)?


  1. inoyakaigor
    30.04.2019 15:04

    Оператор… значительно

    Не устану нести свет людям: это не оператор в его классическом понимании, а синтаксис.

    myArray
    .map(item => {
    //…
    })
    // Избавляемся от ненужных значений
    .filter(Boolean);


    связку map и filter можно заменить, на один reduce:

    […].reduce((prev, curr) => {
    const tmp = /*cast to boolean*/
    return tmp && prev
    }, true)


    1. Cerberuser
      30.04.2019 17:07
      +1

      А последнее, извиняюсь, каким образом? map + filter возвращает массив, reduce в таком исполнении — boolean.


      1. stshk
        02.05.2019 20:11
        -1

        это магия js ;))))))

        [1,2,3].reduce((acc, cur)=>cur%2?acc.concat([cur*3]):acc, [])


        1. Cerberuser
          02.05.2019 20:39

          Нет, про этот-то приём я знаю, я пытался понять, что хотел сказать автор предыдущего комментария своей конструкцией.


          1. stshk
            02.05.2019 21:05

            наверное что-нибудь типа reduce = filter + map:

            [0,null,1,2,false,3].reduce((acc, cur)=>Boolean(cur)?acc.concat([cur*3]):acc, [])

            может просто написал неправильно
            хотя так вот короче и понятнее:
            [0,null,1,2,false,3].filter(Boolean).map(i=>i*3)

            но 2 прогона =(((
            в этом смысле итераторы питона лучше =)


          1. inoyakaigor
            03.05.2019 23:56

            Я просто немного поспешил с ответом. В своей практике я часто привожу массив вида [true, false, true] к переменной в которой хранится показатель того, что все объекты в изначальном массиве содержат нужное свойство или нужное значение у этого свойства


  1. Els0lo
    03.05.2019 08:48

    Благодарю за статью