-1

How could I implement high-quality routines (mentioned by Steve McConnell, on Code Complete, chapter 7) on some Javascript code? For example, in this case:

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

This is a very common snippet, but it is passing a function as parameter to another function. In my point of view, are not self-documented (it is not readable) and does not maintain the program abstraction as the book indicates; but is very commmon to see.

Mauricio Moraes
  • 7,255
  • 5
  • 39
  • 59
José Augustinho
  • 110
  • 1
  • 10
  • 2
    Most people will likely not have that book at hand so you'll have to provide a non-paywalled link or explain what they mean by "high-quality routines" yourself. To me it sounds like the confusion of a Java programmer who cannot understand higher order functions or lambda functions. – Matti Virkkunen Jan 27 '15 at 19:23
  • You can see the entire chapter here, at [Google Books web site](https://books.google.com.br/books?id=I-83BAAAQBAJ&pg=PA161&lpg=PA161&dq=code+complete+chapter+7&source=bl&ots=4ER-qOoH4u&sig=GpxVkSwcoCMwLsuAWSIMpi4TS48&hl=pt-BR&sa=X&ei=3ObHVODHHq3dsATTz4LwCg&ved=0CEcQ6AEwBQ#v=onepage&q=high-quali&f=false) – José Augustinho Jan 27 '15 at 19:36
  • 1
    I tend to think this is just the nature of javascript. The language gives you plenty of rope to hang you self so you have to be much more careful and disciplined than with other languages. One thing you could and probably should do is assign the function you are passing to a local variable so you can at the very least name it. – bhspencer Jan 27 '15 at 19:40
  • 1
    I am not going to read 20 pages just to get to a definition. – Matti Virkkunen Jan 27 '15 at 20:03
  • @bhspencer Disagree. You can write good code in any language. JavaScript is no worse in this regard than C#, Java, VB, C++ etc. See my answer. – Ben Aston Jan 27 '15 at 22:07
  • I agree, you can write good code in javascript but the language does very little to help you do so. You can overlay principles of encapsulation on top of javascript and you have to in order to have a maintainable project. The trouble is there are a dozen equally valid different ways to do encapsulation in JS. Without a "official" way of doing it most people never really do it properly if at all. Hopefully classes in ECMAScript 6 will help with this. – bhspencer Jan 28 '15 at 00:26
  • @bhspencer Classes and block scoping won't help you write good code. Intelligence, experience and learning will. – Ben Aston Jan 28 '15 at 01:23

2 Answers2

2

You could assign the function you are passing to a local variable so at the very least you can give it a name:

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

$('#variable').on('click', onClickCallback);
bhspencer
  • 13,086
  • 5
  • 35
  • 44
  • With just this change the snippet isn't any more readable though. It's pretty obvious it's a click callback. Plus you can name inline functions too! Makes for better stack traces (your code doesn't help with those) – Matti Virkkunen Jan 27 '15 at 20:02
  • But it is testable. You cannot test an anonymous function because you don't have a reference to it. – bhspencer Jan 27 '15 at 20:28
  • How are you going to test a function where the only reference is in a local variable in some random place in your code? – Matti Virkkunen Jan 27 '15 at 20:32
  • It is a little bit readable for me. But I think there is some situations that you must declare a function inside the parameters of another function. So, what could I do to maintain good interfaces in this case? – José Augustinho Jan 28 '15 at 02:06
  • I am not convinced it is ever necessary to use an anonymous function as a parameter. Todd Motto has a write up on the topic here that you might find interesting: http://toddmotto.com/avoiding-anonymous-javascript-functions/ – bhspencer Jan 28 '15 at 02:31
-2

Assuming the the DOM node #variable is the root of a "widget" UI element that calls a service and has CSS class 'clicked' added to it when clicked.

The following code demonstrates single-responsibility principle, model-view-controller, dependency injection, meaningful naming, decent documentation, no global variables, data encapsulation, high-cohesion, low coupling etc:

/**
 * 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.  
});
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • Haha! Overkill for changing the class of a single DOM element for sure. But since when has professional development ever been that simple? This code is maintainable and testable. – Ben Aston Jan 27 '15 at 22:09