переменные объекта в JavaScript - PullRequest
1 голос
/ 03 февраля 2012

У меня есть следующий код:

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.timesDelayed и this.maxDelay?Я неправильно понимаю способ создания объектов здесь?

Ответы [ 4 ]

5 голосов
/ 03 февраля 2012

jfriend00 рассказал вам, что вы делаете неправильно.Идиоматический способ решения этой проблемы в CoffeeScript заключается в использовании => (жирная стрелка) для определения вашего wait метода.Я также де-Java расшифровал ваш 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/

2 голосов
/ 03 февраля 2012

Похоже, что CS имеет автоматическое связывание this.

setTimeout => @wait(), 
1000

С помощью жирной стрелки вы можете считать this в анонимной функции своим внешним значением.

2 голосов
/ 03 февраля 2012

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);
    }
  }
1 голос
/ 03 февраля 2012

Редактировать: это объясняет это лучше, чем я:
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 всегда имеет динамическую область действия, что означает, что вызывающий абонент получает возможность выбирать то, что он означает, через что-то вроде

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()
...