2

I want to use the 'class' syntax to create a class, and when it news an instance, the instance can be directly used as a Function.

class Foo
  constructor: (@bar) ->
  baz: ->
    console.log 'baz'
  ...

f = new Foo 'bar'
f() # runs () -> console.log @bar
f.baz() # 'baz'

Here is a JavaScript solution but I cannot reproduce it in CoffeeScript using class syntax.

Community
  • 1
  • 1
Rix
  • 1,688
  • 13
  • 20

4 Answers4

3

I don't think you can write a Coffeescript class which compiles to that Javascript (or something close). The Coffeescript class insists on 2 things:

It ends the class body with return AwesomeObject;

If I put a return bar in the class body, it objects with error: Class bodies cannot contain pure statements.

The linked Javascript model is:

var AwesomeObject = (function() {
    var AwesomeObject = function() {...};
    ...
    return function() {
      var o = new AwesomeObject();
      ...};
})();

It defines an AwesomeObject constructor internally, but returns a different function. This is clearer if the internal name is changed to AwesomeObject1. It functions the same, but there is no way of accessing AwesomeObject1 directly.

Also AwesomeObject() and new AwesomeObject() return the same thing.

{ [Function]
  whatstuff: 'really awesome',
  doStuff: [Function] }

The compiled Coffeescript (for class AwesomeObject...) instead is:

AwesomeObject = (function() {
  function AwesomeObject() {...}
  ...
  return AwesomeObject;
})();

P.S. https://github.com/jashkenas/coffee-script/issues/861 Coffeescript issue discussion on new Foo() versus Foo() syntax. Consensus seems to be that while new-less calls are allowed in Javascript for objects like Date, it isn't encouraged for user defined classes. This is interesting, though not really relevant to the question here.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • The linked pattern is more complex that it is necessary. You can easily write a coffeescript class that does similar things with the same results, see my answer. – Bergi Jan 08 '14 at 22:30
0

How about something like this:

class Foo
  constructor: (@bar) ->
    return => console.log @bar

f = new Foo 'bar'
f()
phenomnomnominal
  • 5,427
  • 1
  • 30
  • 48
  • This is what I've tried. The constructor returns a function, but it's not a formal constructor works. The returned function contains no methods in the class definition but a bare function. I edit my question and add a method in the class, please try that again. – Rix Jan 08 '14 at 11:07
0

This is what you're looking for:

class Foo
  constructor: (@bar) ->
    f = -> console.log bar
    for v, k of @
      f[v] = k
    return f
  baz: ->
    console.log 'baz'

f = new Foo 'bar'
f() # runs () -> console.log @bar
f.baz() # 'baz'

Notice that this solution does not return a callable object which inherits from Foo.prototype (which is impossible), but that it does return a Function object in which some properties are mixed in.

A better pattern would be not to return a callable function object, but to return a standard object instance that has a call or execute method.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

Here's an variation on Bergi's answer:

class Foo
  constructor: (@bar) ->
    foo = () =>
      console.log @bar
    foo.baz = ->
      console.log 'baz'
    return foo
f = new Foo 'bar'
f()
f.baz()

This may just be using class as a wrapper, much as do() does. f is { [Function] baz: [Function] }. Also Foo 'bar' (without the new), produces the same thing.

hpaulj
  • 221,503
  • 14
  • 230
  • 353