Использование оператора останова и оператора распространения в 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]);
Является ли это просто синтаксическим изменением или проблемой производительности?
Ответы
Ответ 1
Разница между ними - это просто синтаксис или проблема с производительностью?
Оба и многое другое...
Параметры останова:
- Являетесь известной идиомой на других языках (Ruby, Python).
- esier читать и поддерживать (vs.
slice
).
- легче понять для начинающих.
- Может (и, вероятно, будет) привести к лучшей производительности, так как двигатели могут оптимизировать.
- инструмент более дружелюбный, так как они могут быть проанализированы статически.
Ответ 2
В дополнение к ответу @kangaxs я бы уточнил, что производительность и правильность многократно возникают при вызове объекта 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
для извлечения вариативных аргументов является одним из многих преимуществ нового синтаксиса.
Ответ 3
В данном примере вы напрямую передаете литерал объекта. Несмотря на то, что он все еще является семантическим, он кажется не аналогичным приложению оператора спредов. 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 demo (скомпилировано 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 demo
В заключение я не думаю, что использование оператора спреда улучшит или затруднит производительность, однако оно обеспечивает улучшенную читаемость кода и, возможно, поддержку. К сожалению, ES6 пока еще не широко внедряется, и я уверенно предполагаю, что потребуется некоторое время для поддержки браузера.
Фантастический факт: PHP 5.6 вводит аналогичные функции с вариативными функциями, которые сделают func_get_args()
избыточным.
Ответ 4
Параметр 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/
Ответ 5
Я думаю, что главными отличиями в параметре отдыха являются:
- Аргументы в аргументах ECMA5 похожи на массив, а не массив, поэтому обычные методы массива недоступны, но в аргументах ECMA6 используется массив.
- Я думаю, что массив объектов в примере кода ECMA5 сделает код немного медленнее, потому что создается новый массив.