是var self = this; 模式不好?


76

我发现自己需要:

var self = this;

我的javascript“类”中有很多内容。尽管通常这样做,但感觉还是有些错误。我希望在这个问题中找到解决这个问题的更好方法,或者说服我这很正常的方法。

这是保持正确绑定的标准方法吗?我是否应该在所有地方都标准化使用“自我”,除非我明确需要“这个”。

编辑:我确切地知道为什么我需要这个,我只是想知道它是否被认为是邪恶的,为什么。我知道还有一个“应用”内置的javascript函数可以在调用方法时显式定义范围。这个会比较好吗?


2
我一直使用var that = this,老实说,我什至没有打扰到应用程序/调用,但现在我读到了有关这些方法的信息,谢谢您提出这个问题!
安德斯(Anders)2010年

我个人认为这本身还不错,但这是可以稍微优化设计的信号。
德米特里

Answers:


49

就像其他人所说的那样:“某种程度上,这个“额外变量”是解决这一事实的唯一方法,因为它this是一个特殊的表达式,因此不是变量,它不受执行上下文/闭包的约束。

但是,我认为您要问的(或我真正想回答的)是:

是否应该var self = this在每个方法/构造函数的顶部放置一个?

概要

当我尝试过一次并且有相同的问题时,我不再使用这种方法。现在,当需要在闭包中访问时,我保留该构造。对我来说,它增加了一点“嘿,这就是我真正想要的!” 我的代码的语义:

this -> thisself -> this (but really that) in a closure

问题单点:

...虽然通常这样做,但感觉还是有些错误。我希望在这个问题中找到解决这个问题的更好方法,或者说服我这样做的一切都很好。

做适合您的事情。不要害怕尝试一种方法,以后再切换回去(但请尝试在每个项目中保持一致:-)

这是保持正确绑定的标准方法吗?我是否应该在所有地方都标准化使用“自我”,除非我明确需要“这个”。

“自我”是最常用的名称。如上所述,我更喜欢使用相反的方法,this除非需要使用闭包绑定。

..如果认为它有点邪恶,为什么。

邪恶是一个愚蠢的主观术语(尽管有时很有趣)。我从未说过这是邪恶的,这就是为什么我不遵循这种方法。有人告诉我,我不使用分号是“邪恶的”。我告诉他们,他们实际上应该提出好的论据和/或更好地学习JavaScript :-)

我知道还有一个“应用”内置的javascript函数可以在调用方法时显式定义范围。这个会比较好吗?

问题apply/call在于您必须在函数调用时使用它们。如果其他调用了您的方法之一,那将无济于事,因为this可能已经关闭了。这对于执行诸如jQuery样式的回调之类的操作最为有用,其中jQuerythis是回调的元素/项目,等等。

作为旁白...

喜欢避免成员上的“需要自我”,因此通常将所有成员函数提升为接收者(this)只是“流经”(通常是“预期”)的属性。

我的代码中的“私有”方法以“ _”开头,如果用户调用它们,它们就在它们上面。当使用原型方法创建对象时,这也更好(实际上是必需的)。但是,道格拉斯·克罗克福德Douglas Crockford)不赞成我的这种“私有”方法,在某些情况下,查找链可能会由于注入意外的接收者而使您受挫:

在构造函数中使用“自我”绑定还可以锁定方法的查找链的上限(它不再是向上的多态!),这可能是正确的,也可能是不正确的。我认为通常是不正确的。

快乐的编码。


1
好答案。您能否详细说明一下:“我想避免成员上的“需要自我”,因此通常将所有成员函数提升为接收者(此)只是“流经”(通常是“预期”)的属性。” 你怎么做到这一点?
埃弗特

1
@Evert如果每个方法都是该对象的成员,则它将/应该以某种形式调用它,该形式obj.member通常可以确保this正确(因为obj->this(obj)->this(obj)->...)。如果您在帖子中看到Crockford链接,您将看到private方法打破了模式,因为私有“方法”现在存储在构造函数中的变量中,因此不会在obj.member表单中调用。除非完成额外的工作,否则this它们将在它们内部抛出(就像this在调用时,函数的接收者一样obj)。

3
aelf是窗口对象-我会避免使用保留字,和使用var that = thisvar _self = this
chovy

@chovy我从未想过的有趣观点。我想我总是回避的问题始终[除了在罕见的时候,我忘了下,用window.x在我的代码资格(这一点,我从来没有使用window.self。)

是邪恶的两倍。放入var self = false全局上下文,然后var self = this;在需要的地方添加方法。然后,如有疑问,请写信(self || this).method(...)。这是很糟糕的编码,但是感觉很好。
Orwellophile

17

是的,这是标准方式。

Function.apply()并且Function.call()可以帮助,但并非总是如此。

考虑以下

function foo()
{
  var self = this;
  this.name = 'foo';

  setTimeout( function()
  {
    alert( "Hi from " + self.name );
  }, 1000 );       
}

new foo();

如果您想这样做,但避免使用像selfand usecall()apply()...这样的变量,那么...您可以看一下并开始尝试,但是很快就意识到您做不到。setTimeout()负责lambda的调用,因此您无法利用这些替代的调用样式。您仍然会最终创建一些中间变量来保存对该对象的引用。


4
抱歉,如果我听错了,但是如果您这样做,它会起作用:setTimeout( (function() { alert( "Hi from " + this.name ); }).apply(this), 1000 ); }
drodsou

@drodsou不,您的代码不起作用,请看此JSBin。它因语法错误而失败,因为您不能只将某些内容包装在JS的“()”中。如您所见,window.setTimeout不返回函数/对象,而是返回代表计数器ID的“数字”类型的原语。
Sentenza'9

@Peter Bailey实际上还有另一种方法,但是我并不是说那是一个更好的解决方案。
Sentenza

3
@drodsou使用apply(this)将立即执行函数,从而返回undefined作为setTimeout的第一个参数,以及是否存在绑定函数:window.setTimeout(function(){alert(“ Hi from” + this.name);} .bind (this),1e3);
2015年

1
所以...我看到的方式:使用bind给出的代码比此答案中给出的代码更简短:)。所以这个答案是错误的。“但并非总是”具有误导性。
亚当·斯科博兹金斯基

9

这是保持正确绑定的标准方法吗?

没有关于JavaScript和类/实例系统的标准。您将必须选择自己喜欢的对象模型。这是指向背景资料的另一个链接;结论:没有结论。

通常var self= this;,在闭包中保留副本(*)与围绕闭包构建的对象模型齐头并进,闭包中包含每种方法的每个实例的副本。这是做事的有效方法。与原型(使用原型构建,使用thisapply()以及ECMAScript Fifth Editionbind()获取绑定方法)建立的对象模型相比,效率低一些,但通常工作量也少得多。

当您在同一代码中将两种样式混搭在一起时,可以算作“邪恶”。不幸的是,许多常见的JS代码都这样做(因为让我们面对现实,没有人真正理解JavaScript的奇异本机对象模型)。

(*:我通常使用;that而不是self;您可以使用任何您喜欢的变量名,但是self作为window指向窗口本身的成员已经有些模糊且完全没有意义了。)


7

刚遇到这个问题,是因为我的同事们沉迷于自我/那个变量,而我想了解为什么...

我认为当今有一种更好的方法可以解决问题:

function () {}.bind(this);      // native
_.bind(function () {}, this);   // lodash
$.proxy(function () {}, this);  // jquery

4

在具有闭包的javascript和其他语言中,这可能是一件非常重要的事情。this方法中引用的对象实际上可以更改。一旦您设置self变量等于this,则会可靠地保持到相关对象的引用,即使this后来点不同的东西。

与我们使用的许多其他语言相比,这是javascript的重要区别。我来自.Net,所以这种类型的东西一开始对我也很奇怪。

编辑:啊,好的,你知道的全部。(也许对其他人还是有帮助的。)我将补充说,“应用”(和“调用”)更多地是从“外部”使用的,这赋予了您要调用的函数的已知范围。一旦进入函数内部,您将进一步层叠到闭包中,该技术如下:

  var self = this;

是更合适的方式(容易清晰的)的方式来锚您的当前范围。


2

很有可能这样做是为了保持this对范围即将更改的引用(在关闭的情况下)。我不知道我认为这本身就是一种不良做法或模式,不。您会在jQuery之类的库中看到很多类似的事情,并且在使用AJAX时会看到很多类似的事情。


1

我认为,var self = this在所有方法中都应始终包含一个论点:人为因素。

经常需要它,以至于您杂乱无章地访问方法,this而其他人使用self用于相同的目的。如果将代码从一个移到另一个,突然会出现很多错误。

同时,self.foo当我不需要或添加时,我会因为心不在a而习惯性地写作var self = this。因此,我认为养成始终包括必要与否的习惯是有道理的。

唯一的麻烦是... thisself或者that代码上都是丑陋的痘痘,我有点讨厌它们。所以我觉得这是更好地避免使用委托方法在可能的情况,这样就可以尽量避免使用thisthatself绝大多数的时间,使用.bind(this)的时候,否则你可能诉诸self/that。很少在原型上使用委托实际上会为您节省大量内存。

这种方法的一个很好的副作用是,您不必在所有私有变量_前面加上,因为它们将是真正的私有,并且公共属性将由前导调用this.,从而使您的代码更具可读性。

正如bobince所说,var that = this更好是因为它不会遮挡window.selfself = this听起来对我来说不太尴尬,但有时您会收到诸如之类的错误消息property myMethod not found on global,因为您忘记了这一self = this行。


您能为jsfiddle提供一个用.bind(this)替换self的想法吗?
Sentenza'9

1
window.setTimeout(function(){this。$ btnAccept.tooltip('show');} .bind(this),256);
AlexanderB

1

我只想指出'self'等同于'window',请尝试将window === self输出到控制台。您应该将此模式与'that'或类似的变量名一起使用,避免使用'self',因为浏览器已在使用它(一个错误,您将自己创建一个全局变量)。即使听起来很奇怪,也最好使用“ that”作为名称,因为其他开发人员会立即知道您要在代码中完成的工作,避免使用非标准的变量名。我认为这是一个重要的注释,但仅在一个注释中提及,因此我想使其更明显。


尝试使用self对象在浏览器中创建一个全局变量,然后发布一个小提琴。在我的浏览器(Chromium)中,这是行不通的,因为如果您使用不带“ var”的“ self = ...”,它将在“ window”上定义属性“ self”。
Sentenza

我不确定为什么要创建一个名为“ self”的新全局变量。当然,在JavaScript中,您可以修改JavaScript本身,但是这样做时要格外小心,因为其他开发人员在向他们提供代码时会期望使用JavaScript的“标准版本”。当您将某些东西分配给“自我”时,实际上是在覆盖它,我会避免这样做。我只说过,当您比较默认窗口对象和self时,默认情况下它们是同一对象。换句话说,“ self”和“ window”是同义词
Goran Vasic

(一个错误,您将创建一个全局变量)
Sentenza

1

6年后的今天,我有几件事要补充:

bind()现在已经足够普遍,可以在任何地方使用。我经常使用它作为替代。有时感觉更清晰。我还是偶尔用var self = this;。虽然。

箭头功能逐渐变得可行。语法短了一点,这很好,但是我认为杀手级功能实际上是默认情况下它们始终绑定到父作用域。

这个:

var self = this;
var foo = function(a) {

  return self.b + a;

};

现在可以写成:

var foo = a => this.b + a;

这是箭头功能的“最乐观”用法,但非常好用。

简而言之,这没有什么错:

var self = this;

0

我喜欢。这是“自我”的解释。道格拉斯·克罗克福德(Douglas Crockford)对此有话要说。他指出,使用“ that”是惯例。如果您搜寻yui剧院并观看有关Javascript的视频,则可以免费观看Crockford 。


1
感谢您的反馈意见。我想我明白你的意思。但是,我不认为没有上下文就可以回答问题。我碰巧喜欢道格拉斯提出的上下文:)
Phluks 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.