Last Updated: December 26, 2018
·
6.15K
· jiewmeng

Organizing JavaScript (or CoffeeScript, Notes from SuperheroJS.com)

Notes from SuperheroJS/Organizing Your Code

Outline

  • Designing Better JavaScript API
    • Fluent Interfaces: method chaining
    • Handling Arguments (Reducing repetition with maps, being flexible with types)
  • Async JS
    • Callbacks
    • Events
    • Control Flow Libraries (AsyncJS)
    • Promises

Designing Better JavaScript API

"Better APIs developers will love using"

Reference from: Designing Better JavaScript APIs (Smashing Magazine)

Fluent Interface

Method Chaining

  • Heavily used in jQuery

    $elem = $("#elemId")
    $elem.css("background", "orange")
    $elem.on("click", doSomething)
    $elem.on("keypress", doAnother)
    
    # vs
    $("#elemId").css("background", "orange")
                .on("click", doSomething)
                .on("keypress", doAnother)
  • Another possibility (imaginary SQL-like fluent interface)

    query = select(column1, column2)
                .from(tableName)
                .where("column", EQUALS, "x")
    // since this is not a string, u can modify parts out of order
    query.join(table2)
    ...
    // finally get the string
    query.toString()
    • [An example on JSFiddle][fidd le-fluent-sql]
  • Advantages

    • Better readability
    • Less repetetion/accessing variable
    • In some cases, enables more flexibility (like in the fludent SQL API example)

Command Query Separation (CQS)

  • Commands changes state (eg. setters)
  • Query retrieve values (eg. getters)
  • But fluent interfaces returns a self object ...
  • So CQS is broken deliberately

    // getter
    $elem.css("background")
    // setter
    $elem.css("background", "red")

Going fluent by extending prototype

// more fluent
Array::map = someFunction
Array::reduce = anotherFunction
// - example usage
arr.map(doSomething)

// less fluent
_.map = someFunction
_.reduce = anotherFunction
// - example usage
_.map(arr, doSomething)

Handling Arguments

Aim to eliminate repetition (eg. use a map)

$elem.css("background", "red")
     .css("color", "white")
     .css("font-weight", "bold")
// vs
$elem.css({
    "background": "red",
    "color": "white",
    "font-weight": "bold"
})

Being flexible with types

Accept various input types as applicable

myDateObj.until(1370759029735)
myDateObj.until("2013-02-12")
myDateObj.until(new Date(2013, 06, 10))

Named & optional arguments using maps

something = (x1, x2, x3, x4, ...) ->
    if x1 is undefined then x1 = someDefault 
    if x2 is undefined then x2 = someDefault
    ...

// vs
something = (opts) ->
    defaults = {
        prop1 = "x",
        prop2 = "y",
        prop3 = "z"
    }
    options = $.extend({}, defaults, opts)

Asynchronous JavaScript

  • Callbacks
  • Events
  • Control Flow libraries
  • Promises

Callbacks

foo = (doneCallback, errorCallback) ->
// do something

if success 
    if _.isFunction(doneCallback) then doneCallback(value)
else
    if _.isFunction(errorCallback) then errorCallback(err)

Advantages

  • Well known pattern
  • Very easy to implement

Disadvantages

  • Highly nested callbacks (pyramid of doom): hard to read and test
  • Only 1 callback per event

Events

Commonly use in GUI applications. Widely used in jQuery and BackboneJS

vent.trigger("someEvent")
// objects can listen to this and handle it
vent.on("someEvent", doSomething)

Advantages

  • Well understood pattern
  • Many listeners to one object

Disadvantages

  • Slightly harder to implement from scratch. But with libraries like jQuery/Backbone, its very easy

Going Asynchronous

Using a control flow library (AsyncJS)

Advantages

  • Easier to understand - can visualize flow. top to bottom

Disadvantages

  • Functions may need to be adapted for use with AsyncJS (eg. adding done callbacks)

Use deferreds/promises

aLongTask = ->
    deferred = $.Deferred()
    (doSomething ->
        // a long task
        if success 
            deferred.resolve(value)
        else 
            deferred.reject(err)
            )()
    return deferred

aLongTask().done(handleSucess)
           .fail(handleFailure)
  • jQuery has Deferred
  • Many other libraries providing such functionality
  • Useful to know
    • AsyncJS: what if u want to wait for multiple async functions to finish. Or even only some?
    • Or even ParallelJS, true multi-threading using HTML5 Web Workers

Advantages

  • Most powerful
  • Can be aggregated, passes around, add listeners when already resolved

Disadvantages

  • Least understood?
  • Difficult to track with lots of aggregated promises with listeners added along the way?

3 Responses
Add your response

+1 for method chaining.

over 1 year ago ·

I like it! But it should really be called:
"Organizing CoffeeScript and JavaScript (Notes from SuperheroJS.com)"
because most of the examples (except jQuery) are CoffeeScript code rather than plain JavaScript code.

over 1 year ago ·

@josher19, haha, its because this is an outline of a presentation I am doing for work as part of internal training program. And where I work now, we use CoffeeScript

over 1 year ago ·