Редактировать: это объясняет это лучше, чем я:
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()