JavaScript中的对象变量

时间:2012-02-03 19:03:55

标签: javascript coffeescript settimeout

我有以下代码:

WaitModel = ->
  timesDelayed = 0
  maxDelay = 10

  return this

WaitModel.prototype =
  wait: ->
    console.log 'waiting'

    console.log this.timesDelayed, this.maxDelay

    if this.timesDelayed >= this.maxDelay
      console.log 'running'
      this.timesDelayed = 0

    else
      this.timesDelayed++
      setTimeout this.wait, 1000

    return

new WaitModel().wait()

我认为这应该产生如下输出:

waiting
0 10
waiting
1 10
...

但它产生的输出如下:

waiting
0 10
waiting
undefined undefined

我在哪里取消this.timesDelayedthis.maxDelay?我误解了在这里制作物品的方法吗?

4 个答案:

答案 0 :(得分:5)

jfriend00告诉你你做错了什么。在CoffeeScript中解决此问题的惯用方法是使用=> (fat arrow)来定义wait方法。我还de-JavaScripted你的CoffeeScript使其更具惯用性:

class WaitModel
    constructor: (@timesDelayed = 0, @maxDelay = 10) ->
    wait: =>
        console.log 'waiting'
        console.log @timesDelayed, @maxDelay
        if @timesDelayed >= @maxDelay
            console.log 'running'
            @timesDelayed = 0
        else
            @timesDelayed++
            setTimeout @wait, 1000

new WaitModel().wait()

演示:http://jsfiddle.net/ambiguous/aYFmL/1/

答案 1 :(得分:2)

setTimeout不会保留this的值。您可以在调用this之前将self保存到名为setTimeout的本地变量中,或者您想要调用它的任何内容并以此方式引用它。

我不知道coffeescript,但在javascript中,它会是这样的:

  wait: function() {
    var self = this;
    if (this.timesDelayed >= this.maxDelay) {
      console.log('running');
      this.timesDelayed = 0;
    } else {
      this.timesDelayed++;
      setTimeout(function() {self.wait()}, 1000);
    }
  }

答案 2 :(得分:2)

看起来CS有自动this绑定。

setTimeout => @wait(), 
1000

使用胖箭头,您可以将匿名函数中的this视为其外部值。

答案 3 :(得分:1)

编辑:这比我更好地解释了:
http://bonsaiden.github.com/JavaScript-Garden/#function.this

您的示例在我的机器上提供此输出:

waiting
undefined undefined
waiting
undefined undefined

首先,构造函数不应返回任何内容,因此请删除return this行。原因是因为new创建了新对象,构造函数只是填充它。

在构造函数中,this绑定到您正在创建的对象,而普通变量具有常规的局部范围。这意味着你必须在构造函数中说this.timeDelayed等等 - 否则你的两个变量将在构造函数退出时超出范围,并且不会在以后显示为字段或其他任何内容。

通过这些更改,我的输出与您的输出匹配:

waiting
0 10
waiting
undefined undefined

现在谈谈你的问题。

setTimeout功能排队等待运行。函数只是函数 - 它们不会携带关于它们适用于哪个对象的额外信息,即使您将它们视为方法(如this.wait)。这与this非常相关,因为在Javascript中,this关键字asways具有动态范围,这意味着调用者可以通过类似

的内容来选择它的含义
someobject.foo()

或其他什么。在对foo的调用中,this绑定到someobject。如果我们把它拿出来:

var foo = someobject.foo
foo()

foo内,this将是未定义的。为了使其有效,我们不得不说:

foo.call(someobject)

这很重要,因为setTimeout在超时函数运行时没有设置this的值。相反,请替换您的电话:

setTimeout this.wait, 100

有类似的东西:

  self = this
  setTimeout (-> self.wait()),  100

这是有效的,因为该函数不再调用this,而是调用self这是一个普通变量。

Coffeescript包含一些技巧和快捷方式。首先,您可以使用胖箭头语法避免在闭包中捕获self,该语法会自动将函数中的this绑定到当前对象,无论它在何处被引用:

其次,您可以使用@作为this.的快捷方式来编写实例变量/方法。因此,我的最终代码示例如下:

WaitModel = ->
  @timesDelayed = 0
  @maxDelay = 10


WaitModel.prototype =
  wait: ->
    console.log 'waiting'

    console.log @timesDelayed, @maxDelay

    if @timesDelayed >= @maxDelay
      console.log 'running'
      @timesDelayed = 0

    else
      @timesDelayed++
      setTimeout (=> @wait()),  100

    return

new WaitModel().wait()