consejo
En primer lugar, sé que este artículo es aburrido, no es más que esto en JS, y ha habido miles de artículos que escriben esta parte;
Sin embargo, todavía quiero escribir un artículo sobre esto en JS, que puede considerarse como un resumen; (Los dioses pueden dar vueltas y leer mis otros artículos)
En JS, el contexto de esto siempre es impredecible, y a menudo los errores siempre están confundidos. De hecho, siempre que distinga claramente cómo ejecutar en diferentes circunstancias, estará bien.
Ejecución global
Primero, echemos un vistazo a lo que esto es en el entorno global:
primero. Navegador:
console.log (this); // Window {SpeechSythesis: SpeechSythesis, Caches: Cachestorage, LocalStorage: Storage, SessionStorage: Storage, WebKitStorageInfo: DeprecedStorageInfo…}Puede ver que el objeto de la ventana está impreso;
segundo. nodo:
console.log (this); // global
Puede ver que el objeto global está impreso;
Resumen: en el alcance global, esto ejecuta el objeto global actual (ventana en el navegador, global en nodo).
Ejecutar en la función
Llamadas de función pura
Esta es la forma más común de usar la función:
function test () {console.log (this);}; test (); // Window {SpeechSynthesis: SpecheSynthesis, Caches: Cachestorage, LocalStorage: Storage, SessionStorage: Storage, WebKitStorageInfo: DeprecedStorageInfo ...}Podemos ver que cuando una función se llama directamente, pertenece a una llamada global, y en este momento es esto apunta al objeto global;
Modo estricto 'usar estricto';
Si las llamadas de función pura se ejecutan en modo estricto, esto aquí no apunta al global, sino que está indefinido. Esto es para eliminar un comportamiento sin niveles en JS:
'Use Strict'; function test () {console.log (this);}; test (); // UndefinedPor supuesto, ponerlo en una función de ejecución inmediata sería mejor, evitando contaminar la situación global:
(function () {"use estrict"; console.log (this);}) (); // indefinidoLlamada de método como objeto
Cuando una función se llama como método de objeto:
var obj = {nombre: 'Qiutc', foo: function () {console.log (this.name); }} obj.foo (); // 'qiutc'En este momento, esto apunta al objeto actual;
Por supuesto, podemos hacer esto:
función test () {console.log (this.name);} var obj = {name: 'Qiutc', foo: test} obj.foo (); // 'qiutc'También sin cambios, porque en JS todo es un objeto, y una función también es un objeto. Para la prueba, es solo un nombre de función, una referencia a la función, que apunta a esta función. Cuando foo = prueba, Foo también señala esta función.
¿Qué pasa si asigna el método del objeto a una variable y luego llama a esta variable directamente:
var obj = {nombre: 'Qiutc', foo: function () {console.log (this); }} var test = obj.foo; test (); // ventanaSe puede ver que esto ejecuta el mundo global en este momento. Cuando colocamos test = obj.foo, la prueba apunta directamente a una referencia a una función. En este momento, en realidad no tiene nada que ver con el objeto OBJ, por lo que se llama directamente como una función ordinaria, por lo que esto señala el objeto global.
Algunas trampas
A menudo nos encontramos con algunas dificultades en las funciones de devolución de llamada:
var obj = {nombre: 'Qiutc', foo: function () {console.log (this); }, foo2: function () {console.log (this); setTimeOut (this.foo, 1000); }} obj.foo2 ();Después de ejecutar este código, encontraremos que las impresiones son diferentes entre sí:
La primera vez es imprimir esto directamente en Foo2, señalando el objeto OBJ aquí, no tenemos dudas;
Sin embargo, esto. ¿No se usa como un método de función aquí? Esto a menudo desconcierta a muchos principiantes;
De hecho, SetTimeout es solo una función, y la función puede necesitar parámetros. Pasamos esto. Al pasar al parámetro, en realidad hicimos tal operación divertida = this.foo. Al ver que no hay, señalamos directamente la referencia de esto. Cuando se ejecuta, la diversión () realmente se ejecuta, por lo que no tiene nada que ver con OBJ. Se llama directamente como una función ordinaria, por lo que esto apunta al objeto global.
Este problema se encuentra comúnmente en muchas funciones de devolución de llamada asincrónica;
resolver
Para resolver este problema, podemos usar la función de cierre para tratarlo:
var obj = {nombre: 'Qiutc', foo: function () {console.log (this); }, foo2: function () {console.log (this); var _This = this; setTimeOut (function () {console.log (this); // window console.log (_this); // objeto {name: "qiutc"}}, 1000); }} obj.foo2 ();Puede ver que usar esto directamente es una ventana; Debido a que esto en FOO2 apunta a OBJ, primero podemos usar una variable _Elo para almacenarlo, y luego usar _Es en la función de devolución de llamada para apuntar al objeto actual;
Otro pozo de settimeut
Como se mencionó anteriormente, si la función de devolución de llamada se ejecuta directamente sin alcance vinculante, entonces es este punto al objeto global (ventana), que apuntará a indefinido en modo estricto. Sin embargo, la función de devolución de llamada en SetTimeOut muestra diferente en modo estricto:
'Use Strict'; function foo () {console.log (this);} setTimeout (foo, 1); // ventanaHablando lógicamente, agregamos el modo estricto, y la llamada FOO no especificó esto, por lo que debería estar indefinido, pero todavía hay un objeto global aquí. ¿Es porque el modo estricto ha fallado?
No, incluso en modo estricto, cuando el método SetTimeOut llama a la función entrante, si la función no especifica esto, hará una operación implícita: inyectar automáticamente el contexto global, que es equivalente a llamar a foo.apply (ventana) en lugar de foo ();
Por supuesto, si ya especificamos esto cuando pasamos en la función, entonces no seremos inyectados en el objeto global, como: setTimeout (foo.bind (obj), 1) ;;
Usar como constructor
En JS, para implementar la clase, necesitamos definir algunos constructores, y se necesita agregar la palabra clave nueva al llamar a un constructor:
Función Persona (nombre) {this.name = name; console.log (this);} var p = nueva persona ('qiutc'); // persona {nombre: "qiutc"}Podemos ver que cuando se llama como un constructor, esto apunta al objeto instanciado cuando se llama a este constructor;
Por supuesto, el constructor es en realidad una función. Si la ejecutamos como una función normal, esto aún se ejecutará a nivel mundial:
Función Persona (nombre) {this.name = name; console.log (this);} var p = persona ('qiutc'); // ventanaLa diferencia es cómo llamar a la función (nueva).
Función de flecha
En la nueva especificación ES6, se ha agregado una función de flecha. Lo más diferente de las funciones ordinarias es este punto. ¿Recuerdas que usamos cierres para resolver este problema de señalización? Si usamos la función de flecha, podemos resolverla más perfectamente:
var obj = {nombre: 'Qiutc', foo: function () {console.log (this); }, foo2: function () {console.log (this); setTimeOut (() => {console.log (this); // objeto {nombre: "qiutc"}}, 1000); }} obj.foo2 ();Como puede ver, en la función ejecutada por SetTimout, debería haberse imprimido en la ventana, pero esto apunta a OBJ aquí. La razón es que la función (parámetro) pasada a setTimeOut es una función de flecha:
Este objeto en el cuerpo de la función es el objeto que se define, no el objeto que se usa.
Basado en ejemplos, comprendamos esta oración:
Cuando se ejecuta obj.foo2 (), la actual esta apunta a OBJ; Al ejecutar SetTimout, primero definimos una función de flecha anónima, y el punto clave está aquí. El objeto en la función de flecha donde esto se ejecuta al definir esta función de flecha se apunta a este alcance al definir esta función de flecha, es decir, esto en obj.foo2, es decir, obj; Entonces, al ejecutar la función de flecha, es esto -> obj en obj.foo2 -> obj;
En pocas palabras, esto en la función de flecha solo está relacionado con esto en el alcance al definirlo, y no tiene nada que ver con dónde y cómo se llama. Al mismo tiempo, este punto es inmutable.
llamar, aplicar, atar
En JS, las funciones también son objetos, y también hay algunos métodos. Aquí presentamos tres métodos, pueden cambiar este puntero en la función:
llamar
Fun.call (Thatesarg [, arg1 [, arg2 [, ...]]])
Ejecutará la función de inmediato. El primer parámetro especifica el contexto de esto en la función de ejecución, y el parámetro posterior son los parámetros que deben aprobarse en la función de ejecución;
aplicar
Fun.apply (Thatesarg [, [arg1, arg2, ...]])
Ejecutará la función de inmediato. El primer parámetro especifica el contexto de esto en la función de ejecución, y el segundo parámetro es una matriz, que es el parámetro pasado a la función de ejecución (la diferencia de la llamada);
unir
var foo = divers.bind (thatesarg [, arg1 [, arg2 [, ...]]]);
No ejecuta la función, pero devuelve una nueva función. Esta nueva función especifica el contexto de esto, y los parámetros posteriores son los parámetros que deben aprobarse para ejecutar la función;
Estas tres funciones son realmente similares. El propósito general es especificar el contexto de una función (esto). Tomemos la función de llamada como un ejemplo;
Especifique esto para una función normal
var obj = {nombre: 'qiutc'}; function foo () {console.log (this);} foo.call (obj); // objeto {nombre: "qiutc"}Se puede ver que al ejecutar foo.call (obj), esto en la función apunta al objeto obj, que es exitoso;
Especificar un this para el método en el objeto
var obj = {nombre: 'Qiutc', foo: function () {console.log (this); }} var obj2 = {nombre: 'tcqiu222222'}; obj.foo.call (obj2); // objeto {nombre: "tcqiu222222"}Puede ver que al ejecutar la función, esto señala OBJ2, que es exitoso;
Especifique esto para el constructor
Función Persona (nombre) {this.name = name; console.log (this);} var obj = {nombre: 'qiutc22222222'}; var p = nueva persona.call (obj, 'qiutc'); // typeError no capturado: persona.call no es un constructor (...)Aquí se informó un error porque fuimos a una persona nueva. Función, no persona, y la función aquí no es un constructor;
Cambiar para atar e intentar:
Función Persona (nombre) {this.name = name; console.log (this);} var obj = {nombre: 'qiutc22222222'}; var persona2 = persona.bind (obj); var p = new Person2 ('qiutc'); // persona {name: "qiutc"} console.log (obj); // objeto {name: "Qiutc2222222"}}}Lo que se imprime es el objeto instanciado por la persona, que no tiene nada que ver con OBJ, y OBJ no ha cambiado, lo que indica que especificamos este contexto a la persona sin entrar en vigencia;
Por lo tanto, se puede concluir que el uso de enlaces para especificar esto para un constructor. Cuando el nuevo constructor, esto especificado por la función de enlace no entrará en vigencia;
Por supuesto, Bind no solo puede especificar esto, sino también pasar los parámetros. Probemos esta operación:
Función Persona (nombre) {this.name = name; console.log (this);} var obj = {nombre: 'qiutc22222222'}; var persona2 = persona.bind (obj, 'qiutc111111'); var p = nueva persona2 ('qiutc'); // persona {nombre: "qiutc11111111"}Como puede ver, aunque especificar esto no funciona, los parámetros entrantes aún funcionan;
Especifique esto para la función de flecha
Definamos una función de flecha bajo el global, por lo que esto en esta función de flecha inevitablemente apuntará al objeto global. ¿Qué pasa si esto se cambia usando el método de llamada?
var afoo = (a) => {console.log (a); console.log (this);} Afoo (1); // 1 // Windowvar obj = {nombre: 'Qiutc'}; Afoo.call (obj, 2); // WindowComo puede ver, la operación de la llamada que señala esto aquí no fue exitosa, por lo que se puede concluir que esto en la función de flecha ya ha decidido al definirla (ejecutar esto en el alcance que lo define) y no tiene nada que ver con cómo llamarlo y dónde llamarlo. No hay operaciones que incluyan (llamar, aplicar, BIND) y otras operaciones no pueden cambiar su esto.
Solo recuerde que la función de flecha es buena y esto permanece sin cambios.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.