Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deconstructing a list of parameters in class constructor - Coffeescript

Tags:

coffeescript

If I have a class that I pass a number of parameters to:

class Foo

  constructor: (parameters) ->

  @bar = parameters.bar
  @moo = parameters.moo

The class is created like so:

foo = new Foo(bar: 2, moo: 8)

My question is what is the most elegant way to detect in the constructor if the variables being passed exist, and if not to set a default. The way I would do it in javascript would be:

this.bar = ( parameters.bar !== undefined ) ? parameters.bar : 10;

where 10 is the default.

Thanks for your help :)

Good answers - Just to summerize the best:

Inorder to detect if a parameter exists and define a default if it doesn't, in javascript is:

this.bar = ( parameters.bar !== undefined ) ? parameters.bar : 10;

and in coffescript is:

@bar = parameters.bar ? 10

So elegant and compact!

like image 389
Jamie Fearon Avatar asked Jul 15 '12 15:07

Jamie Fearon


4 Answers

You can use the existential operator:

class Foo
  constructor: (options = {}) ->
    @bar = options.bar ? 10
    @moo = options.moo ? 20

That constructor compiles to:

// Compiled JS.
function Foo(options) {
  var _ref, _ref1;
  if (options == null) {
    options = {};
  }
  this.bar = (_ref = options.bar) != null ? _ref : 10;
  this.moo = (_ref1 = options.moo) != null ? _ref1 : 20;
}

An equivalent alternative is to destruct the options object immediately (therefore avoiding an unnecessary name for it) and then setting the default values in case those options were not passed:

class Foo
  constructor: ({@bar, @moo} = {}) ->
    @bar ?= 10
    @moo ?= 20

As passing an object with options is a common pattern in JS code, some libraries have useful utility functions that can help with this. Underscore, for example, provides _.defaults, which i think results in quite readable code:

class Foo
  constructor: ({@bar, @moo} = {}) ->
    _.defaults @, bar: 10, moo: 20

If you are not using Underscore, there is also $.extend (who doesn't use jQuery anyway?):

class Foo
  defaults = bar: 10, moo: 20
  constructor: (options = {}) ->
    {@bar, @moo} = $.extend {}, defaults, options

An alternative is to extend the Foo object directly if you trust that the only options that will be passed are the valid ones (this results in quite minimal JS compared to the other ones):

class Foo
  defaults = bar: 10, moo: 20
  constructor: (options = {}) ->
    $.extend @, defaults, options

And a final alternative is to have the default values in the Foo.prototype and only set them as own properties if they come in the options parameter:

class Foo
  bar: 10
  moo: 20
  constructor: ({bar, moo} = {}) ->
    @bar = bar if bar?
    @moo = moo if moo?

This prevents all the instances of Foo from having separate properties of their own and instead shares the same properties between instances when they use the default values, the same that is usually done with methods. You can also do @bar = bar if @bar isnt bar to assign those properties only when the parameter value differs from the default value.

As you can see, there are quite a lot of ways to do this. None of them is perfect, they all have their pros and cons, so try to choose the one that better suits your needs/taste/whatever =D

like image 151
epidemian Avatar answered Oct 11 '22 08:10

epidemian


You could do this using the ?= operator:

class Foo
  constructor: (parameters) ->
   @bar = parameters.bar
   @bar ?= 10
   @moo = parameters.moo
   @moo ?= 20

If you didn't mind passing in positional arguments rather than a hash, you could also do this very elegantly using default parameter values:

class Foo
  constructor: (@bar = 10, @moo = 20) ->

f = new Foo
like image 44
obmarg Avatar answered Oct 11 '22 07:10

obmarg


You could define an object of defaults and do something like this..

 (parameters = {}) ->
   this[key] = parameters[key] ? defaults[key] for key, value of defaults

But you really don't have to go through all of that. The easiest way to make default values is just to use prototypal inheritance...

class Foo
  bar: 10
  moo: 10
  constructor: (parameters = {}) ->
    this[key] = value for own key, value of parameters

Assuming the above:

a = new Foo();
a.bar # => 10
b = new Foo(bar: 50)
b.bar # => 50

With the exception of the constructor every property you define with a : will become a property on the object's prototype.

The class definition translates to this JavaScript:

Foo = (function() {      
  Foo.prototype.bar = 10;      
  Foo.prototype.moo = 10;
  //etcetc
  return Foo;      
})();

Play with it. Hope that helps.

like image 2
KGZM Avatar answered Oct 11 '22 08:10

KGZM


There is a more simpler way:

class Foo
  constructor: (parameters = {}) ->
    {
      @bar = 10
      @moo = 20
    } = parameters
like image 1
Inanc Gumus Avatar answered Oct 11 '22 07:10

Inanc Gumus