为什么我的coffeescript方法必须属于班级?

时间:2012-04-23 17:20:26

标签: coffeescript

我来自C#/ Java背景,它使用基于类的OO系统,但我还没有获得JavaScript / CoffeeScript原型OO系统。我在下面编写了一个CoffeeScript类,它允许我根据系统端首选项显示联系人的名称。我只能让joinNonEmpty(stringList, joinText)方法属于原型并按照我在Java / C#land中调用静态方法的方式来调用它。

  1. 有没有办法可以使用this.joinNonEmpty(...)调用此方法?
  2. 您能否阐明为什么我可以在构造函数中引用firstLastRender, lastFirstRender and firstOrNickThenLast方法。但是在调用joinNonEmpty帮助程序时,它不适用于那些方法?
  3. 这是否与我如何通过偏好地图找到合适的方法有关?
  4. prefs = displayNameFormat: "FirstOrNickThenLast"
    
    class DisplayNameRenderer
    
        constructor: ->
            @prefToRenderMap =
                FirstLast: this.firstLastRender
                LastFirst: this.lastFirstRender
                FirstOrNickThenLast: this.firstOrNickThenLast
    
        # Why does this method have to be static (a class method)?
        @joinNonEmpty: (stringList, joinText) ->
            nonEmptyStrings = []
            for s in stringList
                nonEmptyStrings.push(s) if s isnt null and s isnt ""
            nonEmptyStrings.join(joinText)
    
        firstLastRender: (contact) ->
            # TypeError: Object expected.
            joinNonEmpty([contact.firstName, contact.lastName], ' ')
    
        lastFirstRender: (contact) ->
            # TypeError: Object doesn't support this method or property
            this.joinNonEmpty([contact.lastName, contact.firstName], ', ')
    
        firstOrNickThenLast: (contact) ->
            # Works correctly.
            DisplayNameRenderer.joinNonEmpty([(if contact.nickname isnt null and contact.nickname isnt "" then contact.nickname else contact.firstName), contact.lastName], ' ')
    
        render: (contact) ->
            @prefToRenderMap[prefs.displayNameFormat](contact)
    
    contact = firstName: "Jonathan", nickname: "Jonny", lastName: "Appleseed"
    
    dnr = new DisplayNameRenderer()
    
     # => "Jonny Appleseed"
    console.log dnr.render(contact)
    

    感谢您抽出宝贵时间回答。

1 个答案:

答案 0 :(得分:4)

调用函数时确定

this(AKA @)(如下所示)。所以当你这样做时:

@prefToRenderMap =
    FirstLast: this.firstLastRender
    LastFirst: this.lastFirstRender
    FirstOrNickThenLast: this.firstOrNickThenLast

您在@prefToRenderMap实例变量中存储对三个函数的未绑定引用,而@prefToRenderMap本身就是一个对象。然后,您尝试调用DisplayNameRenderer实例中的方法,如下所示:

@prefToRenderMap[prefs.displayNameFormat](contact)

并且一切都崩溃了,因为这些方法是在错误的上下文中调用的,而@并不是他们期望的那样。如果prefs'FirstOrNickThenLast',那么您有效地执行此操作:

@prefToRenderMap.FirstOrNickThenLast(contact)

@(AKA this)将在@prefToRenderMap方法中firstOrNickThenLast。但是,当然,@prefToRenderMap没有您尝试调用的任何方法,因此您会遇到各种错误。

一种解决方案是使用fat arrow (=>)来定义方法:

  

胖箭=>既可以用来定义一个函数,也可以当场将它绑定到this的当前值。

所以你可以这样做:

joinNonEmpty: (stringList, joinText) ->
    #...

firstLastRender: (contact) =>
    @joinNonEmpty([contact.firstName, contact.lastName], ' ')

一切都会成功。这是一个精简版演示,它还会向您显示this问题:

  

http://jsfiddle.net/ambiguous/RAPJw/1/

您也可以通过名称引用方法来避免此问题。给定字符串中的方法名称m = 'some_method',您可以在JavaScript和CoffeeScript中调用此方法,如o[m](),结果与您说o.some_method()时的结果相同。更好的结构看起来更像这样:

class DisplayNameRenderer
    constructor: ->
        @prefToRenderMap =
            FirstOrNickThenLast: 'firstOrNickThenLast'

    joinNonEmpty: (stringList, joinText) ->
        #...

    firstOrNickThenLast: (contact) ->
        @joinNonEmpty([(if contact.nickname isnt null and contact.nickname isnt "" then contact.nickname else contact.firstName), contact.lastName], ' ')

    render: (contact) ->
        @[@prefToRenderMap['FirstOrNickThenLast']](contact)

请注意@prefToRenderMap结构的更改及其在render中的使用方式。并演示了这种方法:http://jsfiddle.net/ambiguous/DFYwu/


另外,您可以使用constructor属性代替ClassName.class_method(),而不是在实例方法中使用@constructor.class_method()。另外,您通常会在CoffeeScript中说@method()@property而不是this.method()this.property