ES6: полезные советы и неочевидные приёмы

Автор оригинала: rajaraodv
  • Перевод
Стандарт EcmaScript 2015 (ES6) существует уже несколько лет. Он принёс с собой множество новых возможностей, разные способы использования которых далеко не всегда очевидны. Вот обзор некоторых из этих возможностей с примерами и комментариями.

image

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


ES6 позволяет задавать значения формальных параметров по умолчанию, что позволяет, при вызове функции без указания значений этих параметров, подставлять их стандартные значения. Это позволяет задавать параметры, без передачи которых функция работать не будет.

В следующем примере мы задаём функцию required() как значение по умолчанию для параметров a и b. Это означает, что если a или b не будут переданы функции при вызове, будет вызвана функция required() и мы получим сообщение об ошибке.

const required = () => {throw new Error('Missing parameter')};
//При вызове этой функции произойдёт ошибка, если параметры "a" или "b" не заданы
const add = (a = required(), b = required()) => a + b;
add(1, 2) //3
add(1) // Error: Missing parameter.

2. Секреты метода reduce


Метод reduce объекта Array чрезвычайно универсален. Обычно его используют для преобразования массива неких элементов к единственному значению. Однако с его помощью можно сделать ещё очень много всего полезного.

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

▍2.1. Использование reduce для одновременного выполнения мэппинга и фильтрации массива


Представим, что перед нами стоит следующая задача. Имеется список элементов, каждый из которых надо модифицировать (что сводится к использованию метода map), после чего отобрать из него несколько элементов (это можно сделать с помощью метода filter). Эта задача вполне решаема последовательным применением методов map и filter, но так по списку элементов придётся пройтись дважды. А нас это не устраивает.

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

const numbers = [10, 20, 30, 40];
const doubledOver50 = numbers.reduce((finalList, num) => {
  
  num = num * 2; //удвоить каждое число (аналог map)
  
  //отобрать числа > 50 (аналог filter)
  if (num > 50) {
    finalList.push(num);
  }
  return finalList;
}, []);
doubledOver50; // [60, 80]

▍2.2. Использование reduce вместо map или filter


Если вы проанализировали вышеприведённый пример, то для вас окажется совершенно очевидной возможность использования метода reduce вместо map или filter.

▍2.3. Использование reduce для анализа расстановки скобок


Вот ещё один пример полезных возможностей метода reduce.

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

Эту задачу можно решить с помощью метода reduce так, как показано ниже. Тут мы используем переменную counter с начальным значением, равным 0. Мы увеличиваем её значение на единицу, если находим символ «(», и уменьшаем, если находим символ «)». Если скобки в строке сбалансированы, на выходе должен получиться 0.

//Возвращает 0 если скобки сбалансированы.
const isParensBalanced = (str) => {
  return str.split('').reduce((counter, char) => {
    if(counter < 0) { //matched ")" before "("
      return counter;
    } else if(char === '(') {
      return ++counter;
    } else if(char === ')') {
      return --counter;
    }  else { //найден какой-то другой символ
      return counter;
    }
    
  }, 0); //<-- начальное значение для счётчика
}
isParensBalanced('(())') // 0 <-- скобки сбалансированы
isParensBalanced('(asdfds)') //0 <-- скобки сбалансированы
isParensBalanced('(()') // 1 <-- скобки несбалансированы
isParensBalanced(')(') // -1 <-- скобки несбалансированы

▍2.4. Подсчёт количества совпадающих значений массива (преобразование массива в объект)


Иногда нужно подсчитать одинаковые элементы массива или преобразовать массив в объект. Для решения этих задач также можно использовать reduce.

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

var cars = ['BMW','Benz', 'Benz', 'Tesla', 'BMW', 'Toyota'];
var carsObj = cars.reduce(function (obj, name) { 
   obj[name] = obj[name] ? ++obj[name] : 1;
  return obj;
}, {});
carsObj; // => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }

На самом деле, возможности reduce на этом не ограничиваются. Если вам интересны подробности об этом полезном методе — изучите примеры с этой страницы.

3. Деструктурирование объектов


▍3.1. Удаление ненужных свойств


Бывает так, что из объекта требуется удалить ненужные свойства. Возможно, это свойства, которые содержат секретные сведения, возможно — они слишком велики. Вместо того, чтобы перебирать весь объект и удалять подобные свойства, можно извлечь эти свойства в переменные и оставить нужные свойства в rest-параметре.

В следующем примере нам нужно избавиться от свойств _internal и tooBig. Их можно присвоить переменным с такими же именами и сохранить оставшиеся свойства в rest-параметре cleanObject, который можно использовать позже.

let {_internal, tooBig, ...cleanObject} = {el1: '1', _internal:"secret", tooBig:{}, el2: '2', el3: '3'};
console.log(cleanObject); // {el1: '1', el2: '2', el3: '3'}

▍3.2. Деструктурирование вложенных объектов в параметрах функции


В следующем примере свойство engine — это вложенный объект объекта car. Если нам нужно узнать, скажем, свойство vin объекта engine, его легко можно деструктурировать.

var car = {
  model: 'bmw 2018',
  engine: {
    v6: true,
    turbo: true,
    vin: 12345
  }
}
const modelAndVIN = ({model, engine: {vin}}) => {
  console.log(`model: ${model} vin: ${vin}`);
}
modelAndVIN(car); // => model: bmw 2018  vin: 12345

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


ES6 поддерживает оператор расширения (spread), выглядящий как три точки. Обычно его применяют для работы с массивами, но его можно использовать и с обычными объектами.

В следующем примере мы используем оператор расширения для формирования нового объекта из двух существующих. Ключи свойств объекта №2 переопределят ключи свойств объекта №1. В частности, свойства b и c из object2 переопределят такие же свойства объекта object1.

let object1 = { a:1, b:2,c:3 }
let object2 = { b:30, c:40, d:50}
let merged = {…object1, …object2} //применим оператор расширения для формирования нового объекта
console.log(merged) // {a:1, b:30, c:40, d:50}

4. Коллекции


▍4.1. Удаление повторяющихся значений из массивов


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

let arr = [1, 1, 2, 2, 3, 3];
let deduped = [...new Set(arr)] // [1, 2, 3]

▍4.2. Использование методов массивов с коллекциями


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

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

let mySet = new Set([1,2, 3, 4, 5]);
var filtered = [...mySet].filter((x) => x > 3) // [4, 5]

5. Деструктурирование массивов


Часто бывает так, что функция возвращает множество значений в виде массива. Извлечь эти значения из массива можно с использованием техники деструктурирования.

▍5.1. Обмен значений переменных


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

let param1 = 1;
let param2 = 2;
//поменяем местами значения param1 и param2 
[param1, param2] = [param2, param1];
console.log(param1) // 2
console.log(param2) // 1

▍5.2. Приём и обработка нескольких значений, возвращаемых функцией


В следующем примере мы загружаем некую статью, находящуюся по адресу /post, и связанные с ней комментарии с адреса /comments. Тут применяется конструкция async/await, функция возвращает результат в виде массива.

Деструктурирование упрощает присвоение результатов работы функции соответствующим переменным.

async function getFullPost(){
  return await Promise.all([
    fetch('/post'),
    fetch('/comments')
  ]);
}
const [post, comments] = getFullPost();

Итоги


В этом материале мы рассмотрели несколько простых, но неочевидных приёмов использования новых возможностей ES6. Надеемся, они вам пригодятся.

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

RUVDS.com
880,00
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 87

    +1

    Строго говоря, async/await и ... для объектов в ES6 (то есть ES2015) не входят. Первое попало только в ES2017, второе будет в ES2018.


    А reduce крутая вещь, да.

      +5

      Но reduce был ещё в ES5.

        +2

        Действительно. В общем, не очень точное название у статьи:)

      +2
      Фишка с required аргументами понравилась, это та вещь которой по непонятной причине нет «из коробки»
        0

        Если у вас транспилируется stage 2, то можно писать и так:


        const requiredArgument = new Error('Missing argument')
        const add = (a = throw new Error('Argument `a` is required'), b = throw new Error('Argument `b` is required')) => a + b;
          +2
          Вообще, проверка как в статье бесполезна, т. к. чаще всего, если делается проверка аргумента, то будет сделана и проверка типа, и даже, возможно, диапазона значений. Здесь же только проверка, что не undefined, да и со стандартной ошибкой, что как-то скупо…

          Но если бы можно было обозначать примерно в таком стиле, то мб и пошло:
          function(arg!, otherArg!) {}

          Тоже только на undefined, но короче и с нормальной ошибкой.
            0

            Почему бы и да?


            const required = varName => throw new Error(`Variable ${varName} shouldnt be undefined`);
            
            function myFunc(a = required('a')){...}
              0
              Два раза повторять название параметра, не очень удобно.
        +1
        var filtered = [...mySet].filter((x) => x > 3) // [4, 5]

        напишите тестик на производительность этого на сете хотяб на пару тысяч элементов, а лучше больше, по сравнению с foreach по этому сету где сложат всё нужное в отдельный заранее созданный массив. И прогоните на современных браузерах включая мобилки. Я прям вот специально сейчас не пойду и не буду писать такой тест, потому что делал подобное уже 100 раз. Гарантирую подлинное удовольствие каждому разработчику который будет это делать.
        +6
        async function getFullPost(){
          return await Promise.all([
            fetch('/post'),
            fetch('/comments')
          ]);
        }
        const [post, comments] = getFullPost();

        Так работать не будет


        Нужно вызывать (внутри async-функции) так:


        const [post, comments] = await getFullPost();

        Или, на худой конец, так:


        getFullPost()
            .then([post, comments] => {
                /*some code*/
            })
            .catch(error => {
                /*some code*/
            });

        Потому что функция getFullPost возвращает не массив, а Promise

          –4

          Я конечно не спец по джаваскрипту. Но вроде getFullPost возвращает уже выполненный (не уверен как правильно назвать) промис?
          Там стоит return await Promise...

            +1

            А перед этой функцией стоит async. Такая функция всегда возвращает промис.

            +5

            Более того, return await считается антипаттерном, ибо не несёт никакого смысла.

              –4
              В данном случае более чем несёт. При return await промис, созданный getFullPost, будет заполнен массивом с двумя значениями, а без return будет заполнен undefined.

              В данном случае можно было бы вообще убрать async await, но это может:

              1. Нарушать стиль (раньше все async-функции использовали async и await, а теперь нет)
              2. Усложнять код (надо перечитывать код, чтобы убедиться, что мы возвращаем нужный промис, а при вызове функции используем await)
              3. Если потребуется добавить ещё await'ы в функцию, и тогда убранный async await придётся возвращать

                –1
                Пункт 2 — мимо, потому что при вызове оператор await нужно ставить или не ставить независимо от того объявлена ли функция как async
                  –1
                  Имелось ввиду, что всем итак понятно, что async-функция возвращает промис, и если у нас 95% функций — это async, то когда попадётся асинхронная функция, не помеченная как async, ты сразу начнёшь думать, а нет ли такого, что я где-то в коде не заметил, что это на самом деле асинхронная функция, и с ней тоже надо было использовать await.

                  Т. е. ты привык, что твои асинхронные функции выделены как async и уже на автомате используешь await с ними, а тут функция выбивается из стандартного шаблона — она не async, но всё-равно возвращает промис, и всё-равно надо использовать await.

                  Программисты любят, когда всё стандартизировано =)
                  Т. е. получилось бы, что часть асинхронных функций написано так, а часть так, и вот это было бы странным. Проще везде поставить async и понадеяться, что оптимизатор вырежет лишний промис (вероятно, нет, т. к. мы могли вернуть кастомный промис, а async-функция должна вернуть стандартный + могут не совпадать тайминги при двойной промисификации).

                  Ну и просто return await Promise может читаться лучше и быть меньше подвержен ошибкам. Поэтому может быть проще.
                  0
                  В данном случае более чем несёт. При return await промис, созданный getFullPost, будет заполнен массивом с двумя значениями, а без return будет заполнен undefined.

                  Нет, разницы никакой нет.
                  return await "дождется" результата Promise.all и "завернёт" его в промис, return ничего ждать не будет и сразу вернёт промис.
                  В кавычках, потому что это чуть иначе работает на самом деле, но смысл именно такой.

                    0
                    Я почему-то подумал про опускание return. Вообще да, если опустить await, результат будет тот же самый — он всё-равно дождётся этого промиса и только тогда заполнит автосозданный промис.

                    До появления официальной спецификации это, кстати, не поддерживалось, вроде, в Babel, и промис заполнялся промисом, а не его результатом.
                      0

                      А тут чуть хитрее: Promise<Promise<A>> === Promise<A>

                        0
                        Ага, это встроено в сам промис, а не async: когда мы вызываем resolve(anotherPromise), он будет его ждать. Resolve в anotherPromise тоже при этом может ждать, и также и далее.
                          0
                          С точки зрения типизации выглядит адово и не нужно.

                          Почему язык за меня решает что я хотел вернуть
                          Promise<A>
                          если может быть я действительно хотел вернуть
                          Promise<Promise<A>>
                          ?

                          Ну и
                          Promise<A>
                          в общем случае не эквивалентен
                          A
                          , но если `A` в свою очередь является промисом, то срабатывает особая логика.

                          Вам бы понравилось если бы скажем `push()` для массива определял случай когда аргумент является массивом, и делал `concat()` вместо `push()`?
                            +1
                            Зато concat умеет делать push если аргумент не является массивом…
                              0
                              Почему язык за меня решает что я хотел вернуть
                              Так сложилось исторически: когда async-await ещё не было, это было способом строить цепочки промисов, например:
                              promise.then(v => {
                              	return anotherPromise;
                              }).then(v2 => {
                              	return someAnotherPromiseAgain;
                              }).then(v3 => {
                              	…
                              });

                              Если бы он не ждал anotherPromise, то в v2 мы бы получали не значение, а мгновенно этот промис, который в данном случае бесполезен.

                              Или что-то типа такого:
                              fetch(…).then(res => res.json()).then(json => { … });

                              res.json() возвращает промис, потому что оно не может мгновенно вернуть json, потому что данные ещё не прочитаны из сети или читаются как поток. А мы хотим этот промис подождать.

                              С async-await это можно сделать так:
                              await (await fetch(…)).json();
                                0
                                Но даже тут в последнем примере мы блокируем функцию. Обычно это и требуется, но если вдруг не требуется, то чтобы получить итоговый промис, нам придётся либо выносить в отдельную функцию, либо использовать чейнинг. Можно и без чейнинга, но это было бы менее удобно.

                                В общем, логика есть. Предполагается, что мало кто захочет возвращать сам промис как значение, а вот чтобы функция его подождала — захотят действительно многие, потому что иногда это бывает очень полезно (особенно когда нет async-await — там без этого почти никак).
                      +2
                      Не совсем. Например при использовании try-catch делать return await очень даже оправдано:

                      async function t() { return Promise.reject(new Error('t')); }
                      async function a() {
                        try {
                        	return await t();
                        } catch(e) { console.log(`In a: ${e}`); }
                      }
                      async function b() {
                        try {
                        	return t();
                        } catch(e) { console.log(`In b: ${e}`); }
                      }
                      
                      a().catch(e => console.log(`Outside of a: ${e}`));
                      b().catch(e => console.log(`Outside of b: ${e}`));
                      
                      // In a: Error: t
                      // Outside of b: Error: t
                      


                      Получается что наобороть return без await в async функциях — антипаттерн. Можно потерять catch. Я понимаю что это довольно редкий случай, но это делает его еще более опастным.
                        0

                        Забавный нюанс, не знал и в дикой природе не встречал ни разу (к счастью?). Но если немного поразмыслить, то a и b имеют немного разное поведение. Мне вот например не кажется очевидным, что во всех случаях надо ловить исключение, которое происходит в функции, промис из которой я возвращаю (function a).


                        Но пример годный :)

                      0
                      ru_vds Надо исправить, люди же читают и запоминают неправильно!
                      0
                      Отличная, статья. Без воды, как в принципе и все остальные Ваши статьи.
                        0
                        Я так понимаю что «Слияние Объектов» это то же самое что Object.assign?
                        Можно ли сделать вот так, чтоб не мутировать object1 и object2 или они и так не мутируют?

                        const object3 = { {}, ...object1, ...object2}


                        UPD: проверил, объекты не мутируют, крутая фича.
                          +2

                          Первый пустой объект вам не нужен, он и так уже как бы объявлен окружающими скобками


                          const object3 = { ...object1, ...object2 }

                          Babel как раз это разложит в Object.assign({}, object1, object2). Пример

                            0

                            Да, стандарт требует одинакового поведения для {...a, ...b} и Object.assign({}, a, b)

                            +2

                            В первом примере довольно не очевидно когда вызывается функция required() — в момент объявления add или в момент вызова. Поэтому я предпочту не запутывать себя и буду контролировать аргументы внутри функции.

                              +1
                              В момент вызова. Просто перечитайте доки.
                                0
                                Доки доками конечно, но когда одна и та же конструкция работает в разных контекстах по-разному — не очень хороший подход, как мне кажется. Просто потому, что это действительно запутывает. Ожидается одно поведение, а на практике оно другое, только из-за того, что контекст другой. По мне, лучше такие вещи не использовать, чтобы не запутывать ни себя, ни коллег, которые потом в таком коде будут разбираться.
                                  0
                                  Не понял Вас. Оно всегда в момент вызова. Или Вы про другие языки?
                                    0

                                    Я про то, что поведение в данном контексте совсем неочевидно. В данном случае каждая функция, привязанная к аргументу как значение по умолчанию, выполняется каждый раз (или один раз?) при необходимости посчитать значение по умолчанию для аргумента, а не единожды в момент создания искомой функции с этими аргументами. И если в любом другом контексте выражение имя_функции() имеет однозначную трактовку, а именно исполнение кода этой функции сразу, как только интерпретатор добирается до этого места в коде, то в данном контексте оно так не работает.
                                    Вдвойне запутывает ситуация, когда надо вычислить значения аргументов сразу при инициализации функции. Предположу, что это делается как-то так


                                    const required = (p1, p2) => {...};
                                    defaultA = required(v1, v2);
                                    defaultB = required(v3, v4);
                                    const add = (a = defaultA, b = defaultB) => a + b;

                                    Но почему не тем синтаксисом, что описан в статье? Казалось бы, это же поведение конструкции по умолчанию.
                                    По сути тут меняется поведение базовой конструкции в зависимости от контекста, что-то типа "оно работает так, но вот в этом конкретном случае — по-другому". Это ведёт к тому, что вам нужно ещё и контекст контролировать, что добавляет дополнительную бессмысленную сложность при чтении кода. И это при том, что во всех остальных языках, в которых такая конструкция именно вызывает функцию, это поведение соблюдается везде, независимо от контекста. И нет никакой двоякой трактовки, конструкция везде работает одинаково — выполняет функцию в том месте, где встречается это выражение.
                                    Почему например, нельзя было придумать новый синтаксис для выполнения функции в контексте аргументов по умолчанию? Такое же уже делали (arrow functions, например). Тогда бы каждая конструкция однозначно трактовалась независимо от контекста. Как пример,


                                    const required = () => {...};
                                    const add = (a = @required, b = @required) => a + b;

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

                                      0
                                      Я же говорю, в js оно вычисляется заново при каждом вызове функции. Вы что-то перепутали. Смотрите здесь раздел «Вычисляются во время вызова».

                                      поведение меняется в зависимости от контекста
                                      В том то и дело, что здесь поведение никогда не меняется: оно всегда вычисляется в момент вызова функции, и так в любом контексте. Если функция была вызвана 100 раз, то и будет вычислено 100 раз. Вы просто что-то перепутали.
                                        0

                                        Да, вы правы. Все выражение вычисляется каждый раз по-новому, а не только при создании функции. Мне почему-то казалось, что это делается только для функций. Вот такая конструкция работает ожидаемо


                                        let a = 1;
                                        let b = 2;
                                        let fn = (c = a + b) => c;
                                        fn() == 3
                                        a = 2
                                        fn() == 4

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


                                        let a = 1;
                                        let b = 2;
                                        let argA = a; // только так безопасно далее менять a
                                        let fn = (c = argA + b) => c;
                                        ...
                                        a = 2; // не скажется на результате вычисления функции

                                        Выглядит, как костыль. Ну, а с вложенными функциями вообще беда получается


                                        let fn = function (c = a + b) { 
                                            let fn1 = (k = c + a) => k;
                                            ... // тут мы что-то могли сделать с 'c'
                                            return fn1();
                                        }
                                        ... // ну и тут мы что-то могли сделать с 'a' и 'b' дополнительно

                                        Что в итоге получится, никому не известно.
                                        Как мне кажется, было бы удобнее и очевиднее, если бы значение вычислялось единожды при создании функции. А если нужна динамика, просто передаете в качестве аргумента функцию, указывая ее с новым синтаксисом, типа


                                        let argFn = () => 1;
                                        let fn = (c = @argA) => c;

                                        или разделить выполнение сразу и на каждый вызов, типа


                                        let fn = (c = a + b) => c; // сразу
                                        let fn = (c = {a + b}) => c; // на каждый вызов

                                        Это примеры, синтаксис можно придумать любой. Основная суть в том, чтобы добавить гибкости. Но все вышеперечисленное — это конечно, ИМХО.

                                          +1
                                          Потому что в качестве значения по умолчанию может передаваться массив или объект, и если он будет тем же самым, это совсем не то, что ожидает программист.

                                          Ну а если нужны те же самые, никто не мешает сохранить во внешнюю переменную.

                                          А те минусы, которые Вы привели — это просто от плохого знания замыканий. Для тех, кто давно пишет на js или других языках с замыканиями, такие вещи более чем очевидны. А вот кто недавно познакомился, пока ещё могут плохо понимать. Это не минус js. Сами по себе замыкания иногда очень полезны (без них не получится работать с функциями как с объектом), просто не нужно ожидать, что все языки одинаковые. Можете считать, что в js просто более развитое ООП (хотя приватные свойства, да, нельзя).

                                          Ну и если Вы боитесь, что значение переменной может измениться, слово const вместо let тоже никто не отменял.
                                            0
                                            Потому что в качестве значения по умолчанию может передаваться массив или объект, и если он будет тем же самым, это совсем не то, что ожидает программист.

                                            Вот тут не совсем я вас понял. Разве в JS все сложные объекты передаются не по ссылке? То есть при указании объекта в качестве параметра по умолчанию это будет один и тот же объект в любом случае. Ну просто потому, что сложные объекты иначе не передаются, только по ссылке. А теперь это поведение добавлено еще и для простых типов в качестве значений по умолчанию (через замыкание, как вы и написали ниже).


                                            Ну а если нужны те же самые, никто не мешает сохранить во внешнюю переменную.

                                            Можете привести пример кода. Не совсем понятно, кусочек кода многое бы прояснил) Спасибо


                                            А те минусы, которые Вы привели — это просто от плохого знания замыканий.

                                            Я представляю, как работают замыкания) Но есть подход, когда замыкание используется явно, то есть


                                            let a = 1;
                                            let fn = function () {
                                                let c = a + 1;
                                            }

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


                                            let a = 1;
                                            let independentA = a;
                                            let fn = function (k = independentA) {
                                                ...
                                            }

                                            А взять именно значение из 'a', не привязываясь к 'a' по ссылке никак по-другому нельзя без копирования значения в отдельную переменную и уже использование ее в качестве значения по умолчанию.
                                            Если вы напишете код, которые берет именно значение из 'а', при этом не привязываясь к самой ссылке 'a', я буду вам благодарен.


                                            Ну и если Вы боитесь, что значение переменной может измениться, слово const вместо let тоже никто не отменял.

                                            Я правильно понимаю, что чтобы задать именно значение по умолчанию для аргумента, вы предлагаете скопировать значение из переменной в константу и уже эту константу использовать в качестве значения по умолчанию?) Что-то вроде этого?


                                            const argA = a;
                                            fn = function (b = argA) {
                                                ...
                                            }

                                            В Python это без всяких костылей делается


                                            a = 1
                                            def fn(b = a):
                                                return b
                                            
                                            fn() == 1
                                            a = 10
                                            fn() == 1

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


                                            a = 1
                                            def fn(b = None):
                                                if b is None:
                                                    b = a
                                                return b
                                            
                                            fn() == 1
                                            a = 10
                                            fn() == 10
                                              +1
                                              Вот тут не совсем я вас понял. Разве в JS все сложные объекты передаются не по ссылке? То есть при указании объекта в качестве параметра по умолчанию это будет один и тот же объект в любом случае. Ну просто потому, что сложные объекты иначе не передаются, только по ссылке. А теперь это поведение добавлено еще и для простых типов в качестве значений по умолчанию (через замыкание, как вы и написали ниже).
                                              В том то и дело, что в параметрах по умолчанию объект создаётся каждый раз заново.

                                              function test(a = []) {
                                              	a.push(6, 7);
                                              	console.log(a); //Всегда будет выводить [6, 7], а не [6, 7, 6, 7, 6, 7…]
                                              }

                                              Если бы значение вычислялось один раз, то выводилось бы [6, 7, 6, 7, 6, 7…], а так каждый раз выводится [6, 7].

                                              Все сложные объекты передаются по ссылке
                                              В js все объекты передаются по ссылке вне зависимости от сложности.

                                              Если ему хочется передать именно значение из переменной как значение аргумента по умолчанию, а не как ссылку на объект
                                              Да, с помощью переменной в замыкании:
                                              const cachedA = calc(1,2,3,4,5);
                                              
                                              function test(a = cachedA) {
                                              	//…
                                              }

                                              Конечно же, если test обёрнут в другую функцию, и эта функция вызывается много раз, то cachedA может быть разным. Но в одном конкретно взятом контексте оно никогда не меняется и не вычисляется заново.

                                              Но есть подход, когда замыкание используется явно. А в случае аргументов по умолчанию у программиста нет выбора.
                                              Эм, вообще-то аргументы по умолчанию — это просто синтаксический сахар. Например, такая функция
                                              function test(a = [], b = 5) {
                                              	//…
                                              }

                                              полностью эквивалентна такой:
                                              function test(a, b) {
                                              	if (a === undefined) a = [];
                                              	if (b === undefined) b = 5;
                                              	
                                              	//…
                                              }

                                              Если вы напишете код, которые берет именно значение из 'а', при этом не привязываясь к самой ссылке 'a', я буду вам благодарен.
                                              Что? Как можно взять значение из a, не обратившись к нему?

                                              В Python это без всяких костылей делается
                                              Без костылей? Да. А как тогда без костылей сделать в питоне так, чтобы поведение было обратным? КАК? Вы ниже написали как, но Вам пришлось нагородить такие костыли, что аж страшно…) С js'ом это явно не сравнится =)
                                                –2
                                                В js все объекты передаются по ссылке вне зависимости от сложности.

                                                Если бы было так, как вы написали, то было бы так


                                                let a = 1;
                                                let b = a;
                                                a = 2;
                                                b == 2;  // true

                                                однако, оно не так) Потому что это присвоение по значению и работает оно только для объектов простых типов. Для сложных типа array, object передается по ссылке. Хотя, мне кажется, это вам итак известно, и почему вы такую чушь написали, для меня загадка.


                                                Что? Как можно взять значение из a, не обратившись к нему?

                                                Я разве вам предлагал не обращаться к нему? Если я такое написал, позор мне, ткните меня пальцем в мой же текст)


                                                Эм, вообще-то аргументы по умолчанию — это просто синтаксический сахар.

                                                Ааа, ну вот сразу бы так и написали, что это обычный синтаксический сахар для костылей, а не нормальное определение значений по умолчанию, как в других языках. Тогда вопрос закрыт)

                                                  0
                                                  Если бы было так, как вы написали, то было бы так
                                                  Вы абсолютно неправы. Смотрите, в первой строке Вы присваиваете a ссылку на 1, во второй тоже ссылку на один.

                                                  В данный момент a хранит ссылку на 1 и b хранит ссылку на 1. Дальше Вы в a присваиваете ссылку на 2. Но в b так и осталась ссылка на 1. Далее, когда Вы сравниваете a == b, Вы сравниваете ссылку на 1 и ссылку на 2 — очевидно, что ответ будет false.

                                                  Ну а вообще объект — это всё, кроме числа. Числа в js тоже можно рассматривать как объекты, но которые не могут иметь несколько одинаковых дубликатов. Аналогично и со строками (внутри движка они на самом деле иногда могут, но при сравнении проверяется, что они точно не дубликаты).

                                                  Ааа, ну вот сразу бы так и написали, что это обычный синтаксический сахар для костылей
                                                  Поправочка: сахар для костылей, который делает их не костылями.
                                                    0
                                                    В данный момент a хранит ссылку на 1 и b хранит ссылку на 1. Дальше Вы в a присваиваете ссылку на 2. Но в b так и осталась ссылка на 1.

                                                    Ваша правда. Напутал все на свете) Мутабельность объекта перепутал с иммутабельностью строк и чисел) Ну вроде того


                                                    let a = {"k": 1};
                                                    let b = a;
                                                    a["k"] = 2;
                                                    b["k"] == 2;  // true

                                                    Вечер пятницы) Голова уж плохо соображает, прошу прощения)
                                                    Как вы и указали, это просто синтаксический сахар, поэтому ожидать от него поведения, отличающегося от оригинала "без сахара" глупо. Это все объясняет. Мне почему-то сначала показалось, что новую возможность завезли.

                                            0
                                            Как мне кажется, было бы удобнее и очевиднее, если бы значение вычислялось единожды при создании функции.

                                            Мне так не кажется. Если я ввожу выражение, я ожидаю, что оно будет вычисляться каждый раз, а не при компиляции.

                                            То есть при указании объекта в качестве параметра по умолчанию это будет один и тот же объект в любом случае.

                                            При указании ССЫЛКИ на объект в качестве значения по умолчанию — значением будет эта же самая ссылка, что естественно.
                                            А вот если я введу литерал объекта — объект будет создаваться каждый раз новый. Что, опять-таки, естественно.

                                            let fn = (c = {})=>(c);
                                            let a = fn(), b = fn();
                                            
                                            a==b// false


                                            А взять именно значение из 'a', не привязываясь к 'a' по ссылке никак по-другому нельзя без копирования значения в отдельную переменную и уже использование ее в качестве значения по умолчанию.


                                            Простите, Вы давно на JS пишете? А то прямо как будто про задачку с собеседования говорите.
                                            «Напишите функцию, которая создаст массив из N функций, каждая из которых возвращает всегда одно и то же число, равное её номеру в упомянутом массиве».

                                            Если мне потребуется такое странное поведение, которое Вы написали, я, естественно, оберну функцию в замыкание.
                                            Вот только скажите, для чего может потребоваться такое поведение? Реальный пример использования?
                                              0

                                              Задача сделать чистую функцию fn, чтобы она взяла значение из 'a' и использовала его всегда в дальнейшем, независимо от дальнейшего изменения 'а', то есть не зависела от внешнего контекста. Внимание, нужно именно первоначальное значение из 'а', потому что функция должна быть чистая, то есть независимо от внешнего контекста всегда возвращать один и то же результат при одинаковых входных параметрах. Иначе функция подвержена сайд эффекту из контекста выше, а нам это не нужно по задаче.


                                              let a = 1;
                                              let fn = function (c = a) {
                                                return c + 1;
                                              }
                                              fn() == 1;
                                              a = 10;
                                              fn() == 11;

                                              Представьте, что значение в 'а' нам не известно на этапе написания кода, что вполне логично, это ж переменная.

                                                0
                                                Может я чего-то не догоняю, но это мной воспринимается как какой-то треш.
                                                Поделитесь каким-нибудь менее абстрактным примером.
                                                В каком случае имеет смысл написать
                                                let a = 1;
                                                let fn = function (c = a) {
                                                  return c + 1;
                                                }

                                                Но нельзя написать
                                                let a = 1;
                                                let fn = function (c = 1) {
                                                  return c + 1;
                                                }

                                                или
                                                let a = ()=>(1);
                                                let fn = function (c = a()) {
                                                  return c + 1;
                                                }

                                                ?
                                                  0

                                                  Ну хорошо, перепишу пример, чтобы было понятно, что это именно ПЕРЕМЕННАЯ, а не значение именно 1, которые вы пишете) Вот так понятнее?


                                                  let a = $input.val();
                                                  let fn = function (c = a) {
                                                    return c + 1;
                                                  }

                                                  И вы кстати так и решили поставленную задачу)
                                                  В примере выше функция всегда будет выдавать разное значение при изменении переменной a. Если бы присвоение аргументу значения по умолчанию выполнялось единожды при создании функции, то этой проблемы бы не было) Но оно через замыкание выполняется каждый раз при вызове фукнции. Впрочем, выше уже написали, что это синтаксический сахар, а не новая конструкция языка. А я уж думал...

                                                    –1
                                                    И вы кстати так и решили поставленную задачу)

                                                    И не намерен, потому что саму постановку такой задачи считаю безумием.
                                                    Если мне нужна переменная, чтобы хранить некоторую настройку — например, значение по умолчанию для группы функций, то эта переменная должна использоваться ТОЛЬКО для хранения этой настройки.
                                                    Если же хранить эту настройку на протяжение всего времени работы программы не нужно — то и переменная не нужна.
                                +4
                                2.3. Использование reduce для анализа расстановки скобок

                                Конечно понятно, что приведённые пункты с reduce — это показание возможностей, а не советы, но раз уж на то пошло, возможно, вместо
                                return str.split('').reduce((counter, char) => {

                                стоит написать:
                                return [...str].reduce((counter, char) => {

                                Хотя результат не изменится, т. к. при подсчёте скобок символы с кодом больше 65535 не повлияют (работать тоже, возможно, будет медленнее). Но зато наглядно показываются возможности ES6, а также делается намёк на существование суррогатных пар.

                                2.4. Подсчёт количества совпадающих значений массива

                                Здесь три обращения к хэшу (когда элемент дублируется):
                                obj[name] = obj[name] ? ++obj[name] : 1;

                                Поэтому правильнее написать так:
                                obj[name] = (obj[name] || 0) + 1;

                                Это два обращения к хэшу в любых случаях.
                                  +6

                                  Всё-таки, применение reduce во многих случаях спорно, особенно, в качестве замены map-filter. Если хотим производительности, то for уделяет их всех на две головы, если хотим наглядности — map-filter нагляднее.

                                    –3
                                    reduce работает быстрее чем for. Вот к примеру, результат времени работы reduce и for, в массиве из 10.000.000 где каждое значение умножается само на себя:
                                    Время выполнения для reduce ~= 163.312ms
                                    Время выполнения for ~= 282.706ms
                                      +4
                                      На код можно взглянуть, с помощью которого сделан замер?
                                        –2
                                        Это конечно не самый идеальный вариант кода, но все же…
                                        JS
                                        const now = require('performance-now');
                                        
                                        let array = [];
                                        for(let i = 0; i < 10000000; i++) {
                                        	array.push(i);
                                        }
                                        
                                        let start = now();
                                        var arr2 = array.reduce((num, item) => {
                                        	return num * num;
                                        }, 1);
                                        let end = now();
                                        console.log('Время выполнения = ' + (end - start).toFixed(3));
                                        
                                        start = now();
                                        let arr3 = [];
                                        for(let i = 0; i < 10000000; i++) {
                                        	arr3.push(i * i);
                                        }
                                        end = now();
                                        console.log('Время выполнения = ' + (end - start).toFixed(3));
                                        

                                          +4

                                          У вас же две разных функциональности в коде


                                          В reduce вы считаете факториал 10000000. Значение arr2 будет числом


                                          var arr2 = array.reduce((num, item) => {
                                              return num * num;
                                          }, 1);

                                          Во втором случае у вас будет массив с квадратами исходных чисел


                                          let arr3 = [];
                                          for(let i = 0; i < 10000000; i++) {
                                              arr3.push(i * i);
                                          }

                                          Это не разные операции, дают совсем разный результат, сравнивать их бессмысленно

                                            0
                                            вот блин, точно. Не заметил сразу -_- надо же не num, а item умножать и + то что число в итоге выходит. Тогда да, for шустрее
                                              0

                                              У меня с reduce тут совсем ужас получился

                                                0
                                                push вообще медленная штука, т. к. эквивалентен 5 операциям (по крайней мере в V8, который делает запас по длине массива 25%).

                                                PS. Это во всех языках так, где есть динамический массив.

                                                А в js ещё при записи в динамический массив всякие проверки добавляться могут (хотя бы на длину массива).
                                        +2
                                        Плюсую. За исключением самых простых случаев, reduce всегда очень неочевиден для понимания. Как возможность круто, но в реальном коде я бы его избегал.
                                        0
                                        2.1 как-то совсем не по нраву, особенно учитывая, что выпиливается декларативность, код для восприятия становится только сложнее

                                        Для reduce/reduceRight я бы привел в пример композицию методов для обратоки данных.
                                          +1

                                          Можно ещё и хитро маппить один объект в другой.
                                          Например, хотим из


                                          { catName: 'Alex', fur: { color: 'black', length: 3 }, catAge: 8 }

                                          Получить


                                          { type: 'cat', name: 'Alex', furColor: 'black', furLength: 3, age: 3 }

                                          И при этом чтобы в случае отсутствия какого-то из полей в итоговом объекте не было полей, содержащих undefined.


                                          Есть целых два способа записать это только деструктуризацией, без кучи многострочных ифов, в одну строку. Не скажу, что код получается сильно понятнее (хотя это больше дело привычки и знания такой конструкции), но сама возможность крутая.


                                          function mapAnimal(source){
                                            return {
                                              type: 'cat',
                                              ...(source.catName && {
                                                name: source.catName
                                              }),
                                              // и так далее
                                            };
                                          }

                                          Или даже как-то так:


                                          function mapAnimal(source){
                                            let {undefined, ...result} = {
                                              type: 'cat',
                                              [source.catName && 'name']: source.catName,
                                              [source.fur && source.fur.color && 'furColor']: source.fur.color
                                            };
                                            return result;
                                          }

                                          Можно ещё деструктурировать source прям в записи аргументов.

                                            +4
                                            Почему в примерах местами var, местами let, местами const?
                                            var вообще в ES6 не надо использовать. let только если значение переменной переопределяется в коде. Для всех примеров следовало использовать const.
                                              –1

                                              Чем вам var помешало?

                                                –1
                                                А скажите хоть один кейс когда лучше вместо let/const использовать var в ES6? Я ни одного не знаю и за все время работы с ES6 ни разу var не использовал. Наличие этого ключевого слова в спецификации обусловленно лишь обратной совместимостью с более ранними версиями. В современных гайдлайнах, вроде Airbnb, var не рекомендовано к использованию. Да и тема целесообразности его использования не раз поднималась во множестве статей и обсуждений, которые в большинстве своем сводятся к тому, что для var в ES6 нет места.
                                                  +2

                                                  Скорее всего вам ответят что-нибудь вроде:


                                                  try
                                                  {
                                                    // code
                                                    var some = any();
                                                    // code
                                                  }
                                                  catch(err)
                                                  {
                                                    if(some)
                                                      some.dispose();
                                                  }

                                                  Мотивируя это тем, что без var придётся выносить let some; на уровень выше try-catch и тем самым плодить "лишние строчки".


                                                  P.S.: По моему скромному мнению — строчки экономить не нужно. И предварительная декларация такого рода переменных лучше, чем по месту.

                                                    0
                                                    Кстати да, с самого начала интересно почему не предусмотрели этот момент. Можно же считать try-catch одним «блоком», с одной областью видимости. Не может же быть try без catch, или наоборот.
                                                      –1
                                                      Эмм, раз уж вы взялись предсказывать мой ответ, почему тогда try-catch, а не if-else?
                                                        0

                                                        А зачем вам var в if-else? В нём выполнится только 1 блок (либо if, либо else), что вы там share-ить будете? :) И предсказывал я скорее не ваш ответ, а в целом ответ тех, кто использует var, к примеру vintage.

                                                          +1
                                                          var Foo = Foo || class Foo {}

                                                          try {
                                                              var foo = bar()
                                                              lol()
                                                          } finally {
                                                              return foo
                                                          }

                                                          if( foo ) {
                                                              var bar = lol()
                                                          } else {
                                                              var bar = zol()
                                                          }
                                                          console.log( bar )

                                                          for( var i = 0 ; i < list.length ; ++ i ) {
                                                              if( foo( i ) ) break
                                                              bar()
                                                          }
                                                          
                                                          return list.slice( i )
                                                            0

                                                            Да, точно, спасибо за примеры. О таком применении var в if-else я уже и забыл, слишком давно не использую.

                                                              +1
                                                              Только в таком применении по сравнению с этим:
                                                              let bar;
                                                              if(foo) {
                                                                bar = lol();
                                                              } else {
                                                                bar = zol();
                                                              }
                                                              cosole.log(bar);
                                                              

                                                              кода меньше ровно на один символ переноса строки. А гораздо читаемей, по моему скромному мнению, будет так:
                                                              const bar = foo ? lol() : zol();
                                                              
                                                              console.log(bar);
                                                              
                                                                0

                                                                Так товарищи выше не символы экономят. Не-не. Они строчки экономят. Дескать чем меньше строк, тем больше на экран влезет. Ну и тем меньше глазами бегать искать чего-то. На мой взгляд вынесенная отдельно let bar даёт чётко понять, что она там не спроста, и её судьба будет определена вариативно чуть ниже. Я за let :)


                                                                А гораздо читаемей, по моему скромному мнению, будет так

                                                                Ну тут вы уже нечестно играете. В реальном коде там будут не только bar = lol(); и bar = zol();. Там будет много всего, и тернарника вам не хватит :) Тернарный оператор вообще очень быстро становится не читаемой грудой мусора, с вовлечением чего-нибудь не тривиального. Всякие там аргументы, выражения и пр… Я вот жду do-нотации. Она выглядит вкусно.

                                                                  –2
                                                                  Так товарищи выше не символы экономят. Не-не. Они строчки экономят. Дескать чем меньше строк, тем больше на экран влезет. Ну и тем меньше глазами бегать искать чего-то.

                                                                  Зачем вы продолжаете пытаться читать чужие мысли, если вам уже сказали, что у вас не получается?
                                                      –2
                                                      Вы зачем-то расширили моё утверждение. Я разве где-то утверждал, что он в чём-то лучше? Я скорее не понимаю, почему все стремятся использовать let вместо var везде, где нужно и где не нужно, если ошибки, связанные с var, попадаются очень редко, очень легко ловятся, и вообще достигаются, в основном, новичками в языке, не потрудившимися прочитать, как оно тут работает.
                                                      Это примерно как возмутиться, что у массивов могут быть повторяющиеся значения, и поэтому рекомендовать всегда вместо него использовать Set.

                                                      Моё мнение: они вполне равноправны. Да, где-то let бывает писать удобнее. Но ведь где-то и с var такая же ситуация! (а вот теперь утверждаю, да: всплытие на уровне функции действительно бывает удобным)
                                                        0
                                                        всплытие на уровне функции действительно бывает удобным

                                                        Эта тема немного холиварная. Большинство из тех, кто не использует var, считают, что "удобство" в ущерб очевидности, однозначности, понятности кода — скорее зло. В случае var едва ли оно обоснованное. До того как let и const в языке появились, это была частая причина поливать JS грязью.


                                                        Кстати, в PHP вообще даже var писать не нужно. Просто пишешь $mySuperVariable = и радуешься жизни. "Удобно" :)

                                                          0
                                                          Хорошо. Объясните где, по-вашему, в ES6 нужно использовать var, где не нужно использовать const/let?
                                                            0
                                                            Любое подобное утверждение (в том числе и «всегда используйте let») — это в немалой степени вкусовщина. Ну потому что по сути ничего не меняется кроме «мне кажется, что так красивее».

                                                            Моё имхо насчёт использования следующее:

                                                            1. const для любых констант, которые вряд ли может понадобиться менять. Например, число пи. Не могу представить адекватной ситуации, когда бы мне захотелось изменить значение этой константы.
                                                            Но при этом не использовать для всех-всех значений, которые не меняются конкретно в этом коде (мы можем что-нибудь захотеть изменить в коде, и нам придётся менять определение переменной).
                                                            2. let — когда видимость внутри блока существенно важна. Например, в цикле for, если внутри setTimeout.
                                                            3. var — во всех остальных случаях.
                                                              +1
                                                              Но при этом не использовать для всех-всех значений, которые не меняются конкретно в этом коде (мы можем что-нибудь захотеть изменить в коде, и нам придётся менять определение переменной).
                                                              Так это же плюс, а не минус! Увидел переменную в виде const — и знаешь что дальше в коде гарантировано она изменится. Опыт показывает что часто при дебаге чужого кода приходится перелистывать весь код, чтобы понять не изменили ли где-то по пути такую-то переменную. А если начать использовать const — то вы удивитесь, как редко, на самом деле, вам нужны изменяемые переменные кроме как в циклах да в аргументах функций.

                                                              2. let — когда видимость внутри блока существенно важна. Например, в цикле for, если внутри setTimeout.
                                                              3. var — во всех остальных случаях.
                                                              Ооочень спорно. Если это не говнокод на кучке глобальных переменных — то зачем ему может понадобится var? Чтобы отлавливать лишние баги?
                                                                0
                                                                Если это не говнокод на кучке глобальных переменных — то зачем ему может понадобится var?

                                                                Для красоты. Чтобы явно обозначить переменную, объявленную на уровне процедуры. Хотя, в принципе, в этом случае использования, никакой разницы с let нет.
                                                                  –2
                                                                  Опыт показывает что часто при дебаге чужого кода приходится перелистывать весь код, чтобы понять не изменили ли где-то по пути такую-то переменную. А если начать использовать const — то вы удивитесь, как редко, на самом деле, вам нужны изменяемые переменные кроме как в циклах да в аргументах функций.

                                                                  Вы удивитесь, но у меня ровно противоположный опыт. Смотреть, не изменилась ли переменная, приходится примерно никогда (да и зачем, если можно исполнить код и вывести её в консоль?), зато почти каждый день приходится лезть выше по коду и исправлять const на let, потому что модифицирую код, и внезапно теперь какую-то переменную нужно менять.
                                                                  А уж в консоли вообще весело. Объявишь какую-нибудь переменную по привычке через const, а потом чертыхаешься, потому что её понадобилось поменять, а уже никак.

                                                                  Если это не говнокод на кучке глобальных переменных — то зачем ему может понадобится var?

                                                                  ться.
                                                                  Для того, чтобы объявлять переменные. Ещё раз: от замены let на var в коде почти всегда не меняется примерно ничего. Хотя можете привести какой-нибудь пример, когда у вас реальный настоящий код, скажем, от замены var на let, стал в 2 раза чище и короче. Есть такие примеры?
                                                                  Никаких багов не будет, если вы знаете, как работает var.
                                                          0

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

                                                            +1

                                                            Вот вам задачка для собеседования на одних let-ах:


                                                            let x = 1
                                                            switch( x ) {
                                                                case 1 :
                                                                    let x = 2
                                                                    console.info( x )
                                                                    break
                                                                case 2 :
                                                                    let x = 3
                                                                    console.debug( x )
                                                                    break
                                                                default :
                                                                    console.log( x )
                                                            }
                                                        0
                                                        П.3.1 — несет кучу мусора, изначально непонятно зачем присвоение в переменные, которые потом не используются. А вот 2.3 порадовало, спасибо)
                                                          0
                                                          3.2. Деструктурирование вложенных объектов в параметрах функции — небезопасно когда более одной вложенности
                                                          car={'model': 'повозка'}
                                                          
                                                          modelAndVIN = ({model, engine: {vin}}) => {
                                                            console.log(`model: ${model} vin: ${vin}`);
                                                          }
                                                          
                                                          modelAndVIN(car)
                                                          Uncaught TypeError: Cannot destructure property `vin` of 'undefined' or 'null'.
                                                          
                                                            0
                                                            Да. Поэтому, если и делать так, то для всех верхних узлов нужно указывать значение по-умолчанию:
                                                            car={'model': 'повозка'}
                                                            
                                                            modelAndVIN = ({model, engine: {vin} = {}}) => {
                                                              console.log(`model: ${model} vin: ${vin}`);
                                                            }
                                                            
                                                            modelAndVIN(car)

                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                          Самое читаемое