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

问题

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

下面有两个代码块,替代实现,一个是JavaScript,一个是Coffeescript。 我想在Meteor.js应用程序的服务器上运行它们。 我遇到的问题是当使用绑定方法“this.printSomething”作为我的callback调用函数“setInterval”时,一旦执行该callback,它会丢失范围与实例导致“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(); 

咖啡的实施

 class foo constructor: (bar) -> @bar = bar start: () -> Meteor.setInterval(@printSomething, 3000) printSomething: () -> console.log @bar x = new foo 0 x.start() 

您在setIntervalcallback中丢失了Foo的上下文。 您可以使用Function.bind将上下文设置为像这样将callback函数引用的上下文设置回Foo实例。

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

随着电话

 setInterval(this.printSomething, 3000); 

callback方法获取全局上下文(如果是租户,比如节点,则为web或全局窗口),所以你不会在那里获得属性bar ,因为this是指全局上下文。

小提琴

要不就

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

你也可以尝试创build一个闭包来捕获this 。 喜欢这个:

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

当你input一个函数,你会得到一个新的范围在JavaScript中。 您可以从父范围inheritance,但其值会更改。 在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中处理这种事情的标准方法是在你想要引用的地方创build一个引用,然后在你的作用域调用中使用引用。

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