我在几乎所有的 knockout.js 视图模型示例中都看到该行var self = this
,然后所有局部变量都被引用为self.variableName
. 这比使用有什么好处this.variableName
?
3 回答
通常使用这种方法的主要原因是使电流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()
上面将有一个this
of myObject
,除非method
已经.bind(context)
应用了一个操作。在这种情况下this
,无论最后绑定的上下文是什么。
unattachedFunction()
将有一个this
全局上下文(通常window
在浏览器环境中),除非unattachedFunction
已经.bind(context)
应用了一个操作。在这种情况下this
,无论最后绑定的上下文是什么。
anyFunction.apply(otherObject)
或者
anyFunction.call(otherObject)
两者都将始终具有this
of otherObject
,因为以这种方式调用将覆盖任何绑定。
new myObject()
将有一个this
引用新实例的myObject
,这将覆盖任何绑定。
简单的思想实验
考虑到以上所有因素,this
里面会是referencedMethod
什么?
var referencedMethod = myObject.method;
referencedMethod();
正确的!这将是全球背景。这就是为什么如果你想与其他对象或代码共享方法——但仍然保留原始所有者作为上下文——你真的需要绑定,或者将函数与其所有者对象捆绑在一起,以便你可以调用或应用。
它用于参考目的。this
在 Javascript 下的行为与其他语言不同。有关更多详细信息,请参阅MDN Docs on this