David Hellsing
David Hellsing

Reputation: 108500

[].slice or Array.prototype.slice

I’v come across both ways to apply Array prototypes to a native object:

arr = Array.prototype.slice.call(obj);
arr = [].slice.call(obj);

In similar fashion, getting the true type of a native array-like object:

type = Object.prototype.toString.call(obj);
type = {}.toString.call(obj);

A simple test:

function fn() {
    console.log(
        Array.prototype.slice.call(arguments),
        [].slice.call(arguments),
        Object.prototype.toString.call(arguments), 
        {}.toString.call(arguments)
    );
}

fn(0,1);

Fiddle: http://jsfiddle.net/PhdmN/

They seem identical to me; the first syntax is used more often, but the second is definitely shorter. Are there any shortcomings when using the shorter syntax?

Upvotes: 21

Views: 5351

Answers (2)

ccjmne
ccjmne

Reputation: 9618

That's an interesting question! Let's pull up the pros (✔️) and cons (❌) for each alternative:

[].slice

  • ✔️: Is typed faster
    Two keystrokes, no shift-modifier or anything,
    and your linter knows [.slice is a typo.
  • ✔️: Is read faster
    You can identify the relevant part (slice) faster.
  • ✔️: Is more popular
    56M+ snippets on GitHub (as of late 2018).
  • ✔️: Can't be overwritten
    The first part of Rob's answer demonstrates this perfectly.
  • ✔️: Runs faster.
    Wait, what? Well, that's actually the whole point of this answer.

Contrary to what you'd think and read pretty much everywhere, [].slice.call(...) does NOT instantiate a new, empty Array just to access its slice property!.

Nowadays (it has been so for 5+ years – as of late 2018), the JIT compilation (1) is included everywhere you run JavaScript (unless you're still browsing the Web with IE8 or lower).

This mechanism allows the JS Engine to: (2)

... resolve [].slice directly, and statically, as direct Array.prototype reference in one shot, and just one configurable property access: forEach

Array.prototype.slice

  • ❌: Is typed slower
    Typos (e.g.: Array.prorotype.slice) look fine until you try and run the code.
  • ❌: Is less popular
    8M+ snippets on GitHub (as of late 2018).
  • ❌: Runs slower
    Array.prototype.slice is: (2)

... a lookup for the whole scope for an Array reference until all scopes are walked 'till the global one ... because you can name a variable Array any time you want.

Once the global scope is reached, and the native found, the engine accesses its proottype and after that its method
...
O(N) scope resolution + 2 properties access (.prototype and .forEach).

  • ✔️: Allows you to seamlessly adapt to whichever coding conventions would strictly prevent you from having a line start with either (, [ or `
    Definitely a good thing (sarcastically).
  • ✔️: You won't have to explain why [].slice is better in pretty much every way.
    Although now, that would just boil down to clicking the share link below 👌

Disclaimer

Note that, realistically, neither does effectively run faster than the other. This isn't the bottleneck of your application.


  1. You may want to read A crash course in just-in-time (JIT) compilers
  2. Quoted from Andrea Giammarchi (@WebReflection on Twitter)

Upvotes: 2

Rob W
Rob W

Reputation: 348992

They are identical regarding functionality.

However, the Array object can be overwritten, causing the first method to fail.

//Example:
Array = {};
console.log(typeof Array.prototype.slice); // "undefined"
console.log(typeof [].slice);    // "function"

The literal method creates a new instance of Array (opposed to Array.prototype. method). Benchmark of both methods: http://jsperf.com/bbarr-new-array-vs-literal/3

When you're going to use the method many times, the best practice is to cache the method:

  • var slice = Array.prototype.slice; //Commonly used
  • var slice = [].slice; - If you're concerned about the existence of Array, or if you just like the shorter syntax.

Upvotes: 26

Related Questions