In esnext, you can use array destructuring to capture the first elements of an array, as well as the rest:

const [a, b, ...c] = [1, 2, 3, 4, 5]
console.log([a, b, c]) // [1, 2, [3, 4, 5]]

You can’t, however, put the rest operator at the start or in the middle of the destructuring expression. It must be at the end.

This is a bummer. CoffeeScript supports having a rest argument in the middle of a destructuring expression. Since esnext supports nested array destructuring, this limitation can at least be worked around concisely.

To implement getting the first two, the last two, and the middle, I can define a function leftrest which takes an array [l1, l2, l3..., r1, r2] and a number of rightmost arguments to expect, and returns an array with the first element containing the left arguments, followed by the rightmost arguments ([[l1, l2, l3...], r1, r2]):

const leftrest = (a, n) => {
  const rest = [...a]
  return [rest, ...rest.splice(-n)]
}
const [[a, b, ...c], d, e] = leftrest([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2)
console.log([a, b, c, d, e])
// [ 1, 2, [ 3, 4, 5, 6, 7, 8 ], 9, 10 ]

The first line of the function const rest = [...a] simply copies the array before it’s handed to splice. Splice modifies an array in place, so it should be copied first. It’s best to use pure functions to avoid unintended side effects.

This isn’t DRY because it’s necessary to pass the number 2 to leftrest, but it’s close.