What\'s the usage of rest parameter that will be added in ECMAScript 6?
For example, in ECMAScript 5 you can do the following to get an array of parameters starting
Rest Operator
function sumOfNumber(...args) {
return args.reduce((a, b) => {
return a + b
})
}
console.log(sumOfNumber(1, 2, 3, 4))
Spread Operator
function sumOfNumber(args) {
console.log(...args)
}
sumOfNumber([1, 2, 3, 4])
Is the difference between them is just syntax or there's a performance issue?
Both, and more...
Rest parameters:
slice
).In your given example, you directly pass the object literal. Whilst still semantic, it seems un-analogous to the application of the spread operator. IMO, the spread operator's value becomes apparent when listing each parameter would determent code readability (and maintainability, when replacing traditional push, splice, concat methods).
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 (compiled by 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
In closing, I don't think that using the spread operator will improve or impede performance, it does however offer improved code readability, and arguably maintainability too. Regrettably, ES6 isn't widely implemented yet, and I'll assertively speculate that it will take sometime for browser support to ensue.
Fun fact: PHP 5.6 introduces similar functionality with its variadic functions which will make func_get_args()
redundant.
A rest
parameter will collect individual parameters into an array when you use the dots in a function parameter definition, while the spread operator expands an array into individual parameters when using the dots in a function call
.
When ever you want to collect individual parameters into an array, you would use rest
operator in your function param definition.
let sum = (...numbers) => {
let result = 0;
numbers.forEach(function(n){
result += n;
});
return result;
};
console.log(sum(1,2,3));
Dealing with arguments
object inside nested function becomes really tricky, let's visit the below situation where our inner function needs access to the arguments
object passed.
If our inner function filterNumbers
needs access to arguments, it has to be stored above in a variable
before passing it further as each function has its own arguments
object, which is an array like object.
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);
The approach works, but it's too verbose. var args = arguments
can be omitted and Array.prototype.filter.call(args)
can be transformed to args.filter()
using a rest
parameter.
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
When ever you want to expand an array into individual parameters, you would use spread operator in your function call.
let sum = (a,b,c) => {
return a + b + c;
};
console.log(sum(...[1,2,3]));
In the above code, the spread operator will “spread”
the array of three values across the parameters a, b, and c
. The spread
operator can also expand an array to create individual elements in an array
literal.
var a = [4, 5, 6];
var b = [1, 2, 3, ...a, 7, 8, 9]
console.log(b);
Improved function invocation in ES6
ES5
provides .apply()
method on the function object to solve this. Unfortunately this technique has 3 problems:
It seems irrelevant to indicate in .apply()
second time the context countries making it more verbose.
let countries = ['India', 'USA'];
let otherCountries = ['China', 'Japan'];
countries.push.apply(countries, otherCountries);
console.log(countries); // => ['India', 'USA', 'China', 'Japan']
The spread
operator fills the function
invocation arguments with values from an array
. Let's improve the above sample with a spread operator:
let countries = ['India', 'USA'];
let otherCountries = ['China', 'Japan'];
countries.push(...otherCountries);
console.log(countries); // => ['Moldova', 'Ukraine', 'USA', 'Japan']
Spread
operator configures the constructor invocation arguments from an array, which is bit complicated and difficult directly when using .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'
Moreover you can combine multiple spread
operators and regular arguments in the same invocation. The following example is removing from an array existing elements, then adds other array and an element:
var numbers = [1, 2];
var evenNumbers = [4, 8];
const zero = 0;
numbers.splice(0, 2, ...evenNumbers, zero);
console.log(numbers); // => [4, 8, 0]
Clone an array instance:
var words = ['Hi', 'Hello', 'Good day'];
var otherWords = [...words];
console.log(otherWords); // => ['Hi', 'Hello', 'Good day']
console.log(otherWords === words); // => false
otherWords
is a clone version of words array. Notice that cloning happens only on array itself, but not on the contained elements (i.e. it's not a deep clone).
References: https://dmitripavlutin.com/how-three-dots-changed-javascript/
In addition to @kangax’s response, I would elaborate that performance and correctness are problematic many times the arguments
object is invoked. If you pass the arguments
object to another function, you pass with it the right to modify the corresponding local variables in your scope.
function foo(arg) {
bar(arguments);
return arg;
}
function bar(args) {
args[0] = 20;
}
foo(10) // 20
The existence of this feature invalidates local reasoning within a function, which makes JIT optimization more difficult and introduces certain security hazards. Programs that are designed for speed must work around this problem with far more elaborate boilerplate code than the Array.prototype.slice.call(arguments)
idiom, and in security contexts, the passing of arguments
must be strictly disallowed.
Eliminating the need to use the arguments
object to extract variadic arguments is one of the many advantages to the new syntax.
I think the main differences in rest parameter are: