Комментарии 14
Ожидал увидеть список браузеров где не поддерживается ES6. У вас какой-то специфический заказчик с браузером 8-10 -летней давности? Или что-то не-браузерное с зафиксированным движком?
Хмм, а какие браузеры полностью поддерживают ES6? Я ни одного не знаю. Например, оптимизация хвостовой рекурсии (вернее, PTC) поддерживается сейчас только в Safari.
Вопрос был в конексте статьи и относился к тем четырём функциям Array добавленным в ES6, речь о полной поддержке не шла. Только я в спешке его не совсем корректно задал: автор в начале статьи обещал что раскажет
о полифилах JavaScript: что это и зачем они нужны?
но так и не рассказал, зачем же нужны именно эти полифилы. Надеюсь, ответит позже здесь ниже.
Я так понимаю что данные методы реализованы в основном для доэджевой эпохи интернет эксплореров. Ослами сейчас пользуются по статистике яндекса и лайвинета 0.19% и я так понимаю по большей части из-за специфики организаций(типа гос.структур, где это предусмотрено регламентом и какими-то местными шаманскими уставами). Есть ли вообще сейчас смысл в подобных фичах? Может я не прав.
Вот только то, что приведено в посте — это не полифилы. Полифил метода map должен так и называться — map, а вовсе не myMap.
Отвечу на пару комментариев сверху: хоть map, reduce и прочие уже 7 лет как везде есть, есть другие полифилы, которые по сей день выполняют свою работу. Статья же учебная, показывает что такое полифилл на простых и понятных примерах.
Но только это вредная статья и вот почему:
и опытным специалистам
не льстите себе
Полифил — это код, реализующий какую-то функциональность, которая не поддерживается в некоторых браузерах.
Не "какая-то", а крайне конкретная. Полифил по определению исключительно внесение новых API из спецификации в старые рантаймы. Вы не можете изменить прототип Array методом myMap и назвать это полифилом. Это будет просто патчингом прототипа, а это -- плохая практика. Так же ваш полифил обязан вести себя как метод в спецификации, потому что какая-то библиотека может использовать этот метод и завалиться, потому что ожидала другого поведения.
В статье, например, это правило не удовлетворено:
const foo = [5];
// оригинал
foo.map((i) => { foo.push(i) }); // > [undefined]
// теперь ваш myMap
foo.myMap((i) => { foo.push(i) }); // падает в бесконечный цикл
И какая-то слегка криво написанная библиотека прекрасно может положить ваш код.
Как я писал выше, сегодня мы будем реализовывать несколько полифилов: map, forEach, filter, reduce.
А теперь что-то по-интереснее, типа генератора, можно? А можно (вопрос с подвохом) на proxy? Да и core-js все эти методы реализует хитрым способом за один раз: https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/array-iteration.js
И далеко не все полифилы -- это обновление прототипа. BigInt самостоятельный такой объект.
Еще пункт: вообще сперва хорошо бы проверить, нет ли уже этого метода (поставленного другим полифилом или нативно в браузере)
А в комментариях можете поделиться своим опытом создания и реализации полифилов.
А вот тут самое главное: НЕ ПИШИТЕ СВОИ ПОЛИФИЛЫ. Для 99% подойдет core-js. Вместе с babel-env он умеет определять что используется в вашем коде и что нужно под какие-то браузеры и самостоятельно их расставлять и tree shake. И эта библиотека оттестирована миллионами разработчиков и миллиардами пользователей. Хуже того -- даже если там и есть ошибка, все остальные библиотеки используют core-js и работают с учетом этой ошибки и могут падать на вашем полифиле.
Давайте разберем, что не так в ваших полифилах, на примере .map
.
Эти методы должны быть объявлены как неперечесляемые свойства.
Зачем? Есть такие странные люди, что обходят массивы при помощи цикла for-in
:
var array = [1, 2, 3];
for (var key in array) console.log(array[key]);
// => 1
// => 2
// => 3
// => function myMap !!!
Подобные "полифилы" очень часто ломает чужой код. И это, пожалуй, самая популярная ошибка, при их написании.
.map
и прочие методы массива, добавленные в ES5, игнорируют дырки в массивах.
Array(5).map((_, i) => i); // => [empty × 5]
Array(5).myMap((_, i) => i); // => [0, 1, 2, 3, 4]
Как выше упомянул @kahi4, длина массива должна запоминаться до начала итерации
const array = [1];
array.map(i => array.push(i)); // => [2]
array.myMap(i => array.push(i)); // => бесконечный цикл
Кроме того, в этот момент длина должна приводиться с помощью внутренней операции ToLength
- но это уже мелочи жизни.
С ES6, для поддержки субклассинга,
.map
использует@@species
паттерн, его поддержка реализуется в несколько дополнительных строк.
class MyArray extends Array {}
MyArray.of(1, 2, 3).map(i => i) instanceof MyArray; // => true
MyArray.of(1, 2, 3).myMap(i => i) instanceof MyArray; // => false
"
this === null || this === window
условие сработает в том случае, если метод вызывается как отдельная функция" - что это?
В спецификации прописаны 2 вещи:
this
приводится к объекту с помощью операцииToObject
- данная операция кидает ошибку наnull
иundefined
.Метод должен быть задан в строгом режиме.
По идее, никто не запрещает запускать .map
на window
:
[].map.call(window, it => it); // => [global]
А у вас, кроме этого, полифил не будет работать в окружении, отличном от браузера, где нет window
- та же NodeJS.
Если ваш метод таки задается в строгом режиме, что явно не прописано ('use strict'
- но может быть и контекст модуля), проблем у вас больше. Например, примитивы не будут приводиться к объектам:
[].map.call('1', (v, i, o) => typeof o); // => ['object']
[].myMap.call('1', (v, i, o) => typeof o); // => ['string']
и далее. Обычно, полифилы данных методов, добавленных в ES5, рассчитаны на ES3 браузеры - и у них есть своя специфика.
Например, как было показано на примерах выше, данные методы - дженерики. Это значит, что они могут работать не только на массивах - но и на любых array-like сущностях - например, строках. Вот только в старых движках (IE8-, где строки не индексированы) без дополнительных костылей с этим проблемы - а где-то, например в старых версиях V8, есть соответствующие баги. И это только один момент специфики старых движков, что нужно учитывать.
Полифил должен обнаруживать уже имеющуюся нативную фичу и использовать её, если есть такая возможность. Хотя бы так,
var myMap = [].map || function map() { /* ... */ };
но обычно все куда сложнее.
Пожалуйста, не пишите полифилы ES для использования в вашем проекте сами, если на все 100 не уверены, что без этого никак - обычно, можно найти уже готовые, проверенные, где кол-во ошибок сведено к минимуму.
Откуда в этой публикации появился тег `java`? :)
А причем здесь хаб - java?!
Полифилы JavaScript: что это и зачем они нужны?