SidekickJS's blog

For modern development, Javascript is just something you need to learn

Javascript is a small language. You know its history. The intrinsic smallness of the language can be alarming when you come from a bigger language (Ruby, Java) and you need to learn idioms to do what the bigger language does for you.

This article was prompted by a lovely, completely hyperbolae free article about this today titled ‘For modern development Javascript indeed is a shit language’.

There are three things the author of the piece doesn’t seem to know much about:

  1. how to write JS, as opposed to Ruby
  2. the current version(s) of JS: ES5, and ES3 in old IEs
  3. the next version of JS: ES6

To use any new language effectively you need to learn about it and read a bit of code written by people who know it. It often seems that it’s terribly deficient compared to your language: most of the time though you just don’t know enough about it yet.

JS has callable attributes

if you are not used to message-passing language the whole idea of “some attributes are callable and some are not” seems absolutely legal.

Message passing separates implementation from execution cleanly. Rather than an object having ‘properties’, I send a message to an object which invokes a method. Everything then - property lookups, function calls - go via messages that can be handled differently at run time. I’ll call this ‘run time indirection’.

In Javascript, our objects are just a bag of properties. I can take out a property and try to call it - if it’s a function it’ll work.

Rather than throwing up our hands and wailing, perhaps it’s worth investigating how Javascripters a) achieve run-time indirection b) do the things run-time indirection is used for differently?

Pre-ES5: current defensive practice

So in Ruby we have run-time indirection, in Javascript we have ‘define time’. Our objects are just as dynamic - we don’t have types - but our redefinition of methods must come before the call: we can only put properties in, not intercept property access.

Dynamic getters

ActiveRecord objects in Rails are a good example: Ruby’s message intercept allows us to interpret property read/write dynamically, reflecting on database columns. In Backbone (and Dojo before it), we do that via defining set() and get() methods that similarly let us control property access at run-time.

Mixins

Ruby’s send will work with the ancestors of an object to track down the receiver. In JS, we just do our mixins earlier, via _.extend or similar. If we want to wrap existing methods, that’s easy too: we grab the current value, and make sure it’s called in our new version.

ES5

In the current version of JS we do have a hook for intercepting known property lookup via Object.defineProperty.

var obj = {}
Object.defineProperty(obj,"name",{
  get: function() {
    return "Proteus" + Date.now()
  }
})
obj.name // "Proteus1383048827952"
obj.name // "Proteus1383048829030"

ES6

Proxies will allow run-time indirection; unsurprisingly, as it’s their stated goal.

Objects are unusable for stable keys

[JS] defies the premise that objects can have metadata on them

Objects in Javascript aren’t useable as property keys by default - they’re converted into a string via their toString() method when used as a property. Since we know that, it’s trivial to make their toString() method return an id or hash code.

var store = {}
var bobsStuff = {stuff: "yes"}
var bob = {id:1,name:"bob"}
store[bob] = bobsStuff
Object.keys(store) // ["[object Object]"] - not very useful, not unique

bob.toString = function() { return this.id }
store[bob] = bobsStuff
Object.keys(store) // ["[object Object]","1"] - great, our key system worked

store[bob] // {stuff: "yes" }

More likely though, you’d define an id property and a custom container. This is the approach Backbone and dojo.data etc have taken: for years in Dojo’s case…

ES6

In ES6 we have the Map, which gives us the ability to map objects to values by object identity.

var m = new Map
var a = {}
var b = {}

m.set(a,"a")
m.set(b,"b")

m.get(a) // "a"
m.get(b) // "b"

Additionally it’s a standard API for new Mappy types, so we’ll see lots of custom maps being defined you can use.

Objects are unusable for type systems since an object does not carry any type information.

The built-in option to check an object’s ‘type’ is instanceof, which looks at the function an object was created with. You’ll probably want to use your own keys though, as if you wrap the ConstructorFunction below to sub-class etc it won’t work.

a instanceof ConstructorFunction

If your app actually needs introspection, it’s really very easy: just add your own keys to your types.

function TypeA() {
}
TypeA.prototype.type = "TypeA"


function somethingThatUsesType(a) {
  switch(a.type) {
  }
}

I don’t see this kind of code very often, because, for the same reason as Ruby, type sniffing is normally just for debugging. If you’re using it for something in your code it’s better to stick with duck-typing as it’s under your control, not the language’s, and it’s easier to extend and test.

Null everywhere

Attempting to access a property of a JS object that’s not defined gives you… undefined. If it’s important to your program that you know about this: write some code.

For the last few years I’ve used a constants module. It’s a function that takes an object, and returns a constant function you can lookup constants on:

function constants(h) {
  return function(k) {
    if(h[k] == null) throw new Error("Undefined constant: " + k);
    return h[k]
  }
}
var myConstants = constants({A: "hello"})
myConstants("A") // hello
myConstants("B") // Error!

If you’re annoyed by how, when using objects as hashes, you get undefined on a missing key, it’s probably time to define a custom container!

function MyMap() {
}
MyMap.prototype.get = function(k) {
  if(!this[k]) throw new Error("Missing key: " + k)
  // ...
}

ES6

In ES6, and Node, you can use the const keyword. Woohoo.

Callback hell

‘Callback hell’ isn’t a problem for people who’ve written a lot of JS. They define functions with names, rather than heaps of anonymous functions, and use these to de-inline them and expose them to their unit tests. They use bind(). The may use helper libraries, or tools like streams or promises. In the browser it really is trivial in most cases - in Node, you have more async so more opportunity to get into a mess. But it doesn’t need to be hell: I’ve spoken about this, as have a lot of other people.

ES6

ES6 is bringing [generators] which very much change the game. This has been written and spoken about a lot.

Terrible exception handling

JS has one of the best debuggers out there: the Webkit inspector. It’s usable with Node.js too.

If you attempt to call a property that’s not a function: you get a line-number in the error. Go to that line, and pop in a breakpoint. Debug it!

Write into a language

If you’re frustrated with how JS (or another language) works compared to your pet language, it’s worth realising two things:

I find the similiarties between dynamic languages outweigh their differences, and it’s very unlikely you could find a feature in one you couldn’t rebuild trivially in another. More restrictive languages like Java or Haskell have sharper edges when they’re not working as you’d like: dynamic languages you can simply remould.