Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array.prototype.slice weird behaviour

Tags:

javascript

Consider this piece of code, with console output at the end of each line:

function whatever() {
  console.log(arguments) // { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
  console.log(Array.prototype.slice.call(arguments)) // [ 1, 2, 3, 4, 5 ]
  console.log(Array.prototype.slice.call({ '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 })) // []
}

whatever(1,2,3,4,5)

Why is it that the third console.log outputs an empty array?

like image 471
Derek Chiang Avatar asked Jul 10 '13 06:07

Derek Chiang


2 Answers

Because in order for Array.prototype.slice to work, you need to pass an array-like object. And in order for an object to fit that category, it needs a length property, which your object doesn't have. Try this:

var arr = { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 };
arr.length = 5;
var res = Array.prototype.slice.call(arr);
console.log(res);

FIDDLE

like image 131
basilikum Avatar answered Nov 16 '22 19:11

basilikum


As @basilikum described, this is because .length is required in order to use .slice(). To understand why it's required, imagine that you were writing your own version of Array.prototype.slice() after reading the MDN docs:


Syntax

Array.slice(begin[, end])

Parameters

begin

Zero-based index at which to begin extraction.

As a negative index, begin indicates an offset from the end of the sequence. slice(-2) extracts the second-to-last element and the last element in the sequence.

end

Zero-based index at which to end extraction. slice extracts up to but not including end.

slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3).

As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence.

If end is omitted, slice extracts to the end of the sequence.


To handle all those cases and a few more not listed, your code would have to be something along these lines (this may have bugs but should be close):

Array.prototype.myslice = function( begin, end ) {
    // Use array length or 0 if missing
    var length = this.length || 0;
    // Handle missing begin
    if( begin === undefined ) begin = 0;
    // Handle negative begin, offset from array length
    if( begin < 0 ) begin = length + begin;
    // But make sure that didn't put it less than 0
    if( begin < 0 ) begin = 0;
    // Handle missing end or end too long
    if( end === undefined  ||  end > length ) end = length;
    // Handle negative end (don't have to worry about < 0)
    if( end < 0 ) end = length + end;
    // Now copy the elements and return resulting array
    var result = [];
    for( var i = begin;  i < end;  ++i )
        result.push( this[i] );
    return result;
};

That's why .slice() requires this.length—you wouldn't be able to write the function without it.

like image 27
Michael Geary Avatar answered Nov 16 '22 17:11

Michael Geary