【问题标题】:Intended use of case `this` in anonymous function?在匿名函数中打算使用 case `this`?
【发布时间】:2020-07-30 05:13:37
【问题描述】:

问题

我今天刚开始学习JS/ES7,想看看我是否理解this正确。

根据下面的两个代码sn-ps,我猜this的目的是可以选择是否要继承父函数的变量?

如果这是正确的,那么在第一个示例中是否存在在 setInterval() 中使用 this 的合法用例?

var countup = {
    counter: 0,

    start:function(){
        var countup = this;
        setInterval(function(){
            countup.counter++;
        }, 1000);
    }
};

countup.start();

var countup = {
    counter: 0,

    start:function(){
        setInterval( () => {
            this.counter++;
        }, 1000);
    }
};

countup.start();

更新

问我上述问题的另一种方法是:

任何人都可以在setInterval() 中想出一个很好的this 用例吗?

var countup = {
    counter: 0,

    start:function(){
        var countup = this;
        setInterval(function(){
            // insert good use case of `this` inside this function?
            countup.counter++;
        }, 1000);
    }
};

countup.start();

【问题讨论】:

  • 不适用于setInterval,不,因为您知道this === window 在那里,但在许多其他情况下,回调可能想要使用this 参数。 jQuery 就是一个很好的例子。

标签: javascript ecmascript-2016


【解决方案1】:

在第一个示例中,您在定义 startup 函数时分配了 this 值,并将其分配给变量 countup,此时 this 被存储在countup 变量中,该变量将被setInterval 函数中的回调形成的闭包引用。

如果您不这样做,那么在 function 中,this 将取自该函数的执行上下文。这意味着当 setInterval 运行时,this 将成为全局对象,因为传递给 setInterval 的回调在全局上下文中执行:

var countup = {
  counter: 0,

  start: function() {
    setInterval(function() {
      //this is not the lexical this, but the context of execution
      //since this.counter is not defined, this would print NaN (undefined + 1 == NaN)
      this.counter++;
      console.log("this == window::true ", this == window, "this.counter::NaN:: ", this.counter);
    }, 1000);
  }
};

countup.start();

在第二个示例中,提供给setInterval箭头函数 () => {} 始终从词法范围捕获this,因此this 始终是绑定的。所以这可以正常工作,无需手动从 lexical 范围中捕获this。但请注意,两者都是匿名函数,即缺少 name 属性,这种行为与函数的匿名性质无关。它们是这样定义的,一个是使用function语法定义的,另一个是箭头函数语法。

来自 Mozilla docs

箭头函数没有自己的 this。的这个值 使用封闭词法范围;箭头功能遵循正常 变量查找规则。所以在搜索这个不是 存在于当前范围内,箭头函数最终会找到 this 从它的封闭范围。

【讨论】:

  • 因为第一个示例允许两个不同的this。你能想出一个在setInterval() 中使用this 的用例吗?我已经更新了 OP。
  • @SandraSchlichting 在第一个示例中,您已经给出了使用 this 的一个很好的用例。那里有一个函数可以更新同一对象的属性。为此,您需要 this 引用,该引用具有通过将词法 this 分配给 countup 变量而获得的要更新的属性。现在,当您将回调传递给setInterval 时,回调在start 函数的范围内形成一个闭包,并捕获countup 变量,该变量只不过是词法this
  • @BenAston 是的,它没有,我从来没有说过,OP 手动将this定义函数时分配给变量countup 。这样当定义回调时,结果闭包就可以访问该countup 变量。
【解决方案2】:

我不认为“继承”是正确的词。

每次调用函数时,都会出现a hidden this argument(“this 值”,有时称为“接收者”)。 this 值取决于函数的调用方式。

this 被添加到语言中以启用类似 Java 的面向对象的语法。例如:

class Point {
  constructor(x, y) {
   this.x = x
   this.y = y
  }
  getX() {
    return this.x
  }
}

...或使用构造函数:

function Point(x, y) {
  this._x = x
  this._y = y
}

Point.prototype.getX = function() {
  return this._x
}

因此,this 使程序员可以方便地将调用函数的对象作为方法引用:

const a = new Point(1, 2);
console.log(a.getX());

...但是,不幸的是,JavaScript 中的 this 存在额外的复杂性。

复杂性源于以不同于对象方法的方式调用函数的能力(有关详细信息,请参阅MDN)。例如:

function  myFunction() {
  console.log(this) // `undefined` in strict mode, `window` in non-strict
}
myFunction() // `myFunction` is not called as a method!

您的第一个示例提供了一个以方法以外的方式调用函数的示例:在作为回调提供给setInterval 的函数中,this 值应该是什么?碰巧的是,提供给setInterval 的函数中this 的默认值是全局对象(因此在本例中为window)。

...这解释了我们在您的代码中看到的惯用语:将this 的值分配给变量that(我在您的示例中将其重命名为“countup”)以使回调结束。通过这种方式,我们可以在回调内部中引用外部词法环境的 this 值,无论何时调用它。

var countup = {
    counter: 0,
    start: function() {
        var that = this
        setInterval(function() {
            that.counter++
        }, 1000)
    }
}

您的第二个示例是在做同样的事情,但使用的是更现代的 JavaScript。箭头函数总是使用声明它们的词法环境的 this 值(“词法 this”)。所以我们可以去掉中间的that变量:

var countup = {
    counter: 0,
    start: function() {
        setInterval(() => this.counter++, 1000)
    }
}

setInterval 内部,默认的this 值是全局对象,但没有什么可以阻止您使用箭头函数或callapplybind 使用另一个this 值。它只是对对象的引用(或null,或undefined)。因此,如果您想从setInterval 更新共享状态,那么您可以通过this 进行更新。这是否是一个好主意是主观的。就我个人而言,我尽量避免this,因为我认为这会使事情变得更复杂,但很多开发人员的想法恰恰相反。

根据我的经验,this 往往更多地用于使用基于类的面向对象的代码库中,因为这些代码库的方法经常需要引用调用它们的对象的状态。

class Clock {
  start() {
    setInterval(() => this.tick(Date()), 1000) // using lexical `this`!
  }
  tick(time) {
    console.log(time)
  }
}
const c = new Clock
c.start()

【讨论】:

    【解决方案3】:

    setInterval 存在于全局/WindowOrWorkerGlobalScope 上,因此this 的值与此绑定。由于全局是唯一的并且可以在整个脚本中访问,我认为setInterval

    的“绑定”函数不会有任何用例

    【讨论】:

      猜你喜欢
      • 2012-03-22
      • 2012-01-13
      • 2014-11-08
      • 1970-01-01
      • 2012-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-04
      相关资源
      最近更新 更多