Как написать полифил для метода map в JavaScript
Что проверяют
Знаете ли вы, как map устроен по спецификации, а не только «пройти циклом и вернуть новый массив»: thisArg, разреженные массивы, проверка аргументов, работа с array-like объектами. Следом просят объяснить, почему внутри полифила нельзя стрелочную функцию, или сразу написать filter и reduce.
Как отвечать
Покажите код и проговорите каждое решение:
if (!Array.prototype.map) {
Object.defineProperty(Array.prototype, 'map', {
value: function (callback, thisArg) {
if (this == null) throw new TypeError('this is null or undefined');
if (typeof callback !== 'function') throw new TypeError(callback + ' is not a function');
const O = Object(this);
const len = O.length >>> 0;
const result = new Array(len);
for (let i = 0; i < len; i++) {
if (i in O) result[i] = callback.call(thisArg, O[i], i, O);
}
return result;
},
writable: true,
configurable: true
});
}function, а не стрелка: стрелочная функция не имеет своегоthis, и массив до полифила не доедет;Object(this)и>>> 0: метод работает и на array-like объектах вродеarguments, длина приводится к числу;i in O: дырки пропускаются, колбэк на них не вызывается, и[1, , 3]превращается в[2, , 6]с сохранённой дыркой;- длина фиксируется до цикла: элементы, добавленные во время обхода, не посещаются;
defineProperty: свойство получается неперечисляемым, как у встроенного метода.
На практике map есть везде с ES5, а полифилы в проект приносит core-js через Babel preset-env, но задача проверяет понимание спецификации.
Где спотыкаются
Собирают результат через push. Дырки схлопываются, длина результата не совпадает с исходной.
Забывают проверить, что callback функция, и передают в него только элемент, без индекса и самого массива.
Присваивают Array.prototype.map = ... напрямую. Метод становится перечисляемым и вылезает в каждом for...in по массиву.