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

Аргументы функции

Узнать, сколько функция принимает аргументов, можно в её свойстве length.

var l = (function (a, b, c) {}).length; // == 3

Узнать, сколько в неё было передано аргументов, можно в свойстве length объекта arguments.

(function (a, b, c) {
  var l = arguments.length; // == 2
})(100, 200);

Хоть объект arguments и похож на массив, но таковым он не является. У него нет методов, которые есть у массивов. Чтобы превратить его в настоящий массив нужно сделать его независимую копию.

var args = Array.prototype.slice.call(arguments);
Комментарии к заметке: 4

Реализация WebSocket клиента для Node.js

Для нагрузочного тестирования WebSocket сервера в проекте мы решили написать ботов, имитирующих подключения пользователей, и создать с их помощью некую активность. Ботов написал я на Node.js в виде скрипта с параметрами.

Интересным моментом в этой задаче оказался выбор модуля, реализующего WebSocket подключение. Если в браузере для этого есть специальный объект, который полностью скрывает от разработчик всю кухню подключения, рукопожатия и передачи данных, то в Node.js это нужно делать, что называется, руками. Для этих целей написаны различные модули. Для моих нужд самым подходящим оказался модуль websocket.

Работа WebSocket клиента устроена аналогично net.Socket:


// Создаётся экземпляр клиента
var WebSocketClient = require('websocket').client;
var client = new WebSocketClient();

// Вешаем на него обработчик события подключения к серверу
client.on('connect', handler);

// Подключаемся к нужному ресурсу
client.connect('ws://localhost:9000/');

В обработчик подключения в качестве аргумента передаётся объект, который может принимать и отсылать сообщения:


function handler(connection) {
  connection.on('message', function (message) {
    // делаем что-нибудь с пришедшим сообщением
    console.log(message);
  }
  // посылаем сообщение серверу
  connection.sendUTF('Hi, there!');
}

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

Плагин Backbone.Native

У Backbone среди зависимостей есть жесткая привязка jQuery. С помощью неё обеспечивается манипуляция с DOM-элементами, XHR-запросы и делегация событий. Всё это было реализовано в компактном плагине Backbone.Native.

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

Кеширование медленных вычислений

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

В библиотеке Underscore.js есть метод, который для любой функции делает версию этой функции с кешированием — memoize.

Так, например, её можно применять для кеширования _.template().


var View = Backbone.View.extend({
  render: function () {
    var markup = _.template(this.template, this.model.toJSON());
    this.$el.html(markup);
  }
});

Вместо того, чтобы при каждом обновлении данных компилировать тектовый шаблон , он будет преобразован в функцию только при первом использовании. А все последующие вызовы будут использовать уже скомпилированный вариант.


var cachedTemplate = _.memoize(_template);
var View = Backbone.View.extend({
  render: function () {
    var markup = cachedTemplate(this.template, this.model.toJSON());
    this.$el.html(markup);
  }
});

Разница в производительности при рендеринге простенького шаблона и его кешированного варианта налицо.

Все скомпилированные шаблоны находятся в хранилище ключ-значение внутри замыкания _.memoize(). Ключом в нашем случае выступает текст шаблона, а значением — скомпилированная функция.

В качестве ключа может быть не только текстовая строка. Если первый параметр кешируемой функции не является строкой, то ключом станет то, что венёт его метод .toString() . В некоторых случаях это может оказаться неприемлемо (например, когда первым аргументом идёт объект). Чтобы избежать коллизий, в _.memoize() можно передать опциональную функцию, генерирующую ключ из аргументов.


var cachedTemplate = _.memoize(_.template, function (value) {
  return JSON.stringify(value);
});

Аналогичный приём, но без использования Underscore.js я применяю для кеширования асинхронных запросов.

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

Коллекция разнотипных объектов в Backbone.js

В коллекциях Backbone.js не обязательно должны храниться объекты одного типа. Если в неё явно добавить экземпляр класса, унаследованного от Backbone.Model , то он без изменений будет помещён в неё. У коллекции есть атрибут model , в котором хранится конструктор модели. Когда в коллекцию добавляется простой объект, то он в качестве параметра передаётся в этот конструктор.

В атрибуте model можно, фактически, указать любую функцию. Возвращаемые значения (экземпляры классов, унаследованных от Backbone.Model ) будут помещаться в коллекцию. Такие функции ещё называют фабриками или фабричными методами. В документации к атрибуту model как раз представлен вариант такой фабрики.

Чтобы каждый раз не писать однотипных проверок в подобных ситуациях, я предлагаю пользоваться паттерном «Фабрика фабрик»:


App.Factory = function (getter, hash, def) {
  return function () {
    var value = getter.apply(this, arguments);
    var ctor = hash[value] ? hash[value] : def;
    return new ctor(arguments[0], arguments[1]);
  };
};

  • getter — функция, которая возвращает значение, на основании которого будет выбираться требуемый конструктор;
  • hash — объект ключ-значение с перечислением конструкторов, которые могут использоваться фабрикой;
  • def — конструктор по-умолчанию.

Предположим, что в коллекции могут находиться объекты следующих классов:


App.Item = Backbone.Model.extend({});
App.ItemNews = App.Item.extend({});
App.ItemAction = App.Item.extend({});
App.ItemMessage = App.Item.extend({});

Таким образом, фабрика объектов будет выглядеть так:


App.ItemFactory = App.Factory(
  function (attr) { return attr.type; },
  {
    "news": App.ItemNews,
    "action": App.ItemAction,
    "message": App.ItemMessage
  },
  App.Item
);

App.Factory возвращает функцию, которая, в свою очередь, будет возвращать требуемый экземпляр в зависимости от значения атрибута type . Эту функцию и передаём в описание коллекции.

App.Items = Backbone.Collection.extend({
  model: App.ItemFactory
});

Теперь коллекция будет заполняться моделями нужного типа автоматически.

Практическое применение паттерна можно посмотреть в исходном коде примера.

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