Как стать автором
Обновить

Комментарии 14

«Полифилы». Есть же хорошее слово — костыли.

Ожидал увидеть список браузеров где не поддерживается ES6. У вас какой-то специфический заказчик с браузером 8-10 -летней давности? Или что-то не-браузерное с зафиксированным движком?

Хмм, а какие браузеры полностью поддерживают ES6? Я ни одного не знаю. Например, оптимизация хвостовой рекурсии (вернее, PTC) поддерживается сейчас только в Safari.

Вопрос был в конексте статьи и относился к тем четырём функциям Array добавленным в ES6, речь о полной поддержке не шла. Только я в спешке его не совсем корректно задал: автор в начале статьи обещал что раскажет

о полифилах JavaScript: что это и зачем они нужны?

но так и не рассказал, зачем же нужны именно эти полифилы. Надеюсь, ответит позже здесь ниже.

Данные методы были добавлены в ES5 (IE9), ES6 разве что незначительно меняет их семантику - но тут даже о корректности по ES5 говорить не приходится -)

Я так понимаю что данные методы реализованы в основном для доэджевой эпохи интернет эксплореров. Ослами сейчас пользуются по статистике яндекса и лайвинета 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 и работают с учетом этой ошибки и могут падать на вашем полифиле.

До перехода на core-js, я долгое время использовал в качестве ES5-полифилов наработки Дугласа Крокфорда: ES5 и JSON-js. По мере выхода новых версий языка, также пользовался полифилами из MDN.

Давайте разберем, что не так в ваших полифилах, на примере .map.

  1. Эти методы должны быть объявлены как неперечесляемые свойства.

Зачем? Есть такие странные люди, что обходят массивы при помощи цикла for-in:

var array = [1, 2, 3];
for (var key in array) console.log(array[key]);
// => 1
// => 2
// => 3
// => function myMap !!!

Подобные "полифилы" очень часто ломает чужой код. И это, пожалуй, самая популярная ошибка, при их написании.

  1. .map и прочие методы массива, добавленные в ES5, игнорируют дырки в массивах.

Array(5).map((_, i) => i); // => [empty × 5]
Array(5).myMap((_, i) => i); // => [0, 1, 2, 3, 4]
  1. Как выше упомянул @kahi4, длина массива должна запоминаться до начала итерации

const array = [1];
array.map(i => array.push(i)); // => [2]
array.myMap(i => array.push(i)); // => бесконечный цикл

Кроме того, в этот момент длина должна приводиться с помощью внутренней операции ToLength - но это уже мелочи жизни.

  1. С 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
  1. "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']
  1. и далее. Обычно, полифилы данных методов, добавленных в ES5, рассчитаны на ES3 браузеры - и у них есть своя специфика.

Например, как было показано на примерах выше, данные методы - дженерики. Это значит, что они могут работать не только на массивах - но и на любых array-like сущностях - например, строках. Вот только в старых движках (IE8-, где строки не индексированы) без дополнительных костылей с этим проблемы - а где-то, например в старых версиях V8, есть соответствующие баги. И это только один момент специфики старых движков, что нужно учитывать.

  1. Полифил должен обнаруживать уже имеющуюся нативную фичу и использовать её, если есть такая возможность. Хотя бы так,

var myMap = [].map || function map() { /* ... */ };

но обычно все куда сложнее.

Пожалуйста, не пишите полифилы ES для использования в вашем проекте сами, если на все 100 не уверены, что без этого никак - обычно, можно найти уже готовые, проверенные, где кол-во ошибок сведено к минимуму.

Откуда в этой публикации появился тег `java`? :)

А причем здесь хаб - java?!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий