Array.prototype.slice vs manual array creation

In a constant pursuit of new and more effective ways to implement common JavaScript code patterns I've recently found out (sorry for such probably trivial finding but it was a real news for me) that Array.prototype.slice method can be easily used to make instance of Array from an arguments object.
I instantly decided to use it in my current projects. Currently the same task is performed by simple iteration through members of the arguments object and filling in respective members of a new Array instance.
I was pretty sure that performance of the new variant will be significantly better. It is a native method call after all instead of interpreted JavaScript code. Nevertheless, I wanted to run simple comparison tests and wrote following code:

code

The code is self-explanatory. I used four arguments for method calls. 10000 iterations. An alias for Array.prototype.slice method was added to avoid unnecessary lookup.

The results were surprising. You can see them on the screenshot below.

comparison

Of the four major browsers that I tested the code against, (FF 3.0b3, IE7, Opera 9.50 Alpha and Safari 3.0.4 (Windows)) only Safari proved my initial expectations.
Why is that? Did I make some error in my test code that I can't notice?

By the way, Opera 9.5 is really fast.

10 Comments

  • The arguments object isn't an array. Does getArray2 even work?

  • Technically, ES3 arguments is an object that supports array-like semantics and has properties like callee. Safari 3 is the only implementation that supports the ES4 version of arguments, which is an array.

  • Joe,

    1. I am well aware of the fact that arguments object is not an instance of Array. Actually, the whole purpose of the presented methods is to create instance of Array object from the arguments object.
    If you look at section 15.4.4.10 of ECMA-262 then you'll see such note:

    "The slice function is intentionally generic; it does not require that its this value be an Array object.
    Therefore it can be transferred to other kinds of objects for use as a method. ..."

    Such note is also present for other methods of the Array.prototype object. I knew this fact for a long time and used it quite often. I just wasn't aware of such specific variant of usage of the slice() method (i.e. applied without parameters).

    Subject of the post was not about the correctness of the code itself. Both methods are used in major JavaScript frameworks. And, of course, you can easily check it yourself and make sure the code does work.
    The question was regarding results of the test.

    2. As for you second post. According to my experiments arguments object on Safari 3 (Safari 3.0.4 Windows)is not an instance of Array. At lest the following code lines inside a function body resulted in:
    arguments instanceof Array; // false
    arguments.__proto__ === [].__proto__; // false
    typeof arguments.slice // undefined

    If it was true than it would be at least partial answer to my question (regarding Safari exceptional results). But alas.

    Thank you for your replies.

  • The reason why the slice method is slower in your example is probably because it's looking up the scope chain to find the global _slice object.

    Either replace the reference to the global _slice with Array.prototype.slice inside the function, or pass the slice reference into the function as an argument. Scope chain lookups can be pretty slow.

    Also, looping through only three elements isn't that much. Try generating an array of 1000 elements, and calling the functions with Function.apply, and then the difference may be more pronounced.

    Lastly, I've found some cases where the browsers are optimized to discard values that are not set to be assigned anywhere. Instead of just calling fn(...), try doing var x = fn(...) so that it knows that it has to actually keep track of the return val.

    Cheers!

  • That's interesting.

    Looking up the global Array vs the global _slice should be about the same, so that makes sense. What about passing a reference to Array.prototype.slice directly to the function so that it's a parameter rather than a global var?

    One way that Array.prototype.slice is better than iteration, though, is that it's less code. In general, the fewer instructions you have, the less chance there is for bugs to creep in, and the more clear and maintainable the code is. Early optimization is not usually a good idea, especially if it results in a less clean design.

  • Hi,

    I came here after googling for the slice method because I saw it used without parameters and was wondering what was the point of that. And I have just found it new, it is new to me also, using slice to get the arguments of an abject as an array.

    And then I started reading through the comments. I think the reason you are surprised from the results is because you are thinking that the internal implementation is probably doing something really clever. But if you think of it in more abstract terms, as in algorithm, your implementation as compared to the internal one cannot differ. They both have to iterate through the entire arguments list, it's the same number of (conceptual) operations they both have to execute.

    The internal implementation might as well be implemented with a fewer lines of code or even use some special knowledge of how the interpreter works, still the differences in speed will be negligible. You should not notice a difference unless you are comparing with arrays that contain more than 20,000 if not more elements.

    regards

  • val,

    No, I did not expect any extraordinary clever algorithm from the internal implementation. The only reason I was surprised and the only thing that I thought should matter in this case is that compiled code is not nearly comparable to the interpreted one from the performance point of view.

    Best regards.

  • thank you
    and thanks to the comments above

  • Also a useful pattern:
    fn1(a,b,c){
    }
    fn2(){
    fn1.apply(this,Array.prototype.slice.call(arguments));
    }

    fn2 now calls fn1 in the way that it expects, with context and any number of arguments preserved. This can be used for creating facades or boiler plate code.

  • I agree with Drew, i see this being very useful. Just realized how well that works with the apply method. Gonna have some fun with that. I loved "I instantly decided to use it in my current projects." from the article above.

Comments have been disabled for this content.