Полифиллы и замены

Zepto.js — это минималистичная библиотека, ориентированная на работу с современными браузерами, которая предлагает знакомый интерфейс, похожий на jQuery. Несмотря на компактность, она обеспечивает возможность работы с методами, которые могут отсутствовать в старых браузерах. Для этого используются полифиллы и замены стандартных методов JavaScript.


Полифиллы для коллекций и массивоподобных объектов

Zepto активно использует массивоподобные объекты для работы с DOM-элементами. В некоторых старых браузерах отсутствуют методы forEach, map, filter для NodeList. Zepto реализует собственные обходы коллекций:

Zepto.isArrayLike = function(obj) {
  var length = !!obj && 'length' in obj && obj.length,
      type = $.type(obj);
  return 'function' != type && !$.isWindow(obj) && (
    'array' === type || length === 0 ||
    (typeof length === 'number' && length > 0 && (length - 1) in obj)
  );
};

$.each = function(elements, callback){
  var i, key;
  if (isArrayLike(elements)) {
    for (i = 0; i < elements.length; i++)
      if (callback.call(elements[i], i, elements[i]) === false) return elements;
  } else {
    for (key in elements)
      if (callback.call(elements[key], key, elements[key]) === false) return elements;
  }
  return elements;
};

$.each служит универсальным полифиллом обхода, заменяя стандартный Array.prototype.forEach там, где он недоступен.


Замена querySelectorAll и getElementsByClassName

Zepto поддерживает поиск элементов через CSS-селекторы, используя querySelectorAll там, где это возможно, но предоставляет альтернативы для браузеров, где метод отсутствует:

zepto.qsa = function(element, selector){
  return element.querySelectorAll(selector);
};

Для старых браузеров возможно использование обхода дочерних элементов с проверкой соответствия селектору. В Zepto это реализовано через внутренние функции типа matches:

var el = document.createElement('div');
if (!el.matches) {
  el.matches = el.msMatchesSelector || el.webkitMatchesSelector;
}

Так обеспечивается корректная работа селекторов в Internet Explorer 9+ и старых WebKit.


Полифиллы для событий

События являются критическим аспектом кроссбраузерности. Zepto заменяет методы addEventListener и removeEventListener там, где они отсутствуют, и добавляет полифиллы для input, change, focusin/focusout:

$.Event = function(type, props) {
  if (!(this instanceof $.Event)) return new $.Event(type, props);
  var event = document.createEvent('Events');
  event.initEvent(type, true, true);
  if (props) $.extend(event, props);
  return event;
};

Эта конструкция обеспечивает создание событий, совместимых со старыми браузерами, включая IE8, где отсутствует стандартный конструктор Event.

Для делегирования событий используется $.proxy:

$.proxy = function(fn, context) {
  return function() {
    return fn.apply(context, arguments);
  };
};

Это позволяет сохранять контекст this при обработке событий, что критично для старых реализаций DOM API.


Работа с AJAX и полифиллы для XHR

Zepto реализует легковесный интерфейс для AJAX-запросов через $.ajax, автоматически проверяя наличие XMLHttpRequest. Для старых браузеров используется ActiveX:

$.ajaxSettings = {
  type: 'GET',
  beforeSend: function(){},
  success: function(){},
  error: function(){},
  complete: function(){}
};

$.ajax = function(options){
  var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
  xhr.open(options.type, options.url, true);
  xhr.onreadystatecha nge = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) options.success(xhr.responseText, xhr.status, xhr);
      else options.error(xhr, xhr.status, xhr.statusText);
      options.complete(xhr, xhr.status);
    }
  };
  xhr.send(options.data || null);
};

Такой подход обеспечивает работу AJAX даже в старых версиях IE, заменяя современный fetch.


Полезные полифиллы для анимаций и CSS

Zepto имеет собственный модуль анимаций через $.fn.animate. В старых браузерах, где отсутствует requestAnimationFrame, используется замена через setTimeout:

window.requestAnimationFrame = window.requestAnimationFrame ||
                               window.webkitRequestAnimationFrame ||
                               window.mozRequestAnimationFrame ||
                               function(callback){ return setTimeout(callback, 16); };

Аналогично, для свойств CSS с префиксами (например, transform), Zepto применяет динамическое определение:

var vendors = ['webkit', 'Moz', 'O', 'ms'];
function prefixed(prop) {
  var style = document.createElement('div').style;
  if (prop in style) return prop;
  var capitalized = prop.charAt(0).toUpperCase() + prop.slice(1);
  for (var i = 0; i < vendors.length; i++) {
    var vendorProp = vendors[i] + capitalized;
    if (vendorProp in style) return vendorProp;
  }
  return prop;
}

Это позволяет корректно изменять стиль элементов без опасений, что метод будет недоступен в браузере пользователя.


Универсальные методы для работы с объектами

Zepto добавляет полифиллы для функций Object.keys и JSON.stringify:

if (!Object.keys) {
  Object.keys = function(obj) {
    var keys = [];
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) keys.push(key);
    }
    return keys;
  };
}

if (!window.JSON) {
  window.JSON = {
    stringify: function(obj) { /* упрощённая реализация */ },
    parse: function(str) { /* упрощённая реализация */ }
  };
}

Это обеспечивает совместимость с очень старыми браузерами без современных методов работы с объектами и сериализации данных.


Итоговая роль полифиллов и замен

Полифиллы и замены в Zepto позволяют:

  • Поддерживать работу библиотечных методов на старых браузерах.
  • Унифицировать API для работы с DOM, событиями, AJAX и стилями.
  • Сократить необходимость включения сторонних полифиллов в проект.
  • Обеспечить предсказуемое поведение методов на любых устройствах.

Zepto сочетает минимальный размер с широким охватом браузерной совместимости благодаря грамотно реализованным внутренним заменам и полифиллам.