Проблемы создания хороших интерфейсов в функциях Javascript

Как я могу реализовать высококачественные подпрограммы (упомянутые Стивом Макконнеллом в Code Complete, глава 7) в некотором коде Javascript? Например, в этом случае:

$('#variable').on('click', function(){
                          //do some stuff
});

Это очень распространенный фрагмент, но он передает функцию в качестве параметра другой функции. На мой взгляд, не являются самодокументированными (не читаемыми) и не поддерживают абстракцию программы, как указано в книге; но очень общеизвестно видеть.

2 ответа

Решение

Вы можете назначить функцию, которую вы передаете, локальной переменной, так что, по крайней мере, вы можете дать ей имя:

var onClickCallback = function() {
                      //do some stuff
};

$('#variable').on('click', onClickCallback);

Предполагая, что узел DOM #variable является корнем элемента пользовательского интерфейса "виджет", который вызывает службу и к которой добавлен CSS-класс "clicked".

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

/**
 * Responsible for performing some
 * significant action(s) related to
 * widgets.
 */
function WidgetService() {}

WidgetService.prototype.doSomething = function() {
    //do some stuff
};

/**
 * Responsible for wiring up the object
 * graph backing the widget.
 */
function WidgetController($, widgetService) {
    this._service = widgetService;
    this._model = new WidgetModel({
        renderCb: renderCb.bind(this)
    });
    this._view = new WidgetView($, this._model);

    this.onClick = this._onClick.bind(this);

    function renderCb() {
        this._view.render();
    }
}

WidgetController.prototype._onClick = function() {
    this._service.doSomething();
    this._model.isClicked = true;
};

/**
 * Responsible for encapsulating the
 * state of the UI element.
 */
function WidgetModel(options) {
    options = options || {
        renderCb: noop
    };

    this._renderCb = options.renderCb;
}

WidgetModel.prototype = {
    _isClicked: false,

    get isClicked() {
        return this._isClicked;
    },

    set isClicked(value) {
        this._isClicked = value;
        this._renderCb(this._isClicked);
    }
};

/**
 * Responsible for interacting with the DOM.
 */
function WidgetView($, model) {
    this._$ = $;
    this._model = model;

    this.render = this._render.bind(this);
}

WidgetView.prototype.el = '#variable';

WidgetView.prototype._render = function() {
    this._$(this.el).addClass(this._model.isClicked ? 'clicked' : '');
};

/**
 * Responsible for linking the DOM
 * event with the controller.
 */
function WidgetRouter($, controller) {
    $(WidgetView.prototype.el).on('click', controller.onClick);
}

function noop() {}

$(function() {
    // Go...
    var s, c, r;

    s = new WidgetService();
    c = new WidgetController($, s);
    r = new WidgetRouter($, c);

    // Now clicking on the element with ID '#variable' will add a class of clicked to it.  
});
Другие вопросы по тегам