Para comprender esto en base a dónde se encuentra esto, la situación se puede dividir aproximadamente en tres tipos:
1. En la función: este suele ser un parámetro implícito.
2. Fuera de la función (en el alcance superior): en el navegador, esto se refiere al objeto global; en node.js se refiere a las exportaciones de módulos.
3. Cadena pasada a eval (): si eval () se llama directamente, esto se refiere al objeto actual; Si eval () se llama indirectamente, esto se refiere al objeto global.
Hemos realizado las pruebas correspondientes para estas categorías:
1. Esto en la función
Las funciones básicamente pueden representar todas las estructuras invocadas en JS, por lo que este es también el escenario más común donde se usa, y las funciones pueden subdividirse en los siguientes tres roles:
Funciones reales
Constructor
método
1.1 Esto en funciones reales
En funciones reales, el valor de esto es un patrón que depende del contexto en el que se encuentra.
Modo descuidado: esto se refiere a un objeto global (ventana en el navegador).
La copia del código es la siguiente:
función sloppyfunc () {
console.log (this === ventana); // verdadero
}
sloppyfunc ();
Modo estricto: el valor de esto no está definido.
La copia del código es la siguiente:
función strictFunc () {
'Use estricto';
console.log (this === Undefined); // verdadero
}
strictfunc ();
Este es un parámetro implícito de una función, por lo que su valor es siempre el mismo. Sin embargo, puede definir este valor utilizando métodos de llamada () o aplicar () para mostrar el valor.
La copia del código es la siguiente:
función func (arg1, arg2) {
console.log (esto); // 1
console.log (arg1); // 2
console.log (arg2); // 3
}
func.call (1, 2, 3); // (esto, arg1, arg2)
FunC.Apply (1, [2, 3]); // (esto, Arraywithargs)
1.2 Esto en el constructor
Puede usar una función como constructor a través de nuevo. La nueva operación crea un nuevo objeto y pasa este objeto al constructor a través de esto.
La copia del código es la siguiente:
Var guardó esto;
function constrat () {
SavedThis = esto;
}
var inst = new Constrat ();
console.log (savedthis === inst); // verdadero
El principio de implementación de la nueva operación en JS se muestra aproximadamente en el siguiente código (ver aquí para una implementación más precisa, esta implementación también es más complicada):
La copia del código es la siguiente:
function Newoperator (construye, Arraywithargs) {
var this thisValue = object.create (constr.prototype);
Constr.Apply (este valor, arraywithargs);
devolver este valor;
}
1.3 esto en el método
En los métodos, el uso de esto tiende a estar más en el lenguaje tradicional orientado a objetos: el receptor señalado por esto, es decir, el objeto que contiene este método.
La copia del código es la siguiente:
var obj = {
Método: function () {
console.log (this === obj); // verdadero
}
}
obj.method ();
2. Esto en alcance
En el navegador, el alcance es el alcance global, y esto se refiere a este objeto global (como la ventana):
La copia del código es la siguiente:
<script>
console.log (this === ventana); // verdadero
</script>
En Node.js, generalmente ejecuta funciones en módulos. Por lo tanto, el alcance de nivel superior es un alcance del módulo muy especial:
La copia del código es la siguiente:
// `Global` (no` Window`) consulte el objeto global:
console.log (math === global.math); // verdadero
// `this` no se refiere al objeto global:
console.log (this! == global); // verdadero
// `esto` se refiere a las exportaciones de un módulo:
console.log (this === módulo.exports); // verdadero
3. Esto en eval ()
eval () puede llamarse directamente (llamando al nombre de la función 'eval') o indirectamente (por otros medios, como la llamada ()). Para más detalles, consulte aquí.
La copia del código es la siguiente:
// funciones reales
función sloppyfunc () {
console.log (eval ('this') === ventana); // verdadero
}
sloppyfunc ();
función strictFunc () {
'Use estricto';
console.log (eval ('this') === Undefined); // verdadero
}
strictfunc ();
// constructores
Var guardó esto;
function constrat () {
savedthis = eval ('this');
}
var inst = new Constrat ();
console.log (savedthis === inst); // verdadero
// Métodos
var obj = {
Método: function () {
console.log (eval ('this') === obj); // verdadero
}
}
obj.method ();
4. TRAPS relacionadas con esto
Debe tener cuidado con las 3 trampas relacionadas con esto que se introducirán a continuación. Tenga en cuenta que en el siguiente ejemplo, usar el modo estricto puede mejorar la seguridad del código. Dado que en funciones reales, el valor de esto no está definido, obtendrá una advertencia cuando algo salga mal.
4.1 Olvidé usar nuevo
Si no está utilizando nuevo para llamar al constructor, en realidad está utilizando una función real. Entonces este no será el valor que espere. En modo descuidado, esto apunta a la ventana y creará variables globales:
La copia del código es la siguiente:
punto de función (x, y) {
this.x = x;
this.y = y;
}
var p = punto (7, 5); // ¡Olvidamos nuevos!
console.log (p === indefinido); // verdadero
// Se han creado variables globales:
console.log (x); // 7
console.log (y); // 5
Sin embargo, si está utilizando el modo estricto, aún obtendrá una advertencia (esto === Undefined):
La copia del código es la siguiente:
punto de función (x, y) {
'Use estricto';
this.x = x;
this.y = y;
}
var p = punto (7, 5);
// typeError: no se puede establecer la propiedad 'x' de indefinido
4.2 Método de uso inapropiado
Si obtiene directamente el valor de un método (no lo llama), está utilizando este método como una función. Cuando desee pasar un método como parámetro en una función o un método de llamadas, lo más probable es que lo haga. Este es el caso con SetTimeOut () y los controladores de eventos de registro. Usaré el método Callit () para simular este escenario:
La copia del código es la siguiente:
/** Similar a setTimeOut () y setimmediate ()*/
función Callit (func) {
func ();
}
Si llama a un método como una función en modo descuidado, * esto * apunta al objeto global, por lo que la creada posteriormente será variables globales.
La copia del código es la siguiente:
Var contador = {
Conde: 0,
// método de modo descuidado
inc: functer () {
this.count ++;
}
}
callit (contador.inc);
// no funcionó:
console.log (Counter.Count); // 0
// en su lugar, se ha creado una variable global
// (Nan es el resultado de aplicar ++ a indefinido):
console.log (cuenta); // nan
Si hace esto en modo estricto, esto no está definido y aún no obtendrá el resultado deseado, pero al menos recibirá una advertencia:
La copia del código es la siguiente:
Var contador = {
Conde: 0,
// método de modo estricto
inc: functer () {
'Use estricto';
this.count ++;
}
}
callit (contador.inc);
// typeError: no puede leer la propiedad 'contar' de undefinado
console.log (Counter.Count);
Para obtener los resultados esperados, puede usar Bind ()::
La copia del código es la siguiente:
Var contador = {
Conde: 0,
inc: functer () {
this.count ++;
}
}
callit (contador.inc.bind (contador));
// ¡funcionó!
console.log (Counter.Count); // 1
bind () crea otra función que siempre puede establecer este valor para contrarrestar.
4.3 Oculta esto
Cuando usa funciones en métodos, a menudo ignora que las funciones tienen su propia cuenta. Esto es diferente del método, por lo que no puede mezclar estos dos juntos. Para más detalles, consulte el siguiente código:
La copia del código es la siguiente:
var obj = {
Nombre: 'Jane',
Amigos: ['Tarzan', 'Cheeta'],
loop: function () {
'Use estricto';
this.friends.foreach (
función (amigo) {
console.log (this.name+'Knows'+Friend);
}
);
}
};
obj.loop ();
// typeError: no se puede leer el 'nombre' de la propiedad del indefinido
This.name en la función en el ejemplo anterior no se puede usar porque el valor de esta función no está definido, que es diferente de esto en el bucle de método (). Las siguientes son tres ideas para resolver este problema:
1. Eso = esto, asigne esto a una variable, de modo que esto se manifiesta explícitamente (excepto que, Self también es un nombre de variable muy común utilizado para almacenar esto), y luego use esa variable:
La copia del código es la siguiente:
loop: function () {
'Use estricto';
var que = esto;
this.friends.foreach (función (amigo) {
console.log (that.name+'conocimiento'+amigo);
});
}
2. Bind (). Use Bind () para crear una función. Esta función siempre contiene el valor que desea aprobar (en el siguiente ejemplo, este método):
La copia del código es la siguiente:
loop: function () {
'Use estricto';
this.friends.foreach (función (amigo) {
console.log (this.name+'Knows'+Friend);
} .bind (this));
}
3. Use el segundo parámetro de foreach. El segundo parámetro de foreach se pasará a la función de devolución de llamada y se usará como este de la función de devolución de llamada.
La copia del código es la siguiente:
loop: function () {
'Use estricto';
this.friends.foreach (función (amigo) {
console.log (this.name+'Knows'+Friend);
}, este);
}
5. Mejores prácticas
En teoría, creo que la función real no pertenece a su propia esto, y la solución anterior también se basa en esta idea. ECMAScript 6 utiliza la función de flecha para lograr este efecto, que es una función que no tiene su propia esto. En tal función, puede usar esto a voluntad, sin preocuparse por si existe alguna existencia implícita.
La copia del código es la siguiente:
loop: function () {
'Use estricto';
// El parámetro de foreach () es una función de flecha
this.friends.foreach (amigo => {
// `this` es el` this` de Loop
console.log (this.name+'Knows'+Friend);
});
}
No me gusta que algunas API consideren esto como un parámetro adicional para una función real:
La copia del código es la siguiente:
antes que la función (function () {
this.AddMatchers ({
TobeInRange: function (start, end) {
...
}
});
});
Escribir un parámetro implícito como se pasa explícitamente, el código parecerá mejor para entender, y esto es consistente con los requisitos de la función de flecha:
La copia del código es la siguiente:
ANTEREACH (API => {
API.AddMatchers ({
TobeInrange (Start, End) {
...
}
});
});