Original discussion began on Twitter and can be found by starting here and here. Discussion was continued on es-discuss mailing list in the thread Pure win: Array.from and Array.of
Update Nov. 3, 2011
Official strawman has been posted: http://wiki.ecmascript.org/doku.php?id=strawman:array_extras
Converts a single argument that is an array-like object or list (eg. arguments, NodeList, DOMTokenList (used by classList), NamedNodeMap (used by attributes property)) into a new Array() and returns it;
- Let O be the result of calling ToObject( arg ).
- Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".
- Let len be ToUint32( lenValue ).
- Let A be a new array created as if by the expression new Array() where Array is the standard built-in constructor with that name.
- Let k be 0.
- Repeat, while k < len
- Let Pk be ToString( k ).
- Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
- If kPresent is true, then
- Let kValue be the result of calling the [[Get]] internal method of O with argument Pk.
- Call the [[DefineOwnProperty]] internal method of A with arguments ToString( k ), Property Descriptor {[[Value]]: kValue, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
- Increase k by 1.
- return A.
Array.from = function( arg ) {
var O = Object( arg );
var len = O.length >>> 0;
var A = new Array();
var k = 0;
while( k < len ) {
var kValue;
if ( k in O ) {
kValue = O[ k ];
A[ k ] = kValue;
}
k++;
}
return A;
};
The following use cases are based on the markup in the the attached file domjunk.html
.
var divs = document.querySelectorAll("div");
Array.from( divs );
// [ <div class="some classes" data-info="12"></div>, <div data-info="10"></div> ]
Array.from( divs ).forEach(function( node ) {
console.log( node );
});
// <div class="some classes" data-info="12"></div>
// <div data-info="10"></div>
Array.from( divs ).filter(function( node ) {
return !!node.classList.length;
});
// [ <div class="some classes" data-info="12"></div> ]
Array.from( divs ).reduce(function( prev, current ) {
return ( +prev.dataset.info ) + ( +current.dataset.info );
});
// 22
Array.from( divs[0].classList )
// ["some", "classes"]
// Now shorter then [].foo.call :)
var a = Array;
a.from( divs );
// [ <div class="some classes" data-info="12"></div>, <div data-info="10"></div> ]
Array.of provides a constructor that, unlike Array, does not have the special case for new Array(42), which presets length (and hints to implementations to preallocate) but leaves holes in [0, length ).
The use-case is when you can't write a literal, because you are passing a function-that-constructs as a funarg, and the eventual caller may pass only one number arg, or several args. In that case, Array will not do the right thing in the one-number-arg case. Explanation by Brendan Eich
// Harmony/ES.next rest params
Array.of = function( ...args ) {
return args;
};
// Capable today
Array.of = function() {
return Array.prototype.slice.call( arguments );
};
Array.of( 10 );
// [ 10 ]
// Versus the existing behaviour of new Array():
new Array( 10 );
// [ , , , , , , , , , , ]
// Makes sense when you can't use a literal, such this case...
// ES.next http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters
var o = (function( ArrayCtor, ...rest ) {
return ArrayCtor( rest );
})( Array, 10 );
// Using Array.of:
var o = (function( Ctor, args ) {
return Ctor( ...args );
})( Array.of, args );
// Many args...
Array.of( "things", "that", "aren't", "currently", "an", "array" );
// [ "things", "that", "aren't", "currently", "an", "array" ]
@guillermo
[[Prototype]] of arg is changed, it is not a good idea.