Question

I'm doing a series of tests with classes and CoffeeScript/JavaScript. See the following code:

class Example

    someFunction = ->
        alert @getText()

    constructor: ->
        @text = 'Hello world! ;)'
        someFunction()

    getText: ->
        @text


### Instance ###
example = new Example

It's just an example, when compiling I get the error:

Uncaught TypeError: Object [object global] has no method 'getText'

You know how I can solve this problem? http://jsfiddle.net/P4Xdz/

Was it helpful?

Solution

If you really want to do this sort of thing, you'll have to manually provide the correct @ (AKA this) by hand with call or apply:

constructor: ->
    @text = 'Hello world! ;)'
    someFunction.call(@)

Demo: http://jsfiddle.net/ambiguous/6KZrs/

The problem is that someFunction is not a method of any kind, it is just a simple function. If you need it to behave like a method then you have to "methodize" it manually by providing the desired @ when you call it. This (and epidemian) suggests an alternative approach: explicitly pass the object as an argument:

someFunction = (ex) ->
    console.log ex.getText()

constructor: ->
    @text = 'Hello world! ;)'
    someFunction(@)

Demo: http://jsfiddle.net/ambiguous/hccDr/

Keep in mind that there is no public or private in JavaScript and so there is no public or private in CoffeeScript. You can sort of fake it but fakery has holes and tends to require more chicanery (such as manually supplying the @ with call) to make it work. If you look at the JavaScript version of your code, you'll see that someFunction is just this:

var someFunction = function() { ... };

Just a function in a variable that is scoped to the class function, nothing more. Also keep in mind that since someFunction is local to the Example class function, it won't be visible in any way to subclasses.

OTHER TIPS

This may be obvious but... coffescript isn't able to do anything conceptually you couldn't already do in javascript. Right now your someFunction definition is a local variable, and is not declared as a property on the instance (unlike getText).

When you use '@' within someFunction I'm assuming you expect it to refer to the instance of Example, which would be convenient in your case, however someFunction isn't defined on example.

If you used the => notation it still wouldn't bind it to the instance (it would refer to the class function). Now this may seem inconvenient, or an odd design choice, but its actually consistent. Once again, someFunction isn't on the instance, its defined as a local variable within the Example class function.

If you use ->, '@' refers to javascripts 'this' for that function (which is the local variable and obviously doesn't contain getText). If you use => it refers to javascripts 'this' at the time of the definition, which is at this point the Example class function. The Example instance, which is what you want to refer to, isn't even created yet (though you wish to refer to it).

The reason @ refers to the example instance within functions like getText is because javascripts this keyword refers to the object your defined on. Coffeescript is really no different, other then providing you a convenient syntax of referring to the 'this' at the time of a functions definition.

TLDR:

You can't really accomplish what your looking for, and your probably going to have to give up on the idea of a 'private' function on an instance The best I can see you doing is what you've already described in your comments above Example.prototype.getText() Because the two ways you'll be able to refer to this method are through the instance and the Example.prototype (which the function is defined on). Since your method isn't defined on the instance, then you cannot use 'this'. However if you call the method from the prototype, your getText function will fail anyway.

getText: ->
        @text

the @text refers to what getText is defined on, and in this context its the prototype (not the instance). And text is undefined on the prototype.

If you want to have this method function the way you expect your probably going to have to make it not 'private'. Javascript/Coffeescript do not have access modifiers like public and private, a private method is really a function defined in a particular scope. In this case that scope doesn't have access to what you want, and the this keyword doesn't refer to what you need.

  1. You're using someFunction = rather than someFunction:. This won't do what you expect.
  2. You're calling someFunction, when in fact you probably want to be calling @someFunction.

In the way you've written your example, 'someFunction' is an anonymous function that has not been bound to anything. So, 'someFunction's 'this' is bound to the global object, which explains your error. You can fix it by using fat arrows to define 'someFunction' and placing 'someFunction' in Example's constructor. This will cause 'someFunction' to be bound to your Example instance. If you were to bind 'someFunction' by using a fat arrow, but leave it outside the constructor as you originally had, 'someFunction' would be bound to the Example constructor, causing 'someFunction' to call a non existent static method --getText-- of Example.

Here's how to get rid of your error:

class Example
    constructor: ->
        someFunction = =>
            alert @getText()

        @text = 'Hello world! ;)'
        someFunction()

    getText: =>
        @text


### Instance ###
example = new Example
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top