Продвинутые практики JavaScript в исходном коде Backbone.js
Backbone.js

Продвинутые практики JavaScript в исходном коде Backbone.js

Оригинал: Learning Advanced JavaScript via the Backbone.js source code, Chris Wren

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

В этой статье я поделюсь понятиями, которые помогли мне лучше узнать JavaScript и Backbone.js.

Немедленно вызываемые функции

Немедленно вызываемые функции имеют следующий синтаксис:


(function () {
  ...
}());

Замечение: открывающаяся и закрывающаяся скобки означают, что функция вызывается сразу.

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

Например, в первых выражениях исходного кода Backbone.js глобальные переменные, которые потенциально могут быть изменены в другом месте на странице, сохраняются в локальных:


var root = this;
var previousBackbone = root.Backbone;
var array = [];
var push = array.push;
var slice = array.slice;
var splice = array.splice;

[Более подробно о немедленно вызываемых функциях вы можете прочитать тут — прим. переводчика]

Наследование

Представления в приложении выглядят примерно так:


var MyView = Backbone.View.extend({
    ...
});

MyView расширяет базовый объект Backbone.View, это означает, что он наследует свойства и методы Backbone.View. Разработчик может переопределить эти унаследованные свойства и методы в своей реализации.

Есть три вида свойств и методов, которые содержат базовые классы Backbone.js:

Свойства/методы, которые должны быть переопределены

В комментариях к методам initialize и render в коде Backebone.js явно указано, что эти методы должны быть перелпределены:


initialize: function () {},

render: function () {
    return this;
}

Метод initialize вызывается в конструкторе Backbone.View и может содержать какую-то пользовательскую логику, а метод render отвечает за отрисовку представления. Подробнее о конструкторе мы поговорим позже.

Свойства/методы, которые могут быть переопределены

Метод sync в Backbone.Model и Backbone.Collection — это лучший пример метода, который работает без реализации пользователя, но также может быть переопределен для реализации пользовательского функционала:


sync: function () {
    return Backbone.sync.apply(this, arguments);
}

В реализации метода sync по умолчанию просто вызывается метод Backbone.sync с аргументами, контекст this он получает с помощью функции apply. Подробнее об этом ниже.

Например, вы можете реализовать сохранение модели или коллекции в localStorage . Чтобы добиться этого, вы можете переопределить метод sync в модели:


var MyModel = Backbone.Model.extend({
    sync: function (method, model, options) {
        if (method === 'read') {
            if (window.localStorage.getItem('myData')) {
                return window.localStorage.getItem('myData');
            } else {
                return Backbone.sync.apply(this, arguments);
            }
        } else {
            return Backbone.sync.apply(this, arguments);
        }
    }
});

Выше я просто проверяю, если в localStorage есть значение с ключом MyData — значение берется оттуда. Если нет, я вызываю метод Backbone.Sync так, как это реализовано в Backbone.Model.sync по умолчанию. Эта особенность Backbone является действительно мощной, так как позволяет вам выбрать, на каком уровне кастомизировать ваши классы.

Менее распространенный способ переопределения

В комментарии к методу sync явно сказано, что он может быть переопределен, но вы также можете переопределить и многие другие методы Backbone. В документации Backbonе представлен следующий пример переопределения метода Backbone.Model.set:


var Note = Backbone.Model.extend({
    set: function (attributes, options) {
        Backbone.Model.prototype.set.apply(this, arguments);
        ...
    }
});

Это похоже на шаблон, который мы рассматривали выше, за исключением того, что мы вызываем метод Backbone.Model set вместо его копирования. Обратите внимание, что мы вызываем метод через прототип класса, тем самым обращаемся к наследованным свойствам Backbone.Model.

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

Свойства/методы, которые не должны быть переопределены

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

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

Вот пример двух приватных методов в классе Backbone.View:


_configure: function(options) {
    if (this.options) options = _.extend({}, _.result(this, 'options'), options);
    _.extend(this, _.pick(options, viewOptions));
    this.options = options;
},

_ensureElement: function() {
    if (!this.el) {
        var attrs = _.extend({}, _.result(this, 'attributes'));
        if (this.id) attrs.id = _.result(this, 'id');
        if (this.className) attrs['class'] = _.result(this, 'className');
        var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
        this.setElement($el, false);
    } else {
        this.setElement(_.result(this, 'el'), false);
    }
}

Эти методы являются вспомогательными для других методов и конструктора Backbone.View. Например, метод _.configure() вызывается в конструкторе Backbone.View для настройки представления на основе переданных пользователем параметров.

Конструкторы JavaScript

После написания собственного представления, вы вызываете его, написав что-то вроде этого:


MyViewInstance = new MyView();

Ключевое слово new создает экземпляр MyView путем вызова конструктора и возвращения объекта, содержащего унаследованные свойства и методы. Скорее всего во время программирования на Backbone вы не будете определять собственные конструкторы, т.к. унаследованный конструктор вполне справляется со своей функцией. Вот исходный код конструктора Backbone.View:


var View = Backbone.View = function(options) {
    this.cid = _.uniqueId('view');
    this._configure(options || {});
    this._ensureElement();
    this.initialize.apply(this, arguments);
    this.delegateEvents();
};

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

Ключевое слово this

Понимание ключевого слова this в JavaScript крайне важно. Оно ссылается на объект, в контексте которого вызвана функция. [Более подробно об использовании ключевого слова this в JavaScript вы можете прочитать тут — прим. переводчика]. В представлении Backbone, когда вы пишете this.render() в функции инициализации, this ссылается на ваше представление, потому что оно передается с помощью функции apply в конструкторе.

Интересно использование this в Backbone.View в методе delegateEvents. Следующее выражение в этом методе связывает обработчики событий JQuery с соответствующим представлением так, чтобы переменная this внутри обработчика ссылалась на представление вместо элемента DOM, на котором произошло событие:


method = _.bind(method, this);

Без строчки выше в следующем примере this в обработчике buttonClickHandler ссылался бы на элемент DOM с классом MyButton, и при вызове this.render() возникла бы ошибка:


var MyView = Backbone.View.extend({
    events: {
        'click .myButton': 'buttonClickHandler'
    },
    buttonClickHandler: function (event) {
        // this would refer to the jQuery element not the MyView instance
        this.render();
    }
});

underscore.js

underscore.js  — это мощная утилита, позволяющая писать более семантичный код. При работе с массивами или объектами вы можете использовать такие методы, как _.first, _.any и _.without, вместо того, чтобы писать собственные функции манипулирования данными. Backbone.js пробрасывает методы underscore.js непосредственно в Backbone.Collection и Backbone.Model для более удобного доступа к этим методам.

В дополнение к функциям, служащим для удобства пользователя, underscore реализует кросс-браузерные версии новых функций JavaScript. underscore проверяет, поддерживает ли браузер определенный метод, и если да — использует нативную реализацию.

Заключение

Я надеюсь, что эта статья будет полезна разработчикам, желающим расширить свои знания JavaScript и Backbone.js! Я рекомендую исследовать эти концепции, тестируя на примерах в консоли браузера или в командной строке Node.js. Дайте мне знать, если у вас есть какие-либо вопросы.

Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться