【问题标题】:Why must my coffeescript method belong to the class?为什么我的咖啡脚本方法必须属于该类?
【发布时间】:2012-05-04 08:04:58
【问题描述】:

我来自 C#/Java 背景,使用基于类的 OO 系统,但我还没有获得 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)

感谢您花时间回答。

【问题讨论】:

    标签: coffeescript


    【解决方案1】:

    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) 在firstOrNickThenLast 方法中将是@prefToRenderMap。但是,当然,@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

    【讨论】:

    • 这很有意义,谢谢。我现在了解范围发生了什么。
    猜你喜欢
    • 2013-01-09
    • 1970-01-01
    • 2013-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多