Skip to content

Instantly share code, notes, and snippets.

@jayphelps
Last active May 5, 2020 22:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jayphelps/52ed7a4f1bcdd0108159c1d513964e3b to your computer and use it in GitHub Desktop.
Save jayphelps/52ed7a4f1bcdd0108159c1d513964e3b to your computer and use it in GitHub Desktop.
For the lolz
// The spec might seem somewhat convoluted but it's because you can actually
// use reduce on anything array-like not just arrays.
// e.g. Array.prototype.reduce.call('abc', (acc, char) => char + acc); // "cba"
function reduce(callback, initialValue) {
// In "strict mode" calling this with either null or undefined will make
// this === null in either case, as per spec.
if (this === null) {
throw new TypeError('reduce called on null or undefined');
}
// Spec says to do these before the typeof callback check.
// This actually could be observable from the outside, because o.length
// might be a getter! Chrome does this right, I checked!
const o = Object(this);
const len = Math.max(o.length | 0, 0);
if (typeof callback !== 'function') {
// Symbols (maybe others) will error if you try to interpolate them,
// but not if you first do String(callback).
throw new TypeError(`${String(callback)} is not a function`);
}
if (len === 0 && arguments.length < 2) {
throw new TypeError('Reduce of empty array with no initial value');
}
let k = 0;
let accumulator;
// Important not to just do `initialValue === undefined` because
// undefined is a valid initialValue and not the same thing as
// omitting that argument.
if (arguments.length >= 2) {
accumulator = initialValue;
} else {
let kPresent = false;
while (!kPresent && k < len) {
// We're able to skip the `let Pk` stuff because JS will do the same thing
// under th hood with normal property accesses and I'm lazy right now.
// https://tc39.es/ecma262/#sec-topropertykey does the convertion:
// https://tc39.es/ecma262/#sec-tostring note this is not the same as obj.toString() !
// This is effectively what the spec asks for. The spec's HasProperty(O, Pk)
// is NOT the same thing as o.hasOwnProperty(Pk)
// https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
kPresent = k in o;
if (kPresent === true) {
accumulator = o[k];
}
k = k + 1;
}
// I tried to hit this code path in Chrome's native array reduce, but I
// couldn't induce it. I might be misunderstanding the spec, or it just
// might be such an edge case that it doesn't deal with it.
if (kPresent === false) {
throw new TypeError(`No initial value and not array-like`);
}
}
while (k < len) {
// Skipping the 'Let Pk be ! ToString(k)' stuff again cause I'm lazy and
// it's rather boring. This works the same, I think.
const kPresent = k in o;
if (kPresent === true) {
const kValue = o[k];
accumulator = callback.call(undefined, accumulator, kValue, k, o);
}
k = k + 1;
}
return accumulator;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment