Coffescript closure _这个引用在嵌套循环中丢失了

时间:2017-04-09 19:44:21

标签: javascript coffeescript

对于我观察过的CoffeeScript,这是非常有趣的。

TLDR:{

我们知道胖箭头(=>)生成一个闭包,保存对this的引用,@的每个引用都将替换为this的原始值。因此,以下coffeescript代码:

=>
  @sth

将产生以下内容:

(function(_this) {
  return (function() {
    return _this.sth;
  });
})(this);

注意_this.sth

}

但这是我发现的一个案例:

=>
  for a in sth
    for b in @sth
      sth

评估为:

(function(_this) {
  return (function() {
    var a, b, i, len, results;
    results = [];
    for (i = 0, len = sth.length; i < len; i++) {
      a = sth[i];
      results.push((function() {
        var j, len1, ref, results1;
        ref = this.sth;
        results1 = [];
        for (j = 0, len1 = ref.length; j < len1; j++) {
          b = ref[j];
          results1.push(sth);
        }
        return results1;
      }).call(_this));
    }
    return results;
  });
})(this);

这有点长,但问题是内部循环通过this.sth代替_this.sth

内环的确切线是:

ref = this.sth;
results1 = [];
for (j = 0, len1 = ref.length; j < len1; j++) {
  b = ref[j];

这是正常行为还是错误?

1 个答案:

答案 0 :(得分:2)

Look at the inner loop more closely:

results.push((function() {
  var j, len1, ref, results1;
  ref = this.sth;
  // Loop stuff goes here...
}).call(_this));

The inner loop is wrapped in a function (as part of the loop comprehension code) which is evaluated using Function.prototype.call:

The call() method calls a function with a given this value and arguments provided individually.

call is called with _this (the stashed/bound @ from the =>) so this inside that function is actually _this and all is well.

If you suppress the comprehension code by explicitly returning nothing:

=>
  for a in sth
    for b in @sth
      sth
  return

then you'll see the ref = _this.sth that you were originally expecting:

(function(_this) {
  return (function() {
    var a, b, i, j, len, len1, ref;
    for (i = 0, len = sth.length; i < len; i++) {
      a = sth[i];
      ref = _this.sth; # <---------------------------
      for (j = 0, len1 = ref.length; j < len1; j++) {
        b = ref[j];
        sth;
      }
    }
  });
})(this);