在knockout.js视图模型中使用`var self = this`有什么好处

Nei*_*eil 22 javascript knockout.js

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

Peb*_*bbl 39

通常,使用此方法的主要原因是使当前this可用于子功能或闭包.例如:

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

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

method2: function(){
  var self = this;
  setTimeout(function(){
    alert( self.param );
  },100);
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为this捕获了当前状态- 在正确的点 - 并且将始终引用myObject它可用的每个功能范围.

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


而不是存储为变量

有一种替代方法可以使用self或任何其他变量来"记住"任何特定点的状态,即将您的匿名或子函数与特定上下文"绑定".许多现代解释器现在支持该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"});
Run Code Online (Sandbox Code Playgroud)

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

Window
Document
Object {random:"object"}
Run Code Online (Sandbox Code Playgroud)

如果您希望支持旧浏览器,您可以使用polyfill,或者如果您更喜欢更简单的实现,那么也不必担心绑定参数.绑定代码的基础知识如下:

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

那么,最初的例子如何使用bind?

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

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

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

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

此外,因为绑定返回一个新函数,如果你绑定一个绑定函数,你实际上是在一个函数中包含一个函数,与另一个函数.您应该意识到这一点,因为它会影响性能,并且在避免内存泄漏方面会更难管理.我首选的绑定方法是使用带有自己的解构方法的闭包(即依赖于self,但确保你有方法来取消它的内容),但这确实需要更多的前瞻性思维,并且在较小的JS项目中并不那么重要; 或者一个off函数绑定 - 特别是如果绑定方法永远不会被任何引用捕获.


没有自我和约束?

值得一提的是,有时您可以在不使用的情况下获得相同的结果bind,而是使用apply- 应该在您可能选择使用的任何内容中本机提供.主要的区别在于没有任何内容被函数包含,并且调用apply实际上在那里执行函数,然后使用不同的上下文 - 传递给apply的第一个参数.

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

var myObject = {
  method2: function(){
    externalMethod.apply(this);
  }
};
Run Code Online (Sandbox Code Playgroud)


什么是this

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

myObject.method()
Run Code Online (Sandbox Code Playgroud)

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

unattachedFunction()
Run Code Online (Sandbox Code Playgroud)

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

anyFunction.apply(otherObject)
Run Code Online (Sandbox Code Playgroud)

要么

anyFunction.call(otherObject)
Run Code Online (Sandbox Code Playgroud)

双方将始终有一个thisotherObject,因为调用以这种方式将覆盖任何约束力.

new myObject()
Run Code Online (Sandbox Code Playgroud)

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


简单的思想实验

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

var referencedMethod = myObject.method;
referencedMethod();
Run Code Online (Sandbox Code Playgroud)

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