是 var self = this; 不好的模式?

IT技术 javascript
2021-02-01 07:09:27

我发现自己需要:

var self = this;

在我的 javascript 'classes' 中有很多。虽然这是常用的做法,但感觉有点不对。我希望在这个问题中找到一种更好的方法来解决这个问题,或者让我相信这是很好的。

这是保持正确绑定的标准方法吗?除非我明确需要“this”,否则我是否应该标准化在任何地方使用“self”。

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

6个回答

正如其他人所说:这个“额外变量”是(在某种程度上)获得this特殊表达式这一事实的唯一方法,因此,它不是变量,不受执行上下文/闭包的约束。

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

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

概括

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

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

单点问题:

……虽然这是常用的做法,但感觉有点不对。我希望在这个问题中找到一种更好的方法来解决这个问题,或者让我相信这是很好的。

做你认为正确的事情。不要害怕尝试一种方法并稍后切换回来(但请尝试在每个项目中保持一致:-)

这是保持正确绑定的标准方法吗?除非我明确需要“this”,否则我是否应该标准化在任何地方使用“self”。

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

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

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

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

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

作为旁白...

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

我的代码中的“私有”方法以“_”开头,如果用户调用它们,那就是它们。当使用原型方法创建对象时,这也更有效(确实是必需的)。但是,Douglas Crockford 不同意我的这种“私有”方法,并且在某些情况下,查找链可能会通过注入意外接收器来阻止您:

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

快乐编码。

很好的答案。您能否详细说明一下这句话:“我喜欢避免成员“需要自我”,因此通常将所有成员函数提升到接收者(this)只是“流经”的属性,这通常是“如预期的”。你怎么做到这一点?
2021-03-20 07:09:27
邪恶一倍。放入var self = false全局上下文,然后var self = this;在需要的地方添加方法。然后,如果有疑问,请写(self || this).method(...)这是糟糕的编码,但感觉很好。
2021-03-24 07:09:27
@Evert 如果每个方法都是对象的成员,它将/应该被调用为某种形式,obj.member通常会确保它this是正确的(因为obj->this(obj)->this(obj)->...)。如果您在帖子中看到 Crockford 链接,您将看到私有方法方法打破了模式,因为私有“方法”现在存储在构造函数的变量中,因此不会在obj.member表单中调用除非完成额外的工作,否则这将this在它们内部抛出(就像this调用时函数的接收者一样——obj在上面的例子中)。
2021-04-02 07:09:27
aelf是窗口对象——我会避免使用保留字,并使用var that = thisvar _self = this
2021-04-08 07:09:27
@chovy 我从未想过的有趣点。我想我总是回避的问题始终[除了在罕见的时候,我忘了下,用window.x在我的代码资格(这一点,我从来没有使用window.self。)
2021-04-09 07:09:27

是的,这是标准方式。

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

考虑以下

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

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

new foo();

如果你想这样做但避免使用像selfand use call()or之类的变量apply()......好吧......你看着它并开始尝试,但很快意识到你不能。setTimeout()负责 lambda 的调用,使您无法利用这些替代调用样式。您最终仍会创建一些中间变量来保存对对象的引用。

所以......我看到这个的方式:使用 bind 给出的代码比这个答案中给出的代码更短:)。所以这个答案是错误的。“但并非总是”是一种误导。
2021-03-16 07:09:27
@drodsou 不,你的代码不起作用,看看这个 JSBin它因语法错误而失败,因为您不能在 JS 中将某些内容包装在“()”中。如您所见,window.setTimeout 不返回函数/对象,而是返回一个代表计数器 ID 的“数字”类型的原语。
2021-03-18 07:09:27
@drodsou 使用 apply(this) 将立即执行函数,从而返回 undefined 作为 setTimeout 的第一个参数,但是有一个绑定函数:window.setTimeout(function () { alert( "Hi from "+this.name); }.bind (这), 1e3);
2021-04-02 07:09:27
对不起,如果我误解了,但如果你这样做,它会起作用: setTimeout( (function() { alert( "Hi from " + this.name ); }).apply(this), 1000 ); }
2021-04-04 07:09:27
@Peter Bailey 实际上还有另一种方法,不过我并不是说这是更好的解决方案。
2021-04-07 07:09:27

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

没有关于 JavaScript 和类/实例系统的标准。您必须选择您喜欢的对象模型类型。是背景知识另一个链接结论:没有结论。

通常var self= this;在闭包中保留一个副本(*)与围绕闭包构建的对象模型密切相关,每个方法的每个实例副本。这是一种有效的做事方式;少了几分效率高,而且通常少一点的工作比选择,对象模型构建原型左右,使用thisapply()和ECMAScript第五版的bind()获得绑定方法。

当您在同一代码中混合了两种风格时,更可以算作“邪恶”。不幸的是,很多常见的 JS 代码都是这样做的(因为让我们面对现实,没有人真正理解 JavaScript 奇异的原生对象模型)。

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

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

我认为现在有更好的方法来处理这个问题:

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

在 javascript 和其他带有闭包的语言中,这可能是一件非常重要的事情。this在方法引用的对象实际上可以改变一旦您将self变量设置为等于this,那么self将可靠地保持对相关对象的引用,即使this稍后指向不同的对象。

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

编辑:啊,好吧,你知道这一切。(可能对其他人仍然有帮助。)我要补充一点,Apply(和 Call)更适合从“外部”使用,为您正在调用的函数提供您已经知道的特定范围。一旦你进入一个函数并且你将进一步向下级联到闭包中,该技术:

  var self = this;

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