Para entender isso com base em onde está localizado, a situação pode ser dividida aproximadamente em três tipos:
1. Na função: este geralmente é um parâmetro implícito.
2. Fora da função (no escopo superior): no navegador, isso se refere ao objeto global; no Node.js refere -se às exportações de módulos.
3. String passada para avaliar (): se avaliar () for chamado diretamente, refere -se ao objeto atual; Se avaliar () () for chamado indiretamente, isso se refere ao objeto global.
Realizamos testes correspondentes para essas categorias:
1. Isso na função
As funções podem basicamente representar todas as estruturas invocadas no JS, portanto, esse também é o cenário mais comum em que isso é usado, e as funções podem ser subdivididas nas três funções a seguir:
Funções reais
Construtor
método
1.1 isto em funções reais
Nas funções reais, o valor disso é um padrão que depende do contexto em que está localizado.
Modo desleixado: refere -se a um objeto global (janela no navegador).
A cópia do código é a seguinte:
função sloppyfunc () {
console.log (this === Window); // verdadeiro
}
sloppyfunc ();
Modo rigoroso: o valor disso é indefinido.
A cópia do código é a seguinte:
função strictfunc () {
'Use rigoroso';
console.log (this === indefinido); // verdadeiro
}
strictfunc ();
Este é um parâmetro implícito de uma função; portanto, seu valor é sempre o mesmo. No entanto, você pode definir esse valor usando os métodos Call () ou Aplicar () para exibir o valor.
A cópia do código é a seguinte:
função func (arg1, arg2) {
console.log (this); // 1
console.log (arg1); // 2
console.log (arg2); // 3
}
func.call (1, 2, 3); // (isto, arg1, arg2)
func.Apply (1, [2, 3]); // (isso, Arraywithargs)
1.2 isto no construtor
Você pode usar uma função como construtor através do novo. A nova operação cria um novo objeto e passa esse objeto para o construtor por isso.
A cópia do código é a seguinte:
Var salvou isso;
função const () {
salvoThis = this;
}
var inst = new Constr ();
console.log (salvoThis === Inst); // verdadeiro
O princípio da implementação da nova operação no JS é mostrado aproximadamente no código a seguir (veja aqui uma implementação mais precisa, essa implementação também é mais complicada):
A cópia do código é a seguinte:
função newOperator (construt, ArrayWithargs) {
var thisValue = object.create (constr.prototype);
Constr.Apply (thisValue, ArrayWithargs);
retornar esse valor;
}
1.3 isto no método
Nos métodos, o uso disso tende a ser mais na linguagem tradicional orientada a objetos: o receptor apontado por isso, ou seja, o objeto que contém esse método.
A cópia do código é a seguinte:
var obj = {
Método: function () {
console.log (this === obj); // verdadeiro
}
}
obj.method ();
2. Este está em escopo
No navegador, o escopo é o escopo global, e isso se refere a esse objeto global (como janela):
A cópia do código é a seguinte:
<Cript>
console.log (this === Window); // verdadeiro
</script>
No Node.js, você geralmente executa funções em módulos. Portanto, o escopo de nível superior é um escopo de módulo muito especial:
A cópia do código é a seguinte:
// `global` (não` window`) Consulte o objeto global:
console.log (math === global.math); // verdadeiro
// `this` não se refere ao objeto global:
console.log (this! == Global); // verdadeiro
// `this` refere -se às exportações de um módulo:
console.log (this === Module.exports); // verdadeiro
3. Isso em Eval ()
Eval () pode ser chamado diretamente (chamando o nome da função 'avaliação') ou indiretamente (por outros meios, como Call ()). Para mais detalhes, veja aqui.
A cópia do código é a seguinte:
// Funções reais
função sloppyfunc () {
console.log (avaliar ('this') === janela); // verdadeiro
}
sloppyfunc ();
função strictfunc () {
'Use rigoroso';
console.log (avaliar ('this') === indefinido); // verdadeiro
}
strictfunc ();
// construtores
Var salvou isso;
função const () {
salvoThis = Eval ('this');
}
var inst = new Constr ();
console.log (salvoThis === Inst); // verdadeiro
// Métodos
var obj = {
Método: function () {
console.log (avaliar ('this') === obj); // verdadeiro
}
}
obj.method ();
4. Armadilhas relacionadas a isso
Você deve ter cuidado com as três armadilhas relacionadas a isso que serão introduzidas abaixo. Observe que, no exemplo a seguir, o uso de modo rigoroso pode melhorar a segurança do código. Como nas funções reais, o valor disso é indefinido, você receberá um aviso quando algo der errado.
4.1 Esqueceu de usar novo
Se você não está usando o novo para chamar o construtor, está realmente usando uma função real. Portanto, esse não será o valor que você espera. No modo desleixado, isso aponta para a janela e você criará variáveis globais:
A cópia do código é a seguinte:
ponto de função (x, y) {
this.x = x;
this.y = y;
}
var p = ponto (7, 5); // Esquecemos novos!
console.log (p === indefinido); // verdadeiro
// variáveis globais foram criadas:
console.log (x); // 7
console.log (y); // 5
No entanto, se você estiver usando o modo rigoroso, ainda receberá um aviso (this === indefinido):
A cópia do código é a seguinte:
ponto de função (x, y) {
'Use rigoroso';
this.x = x;
this.y = y;
}
var p = ponto (7, 5);
// typeError: não é possível definir a propriedade 'x' de indefinida
4.2 Método de uso inadequado
Se você obtiver diretamente o valor de um método (não o chamando), estará usando esse método como uma função. Quando você deseja passar um método como um parâmetro em uma função ou um método de chamada, você provavelmente fará isso. É o caso dos manipuladores de eventos Settimeout () e registro. Vou usar o método Callit () para simular este cenário:
A cópia do código é a seguinte:
/** Semelhante a setTimeout () e SetImediate ()*/
Função Callit (func) {
func ();
}
Se você chamar um método como uma função no modo desleixado, * isso * aponta para o objeto global, portanto, os criados posteriormente serão variáveis globais.
A cópia do código é a seguinte:
var contador = {
contagem: 0,
// Método Sloppy-Mode
Inc: function () {
this.count ++;
}
}
calit (contador.inc);
// não funcionou:
console.log (contador.count); // 0
// em vez disso, uma variável global foi criada
// (NAN é resultado da aplicação de ++ a indefinida):
console.log (contagem); // nan
Se você fizer isso no modo rigoroso, isso não é definido e você ainda não receberá o resultado desejado, mas pelo menos você receberá um aviso:
A cópia do código é a seguinte:
var contador = {
contagem: 0,
// Método Strict-Mode
Inc: function () {
'Use rigoroso';
this.count ++;
}
}
calit (contador.inc);
// TypeError: não é possível ler a propriedade 'contagem' de indefinição
console.log (contador.count);
Para obter os resultados esperados, você pode usar o bind ():
A cópia do código é a seguinte:
var contador = {
contagem: 0,
Inc: function () {
this.count ++;
}
}
calit (contador.inc.bind (contador));
// funcionou!
console.log (contador.count); // 1
bind () cria outra função que sempre pode definir esse valor para contrariar.
4.3 Esconda isso
Quando você usa funções nos métodos, geralmente ignora que as funções têm suas próprias. Isso é diferente do método, para que você não possa misturar esses dois. Para detalhes, consulte o seguinte código:
A cópia do código é a seguinte:
var obj = {
Nome: 'Jane',
Amigos: ['Tarzan', 'Cheeta'],
Loop: function () {
'Use rigoroso';
this.friends.foreach (
função (amigo) {
console.log (this.name+'sabe'+amigo);
}
);
}
};
obj.loop ();
// TypeError: não é possível ler a propriedade 'Nome' de indefinida
Este.name na função no exemplo acima não pode ser usado porque o valor dessa função é indefinido, o que é diferente disso no método loop (). A seguir, são apresentadas três idéias para resolver este problema:
1. Isso = isso, atribua isso a uma variável, para que isso seja explicitamente manifestado (exceto que, eu também é um nome de variável muito comum usado para armazenar isso) e, em seguida, use essa variável:
A cópia do código é a seguinte:
Loop: function () {
'Use rigoroso';
var que = this;
this.friends.foreach (função (amigo) {
console.log (that.name+'conhecimento'+amigo);
});
}
2. Bind (). Use bind () para criar uma função. Esta função sempre contém o valor que você deseja passar (no exemplo a seguir, este método):
A cópia do código é a seguinte:
Loop: function () {
'Use rigoroso';
this.friends.foreach (função (amigo) {
console.log (this.name+'sabe'+amigo);
} .bind (this));
}
3. Use o segundo parâmetro de foreach. O segundo parâmetro do foreach será transmitido para a função de retorno de chamada e usado como essa da função de retorno de chamada.
A cópia do código é a seguinte:
Loop: function () {
'Use rigoroso';
this.friends.foreach (função (amigo) {
console.log (this.name+'sabe'+amigo);
}, esse);
}
5. Melhores práticas
Em teoria, acho que a função real não pertence à sua própria, e a solução acima também se baseia nessa idéia. O ECMAScript 6 usa a função de seta para alcançar esse efeito, que é uma função que não tem isso. Em tal função, você pode usar isso à vontade, sem se preocupar se existe alguma existência implícita.
A cópia do código é a seguinte:
Loop: function () {
'Use rigoroso';
// O parâmetro de foreach () é uma função de seta
this.friends.foreach (amigo => {
// `this` é` `` isto '
console.log (this.name+'sabe'+amigo);
});
}
Não gosto de algumas APIs consideram isso um parâmetro adicional a uma função real:
A cópia do código é a seguinte:
antes e cada vez (function () {
this.addmatchers ({
TobeinRange: function (start, end) {
...
}
});
});
Escrevendo um parâmetro implícito, como explicitamente passado, o código parecerá melhor entender, e isso é consistente com os requisitos da função de seta:
A cópia do código é a seguinte:
antes e cada vez (API => {
api.addmatchers ({
TobeinRange (Start, End) {
...
}
});
});