Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Новое в ECMAScript 6. Деструктурирующее присваивание, спред (spread) и рест (Rest) операторы

Лекция



Привет, Вы узнаете о том , что такое Новое в ECMAScript 6. Деструктурирующее присваивание, спред (spread) и рест (Rest) операторы, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое Новое в ECMAScript 6. Деструктурирующее присваивание, спред (spread) и рест (Rest) операторы , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend).

История

Новые добавления в язык называются ECMAScript 6. Или ES6 или ES2015+.

С момента появления в 1995, JavaScript развивался медленно. Новые возможности добавлялись каждые несколько лет. ECMAScript появился в 1997, его целью было направить развитие JavaScript в нужное русло. Выходили новые версии – ES3, ES5, ES6 и так далее.

Новое в ECMAScript 6. Деструктурирующее присваивание, спред (spread) и рест (Rest) операторы

Как видите, между версиями ES3, ES5 и ES6 есть пропуски длиной в 10 и 6 лет. Новая модель – делать маленькие изменения каждый год. Вместо того, чтобы накопить огромное количество изменений и выпустить их все за раз, как это было с ES6.

1 Деструктуризация

Деструктурирующее присваивание – это специальный синтаксис, который позволяет «распаковать» массивы или объекты в набор переменных, так как иногда они более удобны. Деструктуризация также прекрасно работает со сложными функциями, которые имеют много параметров, значений по умолчанию и так далее.

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

Пример деструктуризации массива:

 

// у нас есть массив с данными город, страна
let arr = ["Амстердам", "Нидерланды"]

// деструктурирующее присваивание
// записывает аналогично  city=arr[0], country=arr[1]
let [city, country] = arr;

console.log(city); 
console.log(country); 

выведет

Амстердам

Нидерланды

Теперь мы можем использовать переменные вместо элементов массива.

Отлично смотрится в сочетании со split или другими методами, возвращающими массив:

let [city, surname] = "Арсметрдам,Нидерланды".split(',');

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

ES5
var user = {firstName: 'Adrian', lastName: 'Mejia'};

function getFullName(user) {
  var firstName = user.firstName;
  var lastName = user.lastName;

  return firstName + ' ' + lastName;
}

console.log(getFullName(user)); // Adrian Mejia

То же самое (но короче):

ES6
const user = {firstName: 'Adrian', lastName: 'Mejia'};

function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

console.log(getFullName(user)); // Adrian Mejia

Глубокое сопоставление

ES5
function settings() {
  return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
}

var tmp = settings();
var displayColor = tmp.display.color;
var keyboardLayout = tmp.keyboard.layout;

console.log(displayColor, keyboardLayout); // red querty

То же самое (но короче):

ES6
function settings() {
  return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
}

const { display: { color: displayColor }, keyboard: { layout: keyboardLayout }} = settings();

console.log(displayColor, keyboardLayout); // red querty

Это также называют деструктуризацией объекта (object destructing).

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

Советы:

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

2. Операторы Spread и Rest Остаточные параметры и оператор расширения

Новый оператор ... называется spread (распростанение, расширение) или rest (остаток) в зависимости от того, где и как он используется.

Оператор spread

Начнем сразу с примера:

var log = function(a, b, c) {
  console.log(a, b, c);
};

log(...['Spread', 'Rest', 'Operator']); // Spread Rest Operator

В данном примере переданный в функцию массив разделяется на три значения: Spread, Rest и Operator, после чего передаются функции log. Таким образом, оператор ..., используемый перед массивом, или любой другой коллекцией значений (строки, объекты), называет spread.

Подобным образом использовать массив можно было и до появления оператора ... с помощью метода функций apply:

log.apply(null, [1, 2, 3]); // 1 2 3
// Равнозначно
log(...[1, 2, 3]); // 1 2 3

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

Использование оператора spread

Использование оператора spread не ограничивается передачей параметров функции. Несколько примеров его полезного использования:

Клонирование свойств массивов

var arr = ['will', 'love'];
var data = ['You', ...arr, 'spread', 'operator'];
console.log(data); // ['You', 'will', 'love', 'spread', 'operator']

Подобным образом можно скопировать и весь массив целиком

var arr = [1, 2, 3, 4, 5];
var data = [...arr];
console.log(data); // [1, 2, 3, 4, 5]

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

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

arr === data; // false − ссылки отличаются − два разных массива
arr === copy; // true − две переменные ссылаются на один массив

Преобразование коллекции DOM элементов

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

var links = document.querySelectorAll('a');
var linksArr = Array.slice.call(links);

// Или более короткий вариант
var linksArr = [].slice.call(document.links);

Array.isArray(links); // false
Array.isArray(linksArr); // true

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

Преобразование DOM коллекции в массив с помощью оператора spread выглядит следующим образом:

var links = [...document.querySelectorAll('a')];
// Или просто
var links = [...document.links];

Array.isArray(links); // true

Замена apply для функций конструкторов

Проще всего продемонстрировать использование ..., как замену apply для функций конструкторов, на примере Date. Об этом говорит сайт https://intellect.icu . Обычно конструктор Date работает подобным образом:

var birthday = new Date(1993, 3, 24); // 24 Апреля 1993 года
console.log(birthday); // "1993-04-23T21:00:00.000Z"

Все работает хорошо до тех пор, пока нет необходимости передать массив [1993, 3, 24] в конструктор Date. В данной ситуации вам пришлось бы написать “костыль”:

// Никогда не используйте подобный код − он абсолютно безумен
new (Date.bind.apply(Date, [null].concat([1993, 3, 24]))); // 24 Апреля 1993 года

ES6 дает возможность избежать подобных ситуаций:

var day = [1993, 3, 24];
var birthday = new Date(...day);

Оператор rest

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

var log = function(a, b, ...rest) {
  console.log(a, b, rest);
};

log('Basic', 'rest', 'operator', 'usage'); // Basic rest ['operator', usage]

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

var log = function(...args) {
  conole.log(args);
};

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

Таким образом, больше нет необходимости переводить псевдо-массив arguments функций в настоящий массив − оператор rest сделает это сам:

// Раньше
var sum = function() {
  var args = [].slice.call(arguments);
  return args.reduce(function(s, num) {
    return s += num;
  }, 0);
};

// Теперь
var sum = function(..args) {
  return args.reduce(function(s, num) {
    return s + num;
  }, 0);
};

// Еще короче с использованием стрелочных функций
var sum = (...args) => args.reduce((s, num) => s + num, 0);

Когда мы видим "..." в коде, это могут быть как остаточные параметры, так и оператор расширения.

Как отличить их друг от друга:

  • Если ... располагается в конце списка аргументов функции, то это «остаточные параметры». Он собирает остальные неуказанные аргументы и делает из них массив.
  • Если ... встретился в вызове функции или где-либо еще, то это «оператор расширения». Он извлекает элементы из массива.

Полезно запомнить:

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

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

К аргументам функции можно обращаться и по-старому — через псевдомассив arguments.

От аргументов к rest-параметрам и операции spread.

В ES5 работать с переменным количеством аргументов неудобно.

ES5
function printf(format) {
  var params = [].slice.call(arguments, 1);
  console.log('params: ', params);
  console.log('format: ', format);
}

printf('%s %d %.2f', 'adrian', 321, Math.PI);

С rest ... все намного проще.

ES6
function printf(format, ...params) {
  console.log('params: ', params);
  console.log('format: ', format);
}

printf('%s %d %.2f', 'adrian', 321, Math.PI);

3 Block scope variables - let,const

В ES6 мы перешли от var к let/const.Для чего это почему ельзя всегда использоватьс var?

Проблема var в том, что переменная "протекает" в другие блоки кода, такие как циклы for или блоки условий if:

ES5
var x = 'outer';
function test(inner) {
  if (inner) {
    var x = 'inner'; // scope whole function
    return x;
  }
  return x; // gets redefined on line 4
}

test(false); // undefined 
test(true); // inner

В строке test(false) можно ожидать возврат outer, но нет, мы получаем undefined. Почему?

Потому что даже не смотря на то, что блок if не выполняется, на 4й строке происходит переопределение var x как undefined.

ES6 спешит на помощь:

ES6
let x = 'outer';
function test(inner) {
  if (inner) {
    let x = 'inner';
    return x;
  }
  return x; // gets result from line 1 as expected
}

test(false); // outer
test(true); // inner

Изменив var на let мы откорректировали поведение. Если блок if не вызывается, то переменная x не переопределяется.

IIFE (immediately invoked function expression)

Давайте сначала рассмотрим пример:

ES5
{
  var private = 1;
}

console.log(private); // 1

Как видите, private протекает наружу. Нужно использовать IIFE (immediately-invoked function expression):

ES5
(function(){
  var private2 = 1;
})();

console.log(private2); // Uncaught ReferenceError

Если взглянуть на jQuery/lodash или любые другие проекты с открытым исходным кодом, то можно заметить, что там IIFE используется для содержания глобальной среды в чистоте. А глобальные штуки определяются со специальными символами вроде _, $ или jQuery.

В ES6 не нужно использовать IIFE, достаточно использовать блоки и let:

ES6
{
  let private3 = 1;
}
console.log(private3); // Uncaught ReferenceError

Const

Можно также использовать const если переменная не должна изменяться.

Итог:

  • забудьте var, используйте let и const.
  • Используйте const для всех референсов; не используйте var.
  • Если референсы нужно переопределять, используйте let вместо const.

4 Template Literals

Не нужно больше делать вложенную конкатенацию, можно использовать шаблоны. Посмотрите:

ES5
var first = 'Adrian';
var last = 'Mejia';
console.log('Your name is ' + first + ' ' + last + '.');

С помощью бэктика () и интерполяции строк ${}` можно сделать так:

ES6
const first = 'Adrian';
const last = 'Mejia';
console.log(`Your name is ${first} ${last}.`);

5 Multi-line strings

Не нужно больше конкатенировать строки с + \n:

ES5
var template = '
  • \n' + '

    \n' + ' \n' + ' \n' + '\n' + '

    \n' + ' \n' + '
  • '; console.log(template);

    В ES6 можно снова использовать бэктики:

    ES6
    const template = `
    • `; console.log(template);

      Оба блока кода генерируют одинаковый результат

      6 Классы и объекты

      В ECMAScript 6 перешли от “функций-конструкторов” к “классам” .

      Каждый объект в JavaScript имеет прототип, который является другим объектом. Все объекты в JavaScript наследуют методы и свойства от своего прототипа.

      В ES5 объектно-ориентированное программирование достигалось с помощью функций-конструкторов. Они создавали объекты следующим образом:

      ES5
      var Animal = (function () {
        function MyConstructor(name) {
          this.name = name;
        }
        MyConstructor.prototype.speak = function speak() {
          console.log(this.name + ' makes a noise.');
        };
        return MyConstructor;
      })();
      
      var animal = new Animal('animal');
      animal.speak(); // animal makes a noise.

      В ES6 есть новый синтаксический сахар. Можно сделать то же самое с меньшим кодом и с использованием ключевых слов class и construсtor. Также заметьте, как четко определяются методы: construсtor.prototype.speak = function () vs speak():

      ES6
      class Animal {
        constructor(name) {
          this.name = name;
        }
        speak() {
          console.log(this.name + ' makes a noise.');
        }
      }
      
      const animal = new Animal('animal');
      animal.speak(); // animal makes a noise.

      Оба стиля (ES5/6) дают одинаковый результат.

      Советы:

      • Всегда используйте синтаксис class и не изменяйте prototype напрямую. Код будет лаконичнее и его будет легче понять.
      • Избегайте создания пустого конструктора. У классов есть конструктор по умолчанию если не задать собственный.

      7 Наследование

      Давайте продолжим предыдущий пример с классом Animal. Допустим, нам нужен новый класс Lion.

      В ES5 придется немного поработать с прототипным наследованием.

      ES5
      var Lion = (function () {
        function MyConstructor(name){
          Animal.call(this, name);
        }
      
        // prototypal inheritance
        MyConstructor.prototype = Object.create(Animal.prototype);
        MyConstructor.prototype.constructor = Animal;
      
        MyConstructor.prototype.speak = function speak() {
          Animal.prototype.speak.call(this);
          console.log(this.name + ' roars ');
        };
        return MyConstructor;
      })();
      
      var lion = new Lion('Simba');
      lion.speak(); // Simba makes a noise.
      // Simba roars.

      Не будем вдаваться в детали, но заметьте несколько деталей:

      • Строка 3, напрямую вызываем конструкторAnimal с параметрами.
      • Строки 7-8, назначаем прототип Lion прототипом класса Animal.
      • Строка 11, вызываем метод speak из родительского класса Animal.

      В ES6 есть новые ключевые слова extends и super.

      ES6
      class Lion extends Animal {
        speak() {
          super.speak();
          console.log(this.name + ' roars ');
        }
      }
      
      const lion = new Lion('Simba');
      lion.speak(); // Simba makes a noise.
      // Simba roars.

      Посмотрите, насколько лучше выглядит код на ES6 по сравнению с ES5. И они делают одно и то же! Win!

      Совет:

      • Используйте встроенный способ наследования – extends.

      8 Нативные промисы

      Переходим от callback hell к промисам (promises)

      ES5
      function printAfterTimeout(string, timeout, done){
        setTimeout(function(){
          done(string);
        }, timeout);
      }
      
      printAfterTimeout('Hello ', 2e3, function(result){
        console.log(result);
      
        // nested callback
        printAfterTimeout(result + 'Reader', 2e3, function(result){
          console.log(result);
        });
      });

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

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

      ES6
      function printAfterTimeout(string, timeout){
        return new Promise((resolve, reject) => {
          setTimeout(function(){
            resolve(string);
          }, timeout);
        });
      }
      
      printAfterTimeout('Hello ', 2e3).then((result) => {
        console.log(result);
        return printAfterTimeout(result + 'Reader', 2e3);
      
      }).then((result) => {
        console.log(result);
      });

      С помощью then можно обойтись без вложенных функций.

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

      В ES5 обычные определения функций не исчезли, но был добавлен новый формат – стрелочные функции.

      В ES5 есть проблемы с this:

      ES5
      var _this = this; // need to hold a reference
      
      $('.btn').click(function(event){
        _this.sendData(); // reference outer this
      });
      
      $('.input').on('change',function(event){
        this.sendData(); // reference outer this
      }.bind(this)); // bind to outer this

      Нужно использовать временный this чтобы ссылаться на него внутри функции или использовать bind. В ES6 можно просто использовать стрелочную функцию!

      ES6
      // this will reference the outer one
      $('.btn').click((event) =>  this.sendData());
      
      // implicit returns
      const ids = [291, 288, 984];
      const messages = ids.map(value => `ID is ${value}`);

      10 констукция For…of

      От for переходим к forEach а потом к for...of:

      ES5
      // for
      var array = ['a', 'b', 'c', 'd'];
      for (var i = 0; i < array.length; i++) {
        var element = array[i];
        console.log(element);
      }
      
      // forEach
      array.forEach(function (element) {
        console.log(element);
      });

      ES6 for…of позволяет использовать итераторы

      ES6
      // for ...of
      const array = ['a', 'b', 'c', 'd'];
      for (const element of array) {
          console.log(element);
      }

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

      От проверки параметров переходим к параметрам по умолчанию. Вы делали что-нибудь такое раньше?

      ES5
      function point(x, y, isFlag){
        x = x || 0;
        y = y || -1;
        isFlag = isFlag || true;
        console.log(x,y, isFlag);
      }
      
      point(0, 0) // 0 -1 true 
      point(0, 0, false) // 0 -1 true 
      point(1) // 1 -1 true
      point() // 0 -1 true

      Скорее всего да. Это распространенный паттерн проверки наличия значения переменной. Но тут есть некоторые проблемы:

      • Строка 8, передаем 0, 0 получаем 0, -1
      • Строка 9, передаем false, но получаем true.

      Если параметр по умолчанию это булева переменная или если задать значение 0, то ничего не получится. Почему? Расскажу после этого примера с ES6 ;)

      В ES6 все получается лучше с меньшим количеством кода:

      ES6
      function point(x = 0, y = -1, isFlag = true){
        console.log(x,y, isFlag);
      }
      
      point(0, 0) // 0 0 true
      point(0, 0, false) // 0 0 false
      point(1) // 1 -1 true
      point() // 0 -1 true

      Получаем ожидаемый результат. Пример в ES5 не работал. Нужно проверять на undefined так как false, null, undefined и 0 – это все falsy-значения. С числами можно так:

      ES5
      function point(x, y, isFlag){
        x = x || 0;
        y = typeof(y) === 'undefined' ? -1 : y;
        isFlag = typeof(isFlag) === 'undefined' ? true : isFlag;
        console.log(x,y, isFlag);
      }
      
      point(0, 0) // 0 0 true
      point(0, 0, false) // 0 0 false
      point(1) // 1 -1 true
      point() // 0 -1 true

      С проверкой на undefined все работает как нужно.

Исследование, описанное в статье про Новое в ECMAScript 6. Деструктурирующее присваивание, спред (spread) и рест (Rest) операторы, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое Новое в ECMAScript 6. Деструктурирующее присваивание, спред (spread) и рест (Rest) операторы и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)

создано: 2020-08-14
обновлено: 2021-03-13
132265



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей



Комментарии


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

Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)

Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)