Основы ES 6


2015 год - появление современного JavaScript. принят стандарт EcmaScript 6, он же EcmaScript 2015. До этого последний раз JavaScript обновлялся двенадцать лет назад — в 2009 году, когда появился EcmaScript5. Впрочем, тогда изменения были не такими значительными.

Нововведения EcmaScript 6

     Ключевые слова let и const

    Основное их отличие — блочная область видимости.
    При этом let — переменная, а const — переменная, значение которой не меняется.
    Впрочем, про не меняется значение, это не всегда справедливо. Написать const a = 1; a++; нельзя. а сonst a = 1; return a + 1; — можно. Здесь главное не присваивать константе другое значение (а++ это то же самое, что а = а + 1). Соответственно, массивы, объявленные через const, могут изменяться с использованием метода push, и такой код. меняющий массив, объявленый через сonst, тоже будет вполне валидным

    const arr = [1, 1, 1, 1];

    arr.map(a => a + 1);   // 
    [2, 2, 2, 2];

    Чем не угодил var


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

    В качестве преимущества использования let традиционно приводят  такой код:

    for(var i = 0; i < 3; i++) {
      setTimeout(function(){
        console.log(i)
      }, 100)
    }


    Результатом будут три тройки.
    Аналогичный код с объявлением переменной через let выведет числа от 0 до 3:

     for(let i = 0; i < 3; i++) {
      setTimeout(function(){
        console.log(i)
      }, 100)
    }


    Впрочем, аргументы против всеобщего перехода на let & const тоже серьёзные. Один из них - необходимость поддерживать старые браузеры, в которых новый код работать не будет вообще. Не ухудшит внешний вид, как, например, использование неподдерживаемых css-свойств,  а приведёт к полной неработоспособности сайта.

    Стрелочные функции

     В JavaScript функции являются объектами. Это удобно. Можно их присваивать переменным, передавать в качестве параметров в другие функции, возвращать в качестве результата выполнения других функций. Но необходимость использования ключевых слов function и
    return увеличивала количество кода.
    рассмотрим функцию:

    function square(a) {
      return a * a;
    }


    Всё, что она делает - возводит переданный параметр в квадрат. Её полезное действие описывается тремя символами a * a. Всё остальное - необходимость следовать синтаксису языка.
    Тот же код, записаннй при помощи стрелочной функции стал короче и проще:

    const square = a => a * a;

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

    var arr = ["1", "2", "3", "4", "5"];

    var res = arr.map(a => +a)
                 .filter(a => a%2)
                 .reduce((max, val) => Math.max(max, val), 0);


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

    *Дополнено. 

    Для поиска максимального значения автор предлагает использовать функцию

    arr.reduce((max, val) => Math.max(max, val), 0);
     
    На самом деле можно намного проще:

    Math.max.apply(null, arr)

    И ещё проще:

    Math.max(...arr

    Справедливости ради отмечу, что перечисленные подходы рассматриваются в других видео.


    Ещё одна особенность стрелочных функций - способность сохранять контекст, который был доступный в исходной функции.
    Здесь можно вспомнить тему по основам ООП в которой для передачи контекста во внутренню функцию предлагалось использовать методы bind, call,apply, или кешировать контекст через переменную that.
    Ещё один способ - использовать стрелочную функцию.
    Пример. Обычная функция:

    var a = {
      prop: "a",
      f: function () {
        var fn = function (){    

          console.log(this.prop);
        };
        fn();
      }
    };

    a.f() // undefined


    Стрелочная функция 

    var a = {
      prop: "a",
      f: function () {
        var fn = () => {    

          console.log(this.prop);
        };
        fn();
      }
    };

    a.f() 
    // а


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

    Параметры по умолчанию



    Позволяют вызывать функцию с параметрами или без. Во втором случае подставится параметр по умолчанию.

    Пример:

    function fn(count = 10, start = 1) {
      return start * count;
    }

    fn()  //  10
    fn(undefined, 2)  // 20
    fn(3, 5)  // 15

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

    Rest параметр

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

    function max() {
      var numbers = Array.from(arguments);
      var numMax = Math.max(...numbers);
      return numMax;
    }

    max(1,2,3,4,5);


    Собственно, до 2015 года всё было совсем иначе.
    Строка Array.from(arguments);, которая позволяет перевести псевдомассив в массив, на самом деле до 2015 года выглядела как Array.prototype.slice.call(arguments);.
    Строка Math.max(...numbers);, которая ищет максимальное значение в массиве, выглядела как Math.max.apply(null, numbers).
    Зато уже тогда был  псевдомассив arguments. Хоть что-то.

    Сейчас псевдомассив arguments остался, но дополнительно появился rest-параметр. Выглядит так

    function max(...numbers) {
      return
    numbers;
    }

    max(1,2,3,4,5);  //  [1, 2, 3, 4, 5]



    и представляет собой обычный настоящий массив, а не псевдомассив.

    Кстати, если при запуске функции будет только один аргумент, он всё равно будет единственным элементом массива, а если запускать функцию без аргументов, в rest-параметре будет пустой массив.


    Перед rest-параметром можно указать обычные параметры функции.

    function max(a, b, ...numbers) {
      return
    numbers;
    }

    max(1,2,3);  //  [3]


    Ограничения при работе с rest-параметрами

    1. rest-параметр должен идти последним в функции
    2. нельзя указывать больше одного rest-параметра

    Оператор расширения (spread-оператор)

    spread-оператор раскладывает массив на список независимых элементов.
    Примеры использования:

    Объединение массивов:

    var arr = [3, 4];
    var arr1 = [1, 2, ...arr, 5, 6];


    Поиск максимального значения
    Из одного массива

    var arr = [1, 2, 3, 4, 5];
    var max = Math.max(...arr);


    Из двух массивов и дополнительных элементов

    var arr1 = [1, 2, 3, 4, 5];
    var arr2 = [6, 7, 8, 9, 10];

    var max = Math.max(...arr1, ...arr2, 11, 12);


    Копирование массива

    var arr = ['a', 'b', 'c'];
    var arr2 = [...arr];


    Преобразование строки в массив

    var str = "hello";
    var arr = [...str];


    Удаление дубликатов из массива.
    Вместо

    var arr = ['a', 1, 'a', 2, '1'];
    var unique = Array.from(new Set(arr))
    unique


    Можно написать

    var arr = ['a', 1, 'a', 2, '1'];
    var unique = [...new Set(arr)];
    unique



    Дополнительный источник:
    https://habr.com/ru/company/ruvds/blog/348612/

    Деструктуризация объектов

    Предположим у нас есть объект

    const person = {
      firstName: "Peter",
      lastName: "Smit",
      age: 23
    }



    И мы хотим получить из него имя и возраст. Раньше это делали так:

    const firstName = person.firstName;   
    const age = person.age;

    Результат:
    firstName   // "Peter"
    age             //  23

    Сейчас можно проще

    const person = {
      firstName: "Peter",
      lastName: "Smit",
      age: 23
    }

    const {firstName, age} = person;

    firstName   // "Peter"
    age         //  23

    Предположим, объект содержит внутренние объекты

    const person = {
      name: {
        first: "Peter",
        last: "Smit",
      },
      age: 23
    }


    Его деструктуризация выглядит так:

    const {name: {first, last}} = person;
    first    //     "Peter"


    Имена переменных должны точно повторят имена ключей объекта.
    Если есть необходимость переименовать переменные, это тоже можно сделать:

    const {name: {first: firstName, last: lastName}} = person;
    firstName    //     "Peter"

    Деструктуризация позволяет указать значения по умолчанию

    const {firstName, age, role = "user"} = person;
    role   //  "user"

    Одно из самых востребованных применений деструктуризации: деструктуризация аргументов функции.

    function connect({
      user = "guest",
      host = "localhost",
      port = 1234
      }) {
      console.log(user, host, port)
    }

    connect({});  //  guest localhost 1234

    connect({user: "admin"});  //  admin localhost 1234

    Но если вызвать функцию без аргументов, будет ошибка. Впрочем, её можно обойти, указав в параметрах функции аргументом по умолчанию пустой объект

    function connect({
      user = "guest",
      host = "localhost",
      port = 1234
      } =
    {}) {
      console.log(user, host, port)
    }

    connect();
    //  guest localhost 1234

    Rest-элементы при деструктуризации функций позволяют собрать в отдельный объект те элементы, которым не были присвоены переменные.

    const dict = {
      duck: "quack",
      dog: "woff",
      mouse: "squeak"
    }

    const {duck, ...other} =  dict;
    duck  //  "quack";

    other  //   {dog: "woff", mouse: "squeak"}

    Правила использования rest-элементов такие же, как и rest-параметров.
    Rest-элемент должен быть последним в списке и только один.

    Ещё один новый и удобный метод Object.entries(obj); 
    Метод превоащает объект в двумерный массив, в котором каждый вложеный массив имеет вид [key, value].

    Деструктуризация массивов

    Получим первое и второе значения массива arr

    var arr = [1, 2, 3, 4, 5];
    var [a, b] = arr;
    a  // 1
    b  // 2

    Получим второе и четвёртое значения

    var arr = [1, 2, 3, 4, 5];
    var [, b, , c] = arr;
    c  // 4

    Предположим. массив многоуровневый. Вот такой

    var arr = [[1, 2], [3, 4, 5]];

    Не проблема. Выведем средний элемент второго вложенного массива:

    var [[], [, c]] = arr;

    Значения по умолчанию работают, rest-элементы тоже работают.

    Шаблонные строки

    Соединяют переменные и текст. переменные в фигурных скобках с знаком $ перед ними, вся строка в косых кавычках

    var name = "Ann";
    `I love ${name}`   //  "I love Ann"

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

    Объекты

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

    var obj = {
      x: x,
      y: y
    }

    Стало:

    var obj = {x, y}

    Для обозначения функции - метода объекта не требуется ключ и слово function

    Было

    var obj {
      data: function() {
        // code
      }
    }

    Стало: 

    var obj {
      data() {
        // code
      }
    }

    Появилась функция, позволяющая объединить два объекта


    Object.assign(target, sources)

    Здесь
    target - целевой объект. в него записываются и перезаписываются свойства
    sources - источник (источники), свойства которого имеют более высокий приоритет.

    Если одно и то же свойство, но с разными значениями, есть и в target, и в sources, в целевой объект присвоится свойство sources.
    Если мы хотим создать новый объект, а не перезаписывать  target, синтаксис будет выглядеть так

    Object.assign({}, target, sources)

    Теперь в пустой объект записываются свойства target, а затем их дополняют и перезаписывают свойства sources.

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


    Object.assign удобно использовать для копирования объектов.
    Object.assign({}, sources)

    Spread-оператор для объектов

    Появился в 2018 году. работает аналогично Object.assign и позволяет объединить два объекта.
    Было:

    var res = Object.assign({}, target, sources);

    Стало

    var res = {...target, ...sources};

    ...





    Источник
    Юрий Бура. React + Redux - Профессиональная Разработка Ч.02.
    https://www.udemy.com/pro-react-redux/ 
    Дополнительная информация
    Плейлист. Основы ES 6
    https://www.youtube.com/playlist?list=PLqHlAwsJRxAOpWPtj2T6HhSzX-lKmKV2q