JavaScript setInterval没有正确绑定到正确的闭包

时间:2013-09-11 03:10:10

标签: javascript node.js coffeescript meteor

问题

大家好,我是JavaScript的新手,我来自面向对象的Python和Java世界,这是我的免责声明。

下面有两个代码块,替代实现,一个在JavaScript中,一个在Coffeescript中。我试图在Meteor.js应用程序中的服务器上运行它们。我遇到的问题是当使用bound-method“this.printSomething”作为我的回调函数调用函数“setInterval”时,一旦执行了回调,它就会丢失范围,导致“this.bar”未定义!任何人都可以向我解释为什么JavaScript或coffescript代码不起作用?

JavaScript实施

function Foo(bar) {
  this.bar = bar;

  this.start = function () {
    setInterval(this.printSomething, 3000);
  }

  this.printSomething = function() {
    console.log(this.bar);
  }
}

f = new Foo(5);
f.start();

Coffeescript Implementation

class foo
    constructor: (bar) ->
        @bar = bar

    start: () ->
        Meteor.setInterval(@printSomething, 3000)

    printSomething: () ->
        console.log @bar

x = new foo 0
x.start()

3 个答案:

答案 0 :(得分:2)

您在setInterval回调中丢失了Foo的上下文。您可以使用Function.bind将上下文设置为类似的内容,以将回调函数引用的上下文设置回Foo实例。

setInterval(this.printSomething.bind(this), 3000);

通过电话

setInterval(this.printSomething, 3000);

回调方法获取全局上下文(如果是租户,例如节点,则为Web或全局的窗口),因此您不会获得属性bar,因为this指的是全局上下文。

<强> Fiddle

或只是

 this.printSomething = function() {
     console.log(bar); //you can access bar here since it is not bound to the instance of Foo
  }

答案 1 :(得分:1)

您还可以尝试创建一个闭包来捕获this。像这样:

var self = this;
this.start = function () {
    setInterval(function(){
       self.printSomething();
    }, 3000);
}

答案 2 :(得分:0)

当您输入一个函数时,您将在javascript中获得一个新范围。您可以从父作用域继承,但this的值会更改。在coffeescript中,你可以使用胖箭头(看起来它将成为ecmascript 6的一部分),它在进入新范围之前基本上保留了对this的引用。

class foo
    constructor: (bar) ->
        @bar = bar

    start: () =>
        Meteor.setInterval(@printSomething, 3000)

    printSomething: () =>
        console.log @bar

x = new foo 0
x.start()

在javascript中处理此类事情的标准方法是在您要引用的位置创建对this的引用,然后在超出范围的调用中使用该引用...

function Foo(bar) {

  // make reference to `this` at the point
  // where you want to use it from
  self = this;

  self.bar = bar;

  self.start = function () {
    setInterval(self.printSomething, 3000);
  }

  self.printSomething = function() {
    console.log(self.bar);
  }
}

f = new Foo(5);
f.start();