Использование параметра отдыха и оператора распространения в 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 ответов
Разница между ними только в синтаксисе или есть проблема с производительностью?
И то, и другое...
Параметры отдыха:
- Известна идиома в других языках (Ruby, Python).
- Труднее читать и поддерживать (против
slice
). - Легче понять для начинающих.
- Может (и, вероятно, будет) привести к лучшей производительности, так как двигатели могут оптимизировать.
- Инструмент удобнее, так как их можно анализировать статически.
В дополнение к ответу @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
В заключение, я не думаю, что использование оператора распространения приведет к улучшению или снижению производительности, однако, оно предлагает улучшенную читаемость кода и, возможно, удобство сопровождения. К сожалению, 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])