Implementing Python-like slice in JavaScript Python has by far the best slice implementation I've ever used. As a quick evening project I felt like trying to implement Python's slice construct in a JavaScript function (thinking about sending a pr to a certain library with Python's slice semantics). I came up with the code below, and I'm trying to improve my code. I would love feedback or alternative implementations.

I think I hit all the edge cases, but I only briefly skimmed the Python source, so I could easily be wrong.

/**
 * Implementation of Python's `slice` function... Get a cloned subsequence
 * of an iterable (collection with length property and array like indexs).
 * Will handle both strings and array(likes).
 * 
 * @param {Array|String} collection
 * @param {None|Integer} start First index to include. If negative it will be indicies from end
                             (i.e. -1 is last item). Omit or pass 0/null/undefined for 0.
 * @param {None|Integer} end Last index to include. If negative it will be indicies from end
                             (i.e. -1 is last item). Omit or pass null/undefined for end.
 * @param {None|Intger} step Increments to increase by (non-1 will skip indicies). Negative values
                              will reverse the output.
 * @returns {Array|String} sliced array
 *
 * @example
 * var list = [1, 2, 3, 4, 5]
 * slice(list) // =&gt; [1, 2, 3, 4, 5]
 * slice(list, 2) // =&gt; [3, 4, 5]
 * slice(list, 2, 4) // =&gt; [3, 4]
 * slice(list, -2) // =&gt; [4, 5]
 * slice(list, null, -1) // =&gt; [1, 2, 3, 4]
 * slice(list, null, null, 2) // =&gt; [1, 3, 5]
 * slice(list, null, null, -2) // =&gt; [5, 3, 1]
 * slice("kids a devil I tell ya", 7, -10, -1) // =&gt; "lived"
 */
function slice(collection, start, end, step) {
    var length = collection.length,
        isString = typeof collection == "string", // IE&lt;9 have issues with accessing strings by indicies ("str"[0] === undefined)
        result = [];
    if (isString) {
        collection = collection.split("");
    }
    if (start == null) {
        start = 0;
    } else if (start &lt; 0) {
        start = length + start;
    }
    if (end == null || end &gt; length) {
        end = length;
    } else if (end &lt; 0) {
        end = length + end;
    }
    if (step == null) {
        step = 1;
    } else if (step === 0) {
        throw "Slice step cannot be zero";
    }
    if (step &gt; 0) {
        for (; start &lt; end; start += step) {
            result.push(collection[start]);
        }
    } else {
        for (end -= 1; start &lt;= end; end += step) {
            result.push(collection[end]);
        }
    }
    // Return a string for input strings otherwise an array
    return isString ? result.join("") : result;
}
Throwing errors

You should only throw errors, not strings.

throw new Error("Slice step cannot be zero");


Leveraging existing API

What you're doing looks very similar to the existing slice except for the last parameter, step. This could be more compact if you used built-in slice for everything and then apply the step afterward.

function slice(collection, start, end, step) {
    var slice = collection.slice || Array.prototype.slice,
        sliced = slice.call(collection, start, end),
        result, length, i;

    if (!step) {
        return sliced;
    }
    result = [];
    length = sliced.length;
    i = (step &gt; 0) ? 0 : length - 1;
    for (; i &lt; length &amp;&amp; i &gt;= 0; i += step) {
        result.push(sliced[i]);
    }
    return typeof collection == "string" ? result.join("") : result;
}


This would require you to pass undefined in the places you have null in your usage examples. If you want to be able to pass null, you can add if (start === null) start = undefined, and the same for end.