JavaScript与this和self之间的区别


118

每个人都this在javascript中意识到这一点,但是self在野外也有遇到的实例,例如这里

那么,thisselfJavaScript 和有什么不一样?



8
@dystroy:有一个:window.self=== window)。尽管OP可能意味着一个琐碎的变量名……
Bergi 2013年

2
@dystroy:其实我没想到他真的可以这么说,但实际上在全球范围内(和浏览器环境中)this === self是真的:-)
Bergi 2013年

2
主观旁白:混叠thisself是不是一个伟大的实践时下,当它是常见的有很多(当然,不止一个是够糟糕的了)回调嵌套级别的代码,如异步编程的结果。请改用更具描述性的名称。客观地说,名称this本身不包含任何信息,并且仅是名称的一个不错的选择,因为类定义的词法上下文可以限定名称。
millimoose 2013年

2
这是一个有效且有用的问题,应重新开启
danza 2014年

Answers:


127

除有其他地方的价值selfwindow因为的JavaScript可以访问任何财产xwindow简单地x代替window.x。因此,self确实是window.self,与有所不同this

window.self === window; // true

如果您使用的是在全局范围内执行且未处于严格模式下的函数,则this默认为window,因此

function foo() {
    console.log(
        window.self === window, // is self window?
        window.self === this,   // is self this?
        this === window         // is this window?
    );
}
foo(); // true true true

如果您在其他上下文中使用函数,this则将引用该上下文,但self仍将是window

// invoke foo with context {}
foo.call({}); // true false false

你可以找到window.self在定义为2006年W3C工作草案窗口对象 这里


34
为了完整self起见,当无法访问窗口时,在WebWorker上下文中很有用(developer.mozilla.org/en-US/docs/Web/Guide/Performance/…)。使用self代替window可以让您以可移植的方式访问全局对象。
lqc

24

稍微增加一点,因为在服务人员的背景下人们可能会遇到这种情况,在这种情况下,这意味着有些不同。

您可能会在服务工作者模块中看到以下内容:

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
});

这里self指的是WorkerGlobalScope,这是设置事件侦听器的标准方法。

来自Mozilla docs

通过使用self,您可以以一种不仅在窗口上下文中(self将解析为window.self)而且在worker上下文中(self将解析为WorkerGlobalScope.self)工作的方式引用全局范围。


谢谢 !我一直在寻找这个答案:)
agpt

23

尽管我来晚了,但是我遇到了一个例子,这对于this进一步理解也很有帮助:

var myObject = {
 foo: "bar",
 func: function() {
    var self = this;
    console.log("outer func:  this.foo = " + this.foo);
    console.log("outer func:  self.foo = " + self.foo);
    (function() {
        console.log("inner func:  this.foo = " + this.foo);
        console.log("inner func:  self.foo = " + self.foo);
    }());
  }
};
myObject.func();

O / P

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

在ECMA 5之前,this内部函数将引用全局窗口对象;而从ECMA 5开始,this内部函数将不确定。


这总是在上下文中定义的。未定义的是this.foo。这是一个巨大的差异,并且要实现您提到的ECMA 5之前存在的行为,可以使用箭头函数,或者如您所指定的那样,将self设置为内部函数之外的对象,并使用self内部代替它,更干净的方法是使用箭头函数。
Dejazmach

2

对ECMA 5的引用需要澄清。

我认为这意味着ECMA-262第5版。应该注意的是ECMA-262(又名ECMAScript或更准确地说是Javascript)是一种通用的脚本语言,已在Internet浏览器中实现。从5.1版标准开始:

当控制进入功能对象F中包含的功能代码的执行上下文,调用者提供thisArg和调用者提供argumentsList时,执行以下步骤:

  1. 如果功能代码是严格代码,则将ThisBinding设置为thisArg。
  2. 否则,如果thisArg为null或未定义,则将ThisBinding设置为全局对象。
  3. 否则,如果Type(thisArg)不是Object,则将ThisBinding设置为ToObject(thisArg)。
  4. 否则将ThisBinding设置为此thisArg
  5. ...(与“ this”无关)

术语“全局对象”是指作用域链顶部的任何对象。对于浏览器,这将是“窗口”对象,但这是一种实现选择(Windows脚本宿主具有不可见的全局对象,但没有严格的模式,因此,不合格的引用将访问其属性,并且没有全局“自身”)。另外,必须明确启用“严格模式”,否则它不处于活动状态(标准的14.1节)。这样,在未激活严格模式的情况下,未定义的“ this”仍将解析为“ ECMA 5”中的全局​​对象(窗口)。

因此,问题的答案是:

“ this”总是指调用功能的对象。如果该函数未由对象调用(即不是方法调用),则“ this”(传递给该函数)为“未定义”。但是,如果不使用严格模式,则将未定义的“ this”设置为全局对象(上述规则2)。

“自我”没有特殊的句法含义,它只是一个标识符。浏览器倾向于定义window.self(只是全局window对象的一个​​属性)= window。这导致对“自我”的引用与“窗口”相同,除非在封闭范围内重新定义了“自我”(例如,在上面通过“ var self = this;”定义。祝您好运,重新定义“ this”。)

因此,上面示例的完整说明是:

outer func:  this.foo = bar
// "this" refers to the invoking object "myObject"
outer func:  self.foo = bar
// "self" resolves to the variable in the local scope which has been set to "this" so it is also "myObject"
inner func:  this.foo = undefined
// "this" refers to the invoking object (none) and so is replaced by the global object (strict mode must be off). "window" has no foo property so its "value" is undefined.
inner func:  self.foo = bar
// self resolves to the variable in the enclosing scope which is still "myObject"

该示例的一个有趣变体通过返回对内部函数的引用来创建闭包。

var myObject = {
 foo: "bar",
 func: function() {
    var self = this;
    console.log("outer func:  this.foo = " + this.foo);
    console.log("outer func:  self.foo = " + self.foo);
    return function() {
        console.log("inner func:  this.foo = " + this.foo);
        console.log("inner func:  self.foo = " + self.foo);
    };
  }
};
var yourObject = {
 foo: "blat",
 func: myObject.func() // function call not function object
};
console.log("----");
yourObject.func();

生产中

outer func:  this.foo = bar
outer func:  self.foo = bar
----
inner func:  this.foo = blat
inner func:  self.foo = bar

请注意,只有在yourObject调用内部函数之后,该函数才被调用。因此this.foo现在是yourObject.foo,但是self仍然解析为封闭范围内的变量,该范围在返回内部函数对象时是myObject(并且在生成的封闭中仍然是myObject)。因此,在内部函数中,“ this”是指调用内部函数的对象,而“ self”是指调用外部函数以创建对内部函数的引用的对象。

总结总结的摘要,“ this”由语言标准定义,“ self”由定义它的人(运行时实现者或最终程序员)定义。


0

在全局范围(浏览器环境)中,在下面找到“窗口”,“自我”和“此”控制台输出的一些组合,以查看其引用的位置。

console.log( window ); // Window {…}
console.log( self );   // Window {…}
console.log( this );   // Window {…}

console.log( window.window ); // Window {…}
console.log( window.self );   // Window {…}
console.log( window.this );   // undefined  

console.log( self.self );     // Window {…}
console.log( self.window );   // Window {…}
console.log( self.this );     // undefined

console.log( this.this );     // undefined
console.log( this.window );   // Window {…}
console.log( this.self );     // Window {…}

console.log( window.window.window );    // Window {…}
console.log( self.self.self );          // Window {…}
console.log( window.self.window.self ); // Window {…}
console.log( self.window.self.window ); // Window {…}
console.log( this.this );               // undefined
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.