Diferença entre this e self em JavaScript

118

Todos estão cientes do thisjavascript, mas também há casos de selfencontrados na natureza, como aqui

Então, qual é a diferença entre thise selfem JavaScript?

Ben Nelson
fonte
3
E sobre isso ...
Denys Séguret 01 de
8
@dystroy: Existe um: window.self( === window). Embora OP provavelmente signifique um nome de variável trivial ...
Bergi
2
@dystroy: Na verdade, eu não achei que ele realmente quisesse dizer isso, mas de fato em escopo global (e em um ambiente de navegador) this === selfé verdade :-)
Bergi
2
Subjetivo à parte: aliasing thispara selfnão é uma boa prática hoje em dia quando é comum ter código com muitos (bem, mais de um é ruim o suficiente) níveis de aninhamento de callback, como consequência da programação assíncrona. Use um nome mais descritivo. thisFalar objetivamente o nome em si não contém nenhuma informação e é apenas uma escolha de nome não ruim porque o contexto léxico de uma definição de classe o qualifica.
milimoose de
2
esta é uma pergunta válida e útil, deveria ser reaberta
danza

Respostas:

127

A menos que definido em outro lugar, o valor de selfé windowporque o JavaScript permite que você acesse qualquer propriedade xde windowsimplesmente x, em vez de window.x. Portanto, selfé realmente window.self, o que é diferente de this.

window.self === window; // true

Se você estiver usando uma função que é executada no escopo global e não está no modo estrito, o thispadrão é window, e portanto

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

Se você estiver usando uma função em um contexto diferente, thisirá se referir a esse contexto, mas selfainda será window.

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

Você pode encontrar window.selfas definições no rascunho de trabalho do W3C 2006 para o objeto Janela aqui .

Paul S.
fonte
34
Para ser completo, selfé útil no contexto do WebWorker quando a janela não está acessível ( developer.mozilla.org/en-US/docs/Web/Guide/Performance/… ). Usar em selfvez de windowpermite acessar o objeto global de forma portátil.
lqc 01 de
24

Uma ligeira adição a isso, pois as pessoas podem encontrar isso no contexto de trabalhadores de serviço, caso em que significa algo um pouco diferente.

Você pode ver isso em um módulo de service worker:

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

Aqui eu se refere ao WorkerGlobalScope e este é o método padrão para definir ouvintes de eventos.

Da documentação do Mozilla :

Ao usar self, você pode se referir ao escopo global de uma maneira que funcionará não apenas em um contexto de janela (self resolverá para window.self), mas também em um contexto de trabalhador (self irá então resolver para WorkerGlobalScope.self).

andyhasit
fonte
Obrigado ! Eu estava procurando por esta resposta :)
agpt
23

Embora eu esteja atrasado aqui, encontrei um exemplo que também pode ser útil para entender thismelhor:

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

Antes do ECMA 5, thisna função interna se referia ao objeto de janela global; ao passo que, a partir do ECMA 5, thisna função interna seria indefinida.

Shashank Vivek
fonte
isso é sempre definido em seu contexto. O que é indefinido é this.foo. Essa é uma grande diferença e para atingir o comportamento que você mencionou que existia antes do ECMA 5, as funções de seta podem ser usadas ou conforme você especificou atribuindo self para estar fora da função interna e usando self dentro em vez disso, a maneira mais limpa é a função de seta .
Dejazmach
2

A referência ao ECMA 5 precisa ser esclarecida.

Presumo que signifique ECMA-262 Edição 5. Deve-se observar que ECMA-262 (também conhecido como ECMAScript ou, menos precisamente, Javascript) é uma linguagem de script geral que foi implementada em navegadores da Internet. Do padrão da edição 5.1:

As etapas a seguir são realizadas quando o controle entra no contexto de execução para o código de função contido no objeto de função F, um chamador fornecido thisArg e um chamador fornecido argumentsList:

  1. Se o código da função for um código estrito, defina ThisBinding como thisArg.
  2. Caso contrário, se thisArg for nulo ou indefinido, defina ThisBinding como o objeto global.
  3. Caso contrário, se Type (thisArg) não for Object, defina ThisBinding como ToObject (thisArg).
  4. Caso contrário, defina ThisBinding como thisArg
  5. ... (não sobre "isso")

O termo "objeto global" se refere a qualquer objeto que esteja no topo da cadeia de escopo. Para navegadores, este seria o objeto "janela", mas é uma opção de implementação (o Windows Script Host tem um objeto global invisível, mas nenhum modo estrito, portanto, referências não qualificadas acessam suas propriedades e não há um "self" global). Além disso, o "modo estrito" deve ser ativado explicitamente, caso contrário, não estará ativo (seção 14.1 da norma). Como tal, um indefinido "this" ainda resolveria para o objeto global (janela) em "ECMA 5" com o modo estrito não ativo.

Portanto, a resposta à pergunta é:

"this" sempre se refere ao objeto que invoca a função. Se a função não foi chamada por um objeto (ou seja, não por uma chamada de método), então "this" (conforme passado para a função) é "indefinido". No entanto, se NÃO estiver usando o modo estrito, um "this" indefinido será definido para o objeto global (regra 2 acima).

"self" não tem nenhum significado sintático especial, é apenas um identificador. Os navegadores tendem a definir window.self (apenas uma propriedade do objeto global window) = window. Isso resulta em referências não qualificadas a "self" sendo o mesmo que "window" A MENOS que "self" tenha sido redefinido dentro de um escopo abrangente (como por "var self = this;" acima. Boa sorte ao redefinir "this".)

Portanto, a explicação completa do exemplo acima é:

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"

Uma variação interessante do exemplo cria um fechamento, retornando uma referência à função interna.

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();

Produzindo

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

Observe como a função interna não é chamada até que seja invocada por seuObjeto. Portanto, this.foo agora é seuObjeto.foo, mas self ainda é resolvido para a variável no escopo envolvente que, no momento em que o objeto de função interna foi retornado, era (e no fechamento resultante ainda é) meuObjeto. Portanto, dentro da função interna, "this" se refere ao objeto que chama a função interna, enquanto "self" se refere ao objeto que chamou a função externa para criar a referência à função interna.

Para resumir o resumo do resumo, "this" é definido pelo padrão de linguagem, "self" é definido por quem o define (implementador de tempo de execução ou programador final).

Uber Kluger
fonte
0

Encontre abaixo algumas combinações de saídas de console 'janela', 'self' e 'this' no escopo global (ambiente do navegador) para ver a que se refere.

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
SridharKritha
fonte