0

我来自使用基于类的 OO 系统的 C#/Java 背景,但我还没有获得 JavaScript/CoffeeScript 原型 OO 系统。我在下面编写了一个 CoffeeScript 类,它允许我根据系统端的偏好显示联系人的姓名。我只能通过使该joinNonEmpty(stringList, joinText)方法属于原型并以我在 Java/C# 领域中调用静态方法的方式调用它来使该类工作。

  1. 有没有办法可以使用这个方法调用this.joinNonEmpty(...)
  2. 你能解释一下为什么我可以firstLastRender, lastFirstRender and firstOrNickThenLast用这个来引用构造函数中的方法吗?joinNonEmpty但是在调用助手时这些方法不起作用?
  3. 这与我如何通过首选项映射找到适当的方法有关吗?
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)

感谢您花时间回答。

4

1 回答 1

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) 将@prefToRenderMapfirstOrNickThenLast方法内。但是,当然,@prefToRenderMap没有您尝试调用的任何方法,因此您会遇到各种错误。

一种解决方案是使用粗箭头 ( =>)来定义方法:

粗箭头=>既可用于定义函数,也可用于将其绑定到 的当前值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/


ClassName.class_method()顺便说一句,您可以使用constructor属性代替,而不是在实例方法中说@constructor.class_method():此外,您通常在 CoffeeScript 中使用@method()or@property而不是this.method()and 。this.property

于 2012-04-23T18:56:54.637 回答