JavaScript惯用语的基础是什么:var self = this?


357

我在WebKit HTML 5 SQL Storage Notes Demo的源代码中看到了以下内容:

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

笔者采用自我在一些地方(函数体)及在其他地方(的函数方法的参数列表中定义的机构)。这是怎么回事?既然我已经注意到它了,我是否会开始到处看到它?


4
这是一种称为“词汇闭包”的JS语言功能。
零下

2
可能的重复:var self = this?
DavidRR

THIS的概念在此处明确说明 scotch.io/@alZami/understanding-this-in-javascript
AL-zami

此答案中的相关示例stackoverflow.com/a/20279485/5610569(针对问题“如何访问正确的this在回调内部的代码?”)
FluxLemur

Answers:


431

请参阅alistapart.com上的这篇文章。(编辑:自最初链接以来,该文章已更新)

selfthis即使上下文在变化,也用于维护对原始文档的引用。这是事件处理程序中经常使用的一种技术(尤其是在闭包中)。

编辑:请注意,self现在不鼓励使用,window.self并且如果您不小心的话,有可能导致错误。

您所说的变量并不重要。var that = this;很好,但是名称没有什么魔术。

在上下文中声明的函数(例如,回调,闭包)将有权访问在相同范围或更高范围中声明的变量/函数。

例如,一个简单的事件回调:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});


似乎该文章变成了使用var that = this;
Bob Stein

@BobStein谢谢。我将相应地更新答案。
乔纳森·芬德兰

96

我认为变量名称“ self”不应该再使用这种方式,因为现代浏览器提供了全局变量self指向普通窗口或WebWorker全局对象的。

为了避免造成混乱和潜在的冲突,您可以写var thiz = this或改写var that = this


43
我通常使用_this
djheru 2014年

6
@djheru +1。比“ that”(我的大脑永远不习惯)好得多。
o_o_o-- 2014年

9
我开始使用“我” :)
Mopparthy Ravindranath

3
直到现代浏览器开始提供全局变量_this,that或me。
2015年

10
self只要您将名称声明为variable,使用名称绝对没有问题,它会遮盖全局名称。当然,如果您忘记了,var那么它也不能与其他任何名称一起使用。
Bergi

34

是的,您到处都会看到它。经常that = this;

看看self事件调用的内部函数如何使用?那些将具有自己的上下文,因此self被用来容纳this那些Note()

self即使这些函数只能在Note()函数完成执行后才能执行,其原因仍然对它们可用,原因是内部函数由于closure而获得了外部函数的上下文。


12
对我而言,令人信服的观点是self没有特殊意义。我个人更喜欢使用其他名称以外的var,self因为它经常使我感到困惑,因为我希望'self'是保留字。所以我喜欢你的答案。在OP的示例中,我更喜欢var thisNote = this或类似。
2014年

@steve同意,尽管我尽量避免使用此自引用,因为它们在可维护性方面非常脆弱。
mattLummus

28

还应该注意的是,this如果您不喜欢var self = this惯用语,则可以使用另一种代理模式来维护对回调中原始对象的引用。

由于可以使用function.apply或在给定的上下文function.call中调用函数,因此您可以编写一个包装器,该包装器返回使用applycall使用给定的上下文调用函数的函数。有关proxy此模式的实现,请参见jQuery 函数。这是使用它的一个例子:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFunc然后可以被调用,并将您的版本this作为上下文。


10

正如其他人所解释的,var self = this;允许闭包中的代码引用回父作用域。

但是,现在是2018年,所有主要的Web浏览器都广泛支持ES6。的var self = this;习语并不像以前那么重要。

现在可以var self = this;通过使用箭头功能来避免。

在本该使用的情况下var self = this

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

现在,我们可以使用没有var self = this以下功能的箭头功能:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

箭头函数没有自己的功能,this而仅假设其包含范围。


或者-震惊,恐怖!–为什么不将实际相关的东西作为参数传递给函数(闭包)?您为什么要引用超出范围的状态,为什么有人要这样编程?有从来没有任何真正的理由这样做。取而代之的是,.addEventListender("click", (x) => { console.log(x); });您已经非常清楚地说明了方法和原因,并且我同意使用箭头功能会更有意义,但仍然...这只是可怕的,懒惰的,混乱的编程。
本杰明·R

2
在JavaScript中,回溯到父范围是非常普遍和必要的。这是语言的基本组成部分。您实际上不建议将父范围作为事件处理程序的参数传递。同样,在ES6中,箭头函数使用词法作用域-“ this”是指当前范围,而不是进一步的范围-并不是“引用超出范围状态”或类似内容。
Elliot B.


9

这是一个JavaScript怪癖。当函数是对象的属性(更恰当地称为方法)时,是指对象。在事件处理程序的示例中,包含对象是触发事件的元素。当调用的标准功能,将涉及到全局对象。如示例中所示,当您具有嵌套函数时,根本与外部函数的上下文无关。内部函数做的含功能共享范围,因此开发人员使用的变化var that = this,以保持,他们在内部功能的需要。


5

实际上,自我是对窗口(window.self)的引用,因此当您说var self = 'something'要覆盖对自身的window引用时-因为self存在于window对象中。

这就是为什么大多数开发人员更喜欢var that = thisvar self = this;

无论如何; var that = this;与优良作法不符...假设您的代码将在以后由其他开发人员修改/修改,那么您应该使用与开发人员社区有关的最常见的编程标准

因此,您应该使用类似var oldThis/ var oThis/ etc的东西-要在您的范围内保持清晰// ..不是那么多,但可以节省几秒钟和更少的大脑周期


2
@prior我认为直到最后一段才有意义。
miphe

0

如上所述,在进入函数之前,仅使用“自我”来保持对“ this”的引用。在函数中,“ this”一词是指其他内容。


@JohnPaul ...这确实提供了答案。这可能不是正确的答案,但是您怎么能说“它被用来……”却不是“他为什么这样做”的答案?
GreenAsJade 2015年

-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call(thatt);


1
您应该在代码中添加一些您所做的特别说明。
Farhana '18年
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.