ECMAScript.next: Array.from() and Array.of()

[2011-07-26] esnext, dev, javascript, jslang
(Ad, please don’t block)
Update 2014-05-08. Newer version of this post: “ECMAScript 6’s new array methods

On July 9th, Brendan Eich announced that Rick Waldron had prototyped [1] two new methods for ECMAScript.next: Array.from() and Array.of(). Both methods are also useful in current JavaScript.

Array.from()

Array.from(arrayLike)
converts an array-like object to a true array. Source code:
    // Unary Array.from()
    Array.from = function( arrayish ) {
        return [].slice.call( arrayish );
    };
Example [1]: converting an array-like DOM result into an array.
    var divs = document.querySelectorAll("div");
    Array.from( divs ).forEach(function( node ) {
        console.log( node );
    });
Explanations:
  • Array-like objects: Some objects in JavaScript are array-like, they have indexed access and a length property like arrays, but none of the array methods. Array-like objects include the special variable arguments (giving indexed access to all arguments that were passed to a function) and most DOM results. Not having the standard array methods is especially unfortunate under ECMAScript 5, which has goodies such as Array.prototype.forEach. The canonical way of converting an array-like object to an array can be seen above. Array.from() simply makes this functionality available as a built-in method.
  • Generic methods: Some methods are generic. While they are directly available to instances of their prototype, they can also be borrowed by other instances. To borrow a generic method, one invokes one of the following two methods on it:
    • Function.prototype.call(thisValue, [arg1], [arg2], ...)
    • Function.prototype.apply(thisValue, [arrayWithArguments])
    The borrowing instance is the first argument and becomes the value of this. Generic methods have to be written so that they require this to only have a minimal set of methods. For example, most generic array methods only need this to provide length and indexed access. Array.prototype.slice is generic and allows one to turn any part of an array-like object into an array.

    Example: invoking Array.prototype.map() generically, on the array-like arguments object.

        function prefixHello(prefix) {
            return Array.prototype.map.call(arguments, function(elem) {
                return "Hello "+elem;
            });
        }
    
    Interaction:
        > prefixHello("Jane", "John")
        [ 'Hello Jane', 'Hello John' ]
    
  • [] as a shortcut: [].foo is often used as a shortcut for Array.prototype.foo. That is, you access a prototype property via an instance. I normally prefer not to make this shortcut, because it is less explicit and less performant [3] (though many JavaScript engines optimize the access to [] so it is not much slower). Thus, I would have written this method as follows:
        Array.from = function(arrayLike) {
            return Array.prototype.slice.call(arrayLike);
        };
    

Array.of()

Array.of([elem1], [elem2], ...)
returns elem1, elem2, etc. in an array. Source code:
    // Variable arity Array.of()
    Array.of = function() {
        return [].slice.call( arguments );
    };
Usage example:
    > Array.of("red", "green", "blue")
    [ 'red', 'green', 'blue' ]
This method is not needed very often – array literals are usually a better solution. However, when you need a constructor function (e.g. to pass it to another function) for arrays, this method is useful. That method lets you avoid a potential pitfall of the Array constructor function: If it has several arguments, it behaves like an array literal. If it has a single argument, it creates an empty array of the given length.
    > new Array(3, 4, 5)
    [ 3, 4, 5 ]
    > new Array(3)
    []
Array.of also ensures that your arrays don’t accidentally have holes (but I’m not sure that this feature is very imporant).
    > [1,,3]
    [1,,3]
    > Array.of(1,,3)
      Syntax error:
      Array.of(1,,3)
      ...........^

Related reading

  1. Array goodies from twitter rap with David Herman [refers to a tweet wrapping things up]
  2. A brief history of ECMAScript versions (including Harmony and ES.next)
  3. JavaScript performance: Array.prototype versus []