Использование параметра rest и оператора распространения в javascript

Какое использование параметра rest будет добавлено в ECMAScript 6?

Например, в ECMAScript 5 вы можете сделать следующее, чтобы получить массив параметров, начиная со второго элемента:

// ES 5
store('Joe', 'money');
store('Jane', 'letters', 'certificates');
function store(name) {
  var items = [].slice.call(arguments, 1); //['money'] in first case
  items.forEach(function (item) {
    vault.customer[name].push(item);
  });
}

и это будет эквивалентно следующему коду в ECMAScript 6:

// ES 6
store('Joe', 'money');
store('Jane', 'letters', 'certificates');
function store(name, ...items) {
  items.forEach(function (item) {
    vault.customer[name].push(items)
  });
}

Разница между ними - просто синтаксис или проблема с производительностью?

Также для оператора распространения (...)

//in ecmascript5
var max = Math.max.apply(null, [14, 3, 77]);
//but in ecmascript6
var max = Math.max(...[14, 3, 77]);

Это просто изменение синтаксиса или проблема с производительностью?


person Tareq Salah    schedule 12.12.2013    source источник
comment
Чем больше я читаю и думаю над этим вопросом, тем более проблематичным он кажется мне. Ему не хватает фокусировки. Название и первое предложение основного текста предполагают, что речь идет о использовании три точки в JavaScript. Последняя часть тела вместо этого спрашивает о проблемах с производительностью. См. Раздел "задать несколько вопросов". Тот факт, что другие вопросы являются закрыто, поскольку дубликаты этого еще больше усложняют.   -  person Henke    schedule 15.03.2021
comment
Отвечает ли это на ваш вопрос? Синтаксис распространения и параметр отдыха в ES2015 / ES6   -  person Henke    schedule 15.03.2021


Ответы (6)


Разница между ними только в синтаксисе или есть проблема с производительностью?

И то, и другое ...

Остальные параметры:

  1. Являются известной идиомой в других языках (Ruby, Python).
  2. труднее читать и поддерживать (по сравнению с slice).
  3. легче понять для новичков.
  4. Может (и, вероятно, будет) привести к повышению производительности, поскольку движки могут оптимизироваться.
  5. более удобны для инструментов, так как их можно анализировать статически.
person kangax    schedule 17.12.2013

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

function foo(arg) {
    bar(arguments);
    return arg;
}

function bar(args) {
    args[0] = 20;
}

foo(10) // 20

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

Устранение необходимости использовать объект arguments для извлечения вариативных аргументов - одно из многих преимуществ нового синтаксиса.

person Kris Kowal    schedule 27.02.2014

В данном примере вы напрямую передаете литерал объекта. Несмотря на то, что он все еще семантический, он кажется не аналогичным применению оператора распространения. ИМО, значение оператора распространения становится очевидным, когда перечисление каждого параметра может ухудшить читаемость кода (и ремонтопригодность при замене традиционных методов push, splice, concat).

С оператором спреда

let nums = [1.25,5.44,7.15,4.22,6.18,3.11];   

function add(a, b, ...nums){
  let sum = parseInt(a + b);
  nums.forEach(function(num) {
    sum += num;
  })
  return parseInt(sum);
}

console.log(add(1, 2, ...nums)); //30

демонстрация ES6Fiddle (скомпилирована traceur-compiler)

Без оператора спреда

var nums = [1.25,5.44,7.15,4.22,6.18,3.11];   

function add(a, b, nums){
  var sum = parseInt(a + b);
  nums.forEach(function(num) {
    sum += num;
  })
  return parseInt(sum);
}

console.log(add(1, 2, nums)); //30

демонстрация JSFiddle

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

Интересный факт: PHP 5.6 предоставляет аналогичные функции с его вариативные функции, которые сделают func_get_args() избыточным.

person Jack    schedule 19.07.2014

Параметр rest будет собирать отдельные параметры в массив, когда вы используете точки в определении параметра функции, в то время как оператор распространения расширяет массив на отдельные параметры при использовании точек в function call.

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

let sum = (...numbers) => {
  let result = 0;

  numbers.forEach(function(n){
  result += n;
  });

  return result;
};

console.log(sum(1,2,3));

Работа с объектом arguments внутри вложенной функции становится действительно сложной, давайте рассмотрим ситуацию ниже, когда нашей внутренней функции требуется доступ к переданному объекту arguments.

Если нашему inner function filterNumbers требуется доступ к аргументам, он должен быть сохранен выше в variable, прежде чем передавать его дальше, поскольку каждая функция имеет свой собственный объект arguments, который представляет собой массив, подобный объекту.

function sumOnlyNumbers() {
  var args = arguments;
  var numbers = filterNumbers();
  return numbers.reduce((sum, element) => sum + element);
    function filterNumbers() {
      return Array.prototype.filter.call(args,
        element => typeof element === 'number'
    );
  }
}
sumOnlyNumbers(1, 'Hello', 5, false);

Подход работает, но слишком многословен. var args = arguments можно опустить, а Array.prototype.filter.call(args) можно преобразовать в args.filter() с помощью параметра rest.

function sumOnlyNumbers(...args) {
  var numbers = filterNumbers();
  return numbers.reduce((sum, element) => sum + element);
    function filterNumbers() {
      return args.filter(element => typeof element === 'number');
    }
  }
sumOnlyNumbers(1, 'Hello', 5, false); // => 6

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

let sum = (a,b,c) => {
  return a + b + c;
};

console.log(sum(...[1,2,3]));

В приведенном выше коде оператор распространения будет “spread” массивом из трех значений parameters a, b, and c. Оператор spread также может расширять массив для создания отдельных элементов в литерале array.

var a = [4, 5, 6];
var b = [1, 2, 3, ...a, 7, 8, 9]
console.log(b);

Улучшенный вызов функций в ES6

ES5 предоставляет .apply() метод для объекта функции для решения этой проблемы. К сожалению, у этого метода есть 3 проблемы:

  • Необходимо указать вручную контекст вызова функции
  • Невозможно использовать в вызове конструктора
  • Более короткое решение предпочтительнее

Кажется неуместным указывать во второй раз .apply() страны контекста, делая его более подробным.

let countries = ['India', 'USA'];
let otherCountries = ['China', 'Japan'];
countries.push.apply(countries, otherCountries);
console.log(countries); // => ['India', 'USA', 'China', 'Japan']

Оператор spread заполняет аргументы вызова function значениями из array. Давайте улучшим приведенный выше пример с помощью оператора распространения:

let countries = ['India', 'USA'];
let otherCountries = ['China', 'Japan'];
countries.push(...otherCountries);
console.log(countries); // => ['Moldova', 'Ukraine', 'USA', 'Japan']

Оператор Spread настраивает аргументы вызова конструктора из массива, что немного сложно и сложно напрямую при использовании .apply().

class Actor {
  constructor(name, country) {
  this.name = name;
  this.country = country;
  }
  getDescription() {
  return `${this.name} leads ${this.country}`;
  }
}
var details = ['RajiniKanth the Great', 'India'];
var Alexander = new Actor(...details);
console.log(Alexander.getDescription()); // => 'RajiniKanth the Great leads India'

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

var numbers = [1, 2];
var evenNumbers = [4, 8];
const zero = 0;
numbers.splice(0, 2, ...evenNumbers, zero);
console.log(numbers); // => [4, 8, 0]

Клонировать экземпляр массива:

var words = ['Hi', 'Hello', 'Good day'];
var otherWords = [...words];
console.log(otherWords); // => ['Hi', 'Hello', 'Good day']
console.log(otherWords === words); // => false

otherWords - это клонированная версия массива слов. Обратите внимание, что клонирование происходит только с самим массивом, но не с содержащимися в нем элементами (т.е. это не глубокий клон).

Ссылки: https://dmitripavlutin.com/how-three-dots-changed-javascript/

person Thalaivar    schedule 15.10.2016
comment
@DmitriPavlutin: Проверьте мой обновленный пост, это сборник множества вещей ... может быть, мне стоит опубликовать ссылку. - person Thalaivar; 16.03.2017

Я думаю, что основные отличия в параметре покоя:

  • Аргументы в аргументах ECMA5 подобны массиву, а не массиву, поэтому обычные методы массива недоступны, но в аргументах ECMA6 это массив.
  • Я думаю, что создание массива элементов в примере кода ECMA5 сделает код немного медленнее, потому что создается новый массив.
person Hazem Hagrass    schedule 15.12.2013

Оператор отдыха

function sumOfNumber(...args) {
    return args.reduce((a, b) => {
        return a + b
    })
}

console.log(sumOfNumber(1, 2, 3, 4))

Оператор распространения

function sumOfNumber(args) {
    console.log(...args)
}

sumOfNumber([1, 2, 3, 4])
person Shuvro    schedule 14.03.2020
comment
Просто использовал ваш пример в классе. Престижность !. - person Louis345; 01.10.2020