Использование параметра отдыха и оператора распространения в 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]);

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

5 ответов

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

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

Параметры отдыха:

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

В дополнение к ответу @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 Объект для извлечения переменных аргументов является одним из многих преимуществ нового синтаксиса.

В данном примере вы напрямую передаете литерал объекта. В то время как все еще семантический, это кажется не аналогичным применению оператора распространения. IMO, значение оператора распространения становится очевидным, когда перечисление каждого параметра будет препятствовать удобочитаемости кода (и удобству обслуживания при замене традиционных методов 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-компилятором)

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

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 demo

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

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

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

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

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://rainsoft.io/how-three-dots-changed-javascript/

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

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

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

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])
Другие вопросы по тегам