2

I'm watching Crockford on Javascript - Act III: Function the Ultimate at around 41 mins 26 seconds. The code on his screen uses arguments.slice() in a way that causes an error for me.

function curry(func){
    var args = arguments.slice(1);
    ...
}

He explains it like this:

I'll first get an array of arguments, except the first one, because the first one is a function and I don't need that one. In this case I'm assuming I'm on ES5, so I'm not doing the awful Array.prototype.apply() trick.

The problem is that running arguments.slice() results in this error:

Uncaught TypeError: arguments.slice is not a function

I'm testing on modern browsers that definitely have ES5! The only way I can get the code to work is if I use some "awful" tricks, (as he calls them) e.g. Array.prototype.slice.apply(arguments, [1]) or [].slice.call(arguments, 1);.

Is he just mistaken? Does his slide have a typo in it? Why doesn't arguments.slice() work in my ES5 browsers?

Diptendu
  • 2,120
  • 1
  • 15
  • 28
brentonstrine
  • 21,694
  • 25
  • 74
  • 120
  • That should be a mistake. – ivkremer Jun 27 '15 at 01:57
  • It's hard to research in negative space, but from all I can tell he's wrong. ES6, *possibly*. It's pointless either way though, because of a recommendation from Mozilla: "*You should not slice on arguments because it prevents optimizations in JavaScript engines (V8 for example). Instead, try constructing a new array by iterating through the arguments object.*" – Katana314 Jun 27 '15 at 01:57
  • I cannot find any reference to `arguments.slice` in the [_ES6 Draft_](http://www.ecma-international.org/ecma-262/6.0/#sec-arguments-exotic-objects) so I don't believe this is correct code, but you can enable the method manually `(function () { Object.getPrototypeOf(arguments).slice = Array.prototype.slice; }());` – Paul S. Jun 27 '15 at 02:05
  • 2
    The answer you're looking for: http://stackoverflow.com/a/13189828 – Fabrício Matté Jun 27 '15 at 02:06
  • Thanks @FabrícioMatté! Put this in an answer so I can accept. – brentonstrine Jun 27 '15 at 02:09
  • @FabrícioMatté - how does that dup answer the actual question about what Crockford is using that actually works? – jfriend00 Jun 27 '15 at 02:11
  • Seeing as the answer linked above is from a TC39 member (the committee which governs the development of the ECMAScript/JavaScript language) and explains the historical reasons behind why Crockford expected `arguments.slice` to be in ES5, I'd feel bad quoting/borrowing too much from it hence I chose to cast a close vote as the linked answer already answers the question. `;)` – Fabrício Matté Jun 27 '15 at 02:11
  • @jfriend00 (see comment above) – Fabrício Matté Jun 27 '15 at 02:11
  • @jfriend00 oh, there's also a meta discussion about whether different questions which have the same answer should be considered dupes or not. IIRC, they should. I'll look it up on meta. – Fabrício Matté Jun 27 '15 at 02:12
  • @jfriend00 after some search around MSO/MSE I can find rather contracting answers, so as this question is sufficiently different I guess it is worth the benefit of doubt. Reopening. – Fabrício Matté Jun 27 '15 at 02:17

3 Answers3

5

Quoting TC39 member Allen Wirfs-Brock:

Until very late in the development of ECMAScript 5, argument object were going to inherit all of the Array.prototype methods. But the "final draft" of ES5 approved by TC39 in Sept. 2009 did not have this feature.

Making the arguments object inherit from the Array prototype was actually planned, but when put in practice it broke the web. Hence it was removed from the final revision before official publication.


Nowadays, with ECMAScript 2015 (a.k.a. ES6) standardized, the best approach is to use rest parameters:

function curry(func, ...args) {
  // ...
}

Which is equivalent to ES5:

function curry(func) {
  var args = [].slice.call(arguments, 1);
  // ...
}

This feature is already natively available in Firefox and Edge, and available everywhere if you use a JavaScript compiler such as Babel.

Community
  • 1
  • 1
Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
  • Literally the same answer as mine but mine got downvoted. – Sebastian Nette Jun 27 '15 at 02:30
  • @SebastianNette because you just followed his link and poorly copy/pasted what you found there. – brentonstrine Jun 27 '15 at 02:32
  • @SebastianNette Stack Overflow voters are rather skeptic at times. I also have answers to some questions which I believe to be the best answer and yet are sunk with downvotes. Either way, voting is an anonymous process so we can only guess the reasons. brentonstrine has posed a plausible reason. – Fabrício Matté Jun 27 '15 at 02:35
  • @Fabrício Matté Thanks for explaining that to me. I'm fairly new to SO and just recently started answering question so it''s sometimes really confusing why people downvote on answer and upvote an other. – Sebastian Nette Jun 27 '15 at 02:40
  • @SebastianNette Yours was downvoted because for the first 20 minutes of its existence it was answering a completely different question. Now that I've seen your edit, I've removed my downvote, but it's unlikely the other voters will return and see it. – Chris Hayes Jun 28 '15 at 17:50
0

As arguments is an array-like, you can use:

function fn() {
  const args = Array.from(arguments)
}

(if you have a Polyfill or Babel)

Ian
  • 2,944
  • 1
  • 19
  • 19
-2

The arguments object is not an Array. It is similar to an Array (Array like), but does not have any Array properties except length.

You will have to convert it to an array first using said tricks.

var slice = Array.prototype.slice;
function curry(func){
    var args = slice.call(arguments, 1); // converts into array
}

arguments were going to inherit all prototypal methods from arrays but in the final draft this feature was removed.

https://mail.mozilla.org/pipermail/es5-discuss/2009-August/003112.html

I implement the logic to make the Arguments object inherit from Array in WebKit last friday and it's quickly turned up a severe
incompatibility with Prototype.js ... This breaks at the very least a number of Apple sites and Nasa.gov -- ... Due to these site breakages, caused by a major compatibility problem
in a fairly major library it seems infeasible to attempt to retrofit
array like behaviour onto arguments.

So in the end, instead of forcing everyone to change their code to avoid breaking webpages, they reverted this change. So even in ES5, you still need to apply above stated trick to convert arguments into an actual array.

Sebastian Nette
  • 7,364
  • 2
  • 17
  • 17
  • Yes, the OP knows that `arguments` is not an array. The question is about why Mr Crockford explicitly states that he can use `.slice()` directly on `arguments` where his explanation is that he is assuming ES5 - even though in practice this doesn't actually work. – nnnnnn Jun 27 '15 at 02:04