Заметки в категории «JavaScript» (страница 4)

Однократный вызов функции с таймаутом

Бывает нужно подписаться на какое-то событие, но нет уверенности, что оно вообще может наступить. Можно условиться о неком интервале, в течение которого мы ожидаем это событие.


/**
 * @param {Function} fn function which needs to be called only once
 * @param {Number} timeout for external call
 * @param {Object} [context]
 * @returns {Function}
 */
function withTimeout(fn, timeout, context) {
  var timerId, state = true;

  function cb() {
    if (timerId) {
      clearTimeout(timerId);
      timerId = null;
    }
    if (state) {
      state = false;
      fn.apply(context || window, arguments);
    }
  }

  timerId = setTimeout(cb, timeout);

  return cb;
}

С использованием промисов код получился бы чуть короче, но потребовал бы внешнюю зависимость в виде jQuery или другой библиотеки, реализующей промисы.

Комментарии к заметке: 1

Вызов функции «чуть позже»

Мы уже привыкли пользоваться setTimeout(fn, 0) , чтобы запланировать вызов некой функции и продолжить выполнение текущего кода. Мы ожидаем, что запланированный вызов произойдёт максимально быстро на сколько это возможно. Однако у всех JavaScript движков есть ограничение — 4 миллисекунды. Побороть это ограничение можно, если пользоваться requestAnimationFrame или setImmediate.


/**
 * Runs the callback at the next available opportunity.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/window.setImmediate
 */
var setImmediate = function(callback) {
  return (
    window.setImmediate ||
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.setTimeout
  )(callback, 0);
};

/**
 * Clears a callback previously registered with `setImmediate`.
 * @param {id} id The identifier of the callback to abort
 */
var clearImmediate = function(id) {
  return (window.clearImmediate ||
    window.cancelAnimationFrame ||
    window.webkitCancelAnimationFrame ||
    window.mozCancelAnimationFrame ||
    window.clearTimeout)(id);
};

Метод setImmediate объекта window был реализован в IE10 и Node.js 10+, но встретил сопротивление со стороны разработчиков Firefox и WebKit из-за неясности спецификации.

Комментарии к заметке: 4

Колбек в триггере события

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

Однако, в числе прочих дополнительных параметров, событию можно передать функцию, которая может быть вызвана внутри обработчика.


var a = {};

$(a).on("customEvent", function (e, callback) {
  callback(Math.random());
});

$(a).trigger("customEvent", [
  function (v) { alert(v); }
]);

Пример на jsfiddle.net

Такой трюк может быть чрезвычайно полезен в случаях, когда одному модулю нужны какие-то данные от другого модуля, но связаться между собой они могут только с помощью событий.

Оставте свой комментарий

Операции над коллекциями объектов Backbone.js

Для работы с коллекциями в Backbone.js импортируется 28 методов из библиотеки Underscore.js. Среди них, пожалуй, самыми востребованными являются .forEach(), .filter(), .reject(), .find(), .without().

Предположим, что нужно найти в коллекции элементы, соответствующие определённому условию и уничтожить их.


var boxes = new Boxes(); // коллекция объектов
// предположим, что она уже заполнена нужными данными
var boxesToDestroy = boxes.filter(function (box) {
  return box.isReadyToDestroy();
});
_.forEach(boxesToDestroy, function (box) {
  box.destroy();
});

Выглядит громоздко. Всё это по тому, что методы Underscore.js возвращают массив, а не новый экземпляр коллекции.

Благо в Underscore.js есть метод .chain(), который позволяет всё значительно упростить.


boxes.chain()
  .filter(function (box) { return box.isReadyToDestroy(); })
  .forEach(function (box) { box.destroy(); });

Этот код можно ещё упростить, воспользовавшись методом .invoke().


boxes.chain()
  .filter(function (box) { return box.isReadyToDestroy(); })
  .invoke("destroy");

Так можно стоить цепочки из любых операций над элементами коллекции. Если по завершению цепочки нужно будет сохранить результат выборки в переменную, то последним вызовем метод .value().

Оставте свой комментарий

Истинные размеры изображения

Когда картинка появилась на странице, то к ней сразу же начинают применяться стили и узнать её истинные габариты через свойства width и height уже нельзя.

Во всех современных браузерах (включая IE9 и старше) есть свойства naturalWidth и naturalHeight, которые после полной загрузки картинки принимают значения ширины и высоты изображения, соответственно.


var img = document.getElementById("gallery-image"),
    width = img.naturalWidth,
    height = img.naturalHeight;

В IE8 и младше этих свойств нет. Получить не модифицированные размеры можно, если создать новый элемент с тем же самым значением атрибута src и уже у него прочитать свойства width и height.


function getDimensions(sourceImage) {
    var img = new Image();
    img.src = sourceImage.src;
    return {
        width: img.width,
        height: img.height
    };
}
Комментарии к заметке: 2