23

我在几乎所有的 knockout.js 视图模型示例中都看到该行var self = this,然后所有局部变量都被引用为self.variableName. 这比使用有什么好处this.variableName

4

3 回答 3

40

通常使用这种方法的主要原因是使电流this可用于子函数或闭包。例如:

var myObject = {
  param: 123,
  method: function(){
    alert( this.param );
  },
  method2: function(){
    setTimeout(function(){
      alert( this.param );
    },100);
  }
}

在上面的调用myObject.method中会给你正确的警报123。但是打电话myObject.method2会给你undefined。这是因为this在使用 with 的匿名函数内部setTimeout不引用myObject,取决于 JavaScript 解释器,它会指向不同的东西。但是,如果您有:

method2: function(){
  var self = this;
  setTimeout(function(){
    alert( self.param );
  },100);
}

this这是有效的,因为在正确的点处的当前状态被捕获,并且将始终引用myObject它可用的每个函数范围。

问题不仅限于使用setTimeout. 在你有匿名函数、子函数或闭包的任何时候,这个技巧都会派上用场。有时人们使用self, 或that更具描述性的东西,具体取决于当前引用所代表的内容。


而不是存储为变量

除了使用self或任何其他变量来“记住” this 在任何特定点的状态之外,还有另一种方法,那就是将匿名或子函数与特定上下文“绑定”。现在许多现代解释器都支持该Function.prototype.bind方法,可以这样使用:

var method = function(){
  console.log(this);
};
var methodWithWindow = method.bind(window);
var methodWithDocument = method.bind(document);
var methodWithObject = method.bind({random:"object"});

依次调用每个绑定方法将为您提供以下控制台输出:

Window
Document
Object {random:"object"}

如果您希望支持较旧的浏览器,您可以使用polyfill,或者如果您更喜欢更简单的实现,也可以不用担心绑定参数。绑定代码的基本功能如下:

!Function.prototype.bind && (Function.prototype.bind = function(context){
  var method = this;
  return function(){
    method.apply(context, arguments);
  }
})

那么,使用 bind 的初始示例看起来如何呢?

method2: function(){
  setTimeout((function(){
    console.log(this); // `this` will be the same as the `this` passed to bind.
  }).bind(this),100);
}

正如您在上面看到的,一旦绑定,返回的函数(闭包)会保留指定的上下文;所以它可以在你想要的任何地方传递,并且仍然保留this对你想要的对象的引用。这在示例中很有用,method2因为我们将方法与当前上下文捆绑在一起,并将其传递给setTimeout稍后将执行绑定的方法(在我们退出当前块执行很久之后)。

使用self或任何其他变量时也会发生同样的情况。该变量将被捕获在函数的作用域链中,并且在最终再次调用该函数时仍然可以访问。但是,使用的好处bind是,如果您愿意,您可以轻松地覆盖上下文,您必须编写自己的特定方法来覆盖self变量。

警告:这里值得注意的是,当你绑定一个函数时,会返回一个新函数。如果您将绑定函数与事件侦听器混合,然后尝试使用原始函数而不是绑定版本删除侦听器,这可能会导致混乱的情况。

此外,因为绑定返回一个新函数,如果你绑定一个绑定函数,你实际上是用另一个函数将一个函数包装在一个函数中。您应该意识到这一点,因为它会影响性能,并且在避免内存泄漏方面更难管理。我首选的绑定方法是使用带有自己解构方法的闭包(即依赖自我,但要确保有方法可以取消其内容),但这确实需要更多的前瞻性思考,并且在较小的 JS 项目中不太相关;或一次性函数绑定——特别是如果绑定的方法从未在任何引用中被捕获。


没有自我和绑定?

还值得一提的是,有时您完全不使用也可以达到相同的结果bind,而是使用apply- 这应该在您可能选择使用的任何东西中本机可用。主要区别在于函数没有包含任何内容,调用 apply 实际上会在那里执行函数,然后使用不同的上下文 - 传递给 apply 的第一个参数。

var externalMethod = function(){
  console.log(this); // will output myObject when called below
};

var myObject = {
  method2: function(){
    externalMethod.apply(this);
  }
};


是什么this

只是为了详细说明这个答案this- 在最近的评论被删除之前。this将引用四件事之一,具体取决于您在其中使用它的函数的调用方式:

myObject.method()

上面将有一个thisof myObject,除非method已经.bind(context)应用了一个操作。在这种情况下this,无论最后绑定的上下文是什么。

unattachedFunction()

将有一个this全局上下文(通常window在浏览器环境中),除非unattachedFunction已经.bind(context)应用了一个操作。在这种情况下this,无论最后绑定的上下文是什么。

anyFunction.apply(otherObject)

或者

anyFunction.call(otherObject)

两者都将始终具有thisof otherObject,因为以这种方式调用将覆盖任何绑定。

new myObject()

将有一个this引用新实例的myObject,这将覆盖任何绑定。


简单的思想实验

考虑到以上所有因素,this里面会是referencedMethod什么?

var referencedMethod = myObject.method;
referencedMethod();

正确的!这将是全球背景。这就是为什么如果你想与其他对象或代码共享方法——但仍然保留原始所有者作为上下文——你真的需要绑定,或者将函数与其所有者对象捆绑在一起,以便你可以调用或应用。

于 2013-06-18T07:51:01.953 回答
2

Self用于确保this在对象中保留原件。

这在使用事件处理程序等时会派上用场。

您可以在此处阅读有关此内容的更多信息

第一个答案基本上涵盖了它,它也显示了一个很好的链接。一探究竟。

于 2013-06-18T07:46:15.153 回答
1

它用于参考目的。this在 Javascript 下的行为与其他语言不同。有关更多详细信息,请参阅MDN Docs on this

于 2013-06-18T07:52:30.717 回答