De todos modos, siempre que la actualización no se reproduzca después de un error de JavaScript, el usuario puede resolver el problema refrescante, y el navegador no se bloqueará, y estará bien si no ha sucedido. Esta suposición era cierta antes de que la aplicación de una sola página se hiciera popular. La aplicación actual de una sola página es extremadamente compleja después de ejecutarse durante un período de tiempo. ¿No volvería a trabajar por completo las operaciones anteriores? Por lo tanto, todavía es necesario que captemos y analicemos esta información de excepción, y luego podemos modificar el código para evitar afectar la experiencia del usuario.
Cómo atrapar excepciones Nos escribimos a nosotros mismos throw new Error() que ciertamente podemos capturar si queremos capturar, porque sabemos muy bien dónde se escribe throw . Sin embargo, las excepciones que ocurren al llamar a la API del navegador no son necesariamente tan fáciles de atrapar. Para el primero también podemos atraparlo a través de try-catch , para el segundo debemos escuchar las excepciones globales y luego atraparlo.
Si se sabe que algunas API del navegador arrojan excepciones, debemos poner la llamada en try-catch para evitar que todo el programa ingrese a un estado ilegal debido a errores. Por ejemplo, window.localStorage es una API así.
try {localStorage.SetItem ('date', date.now ());
} capt (error) {
reporterRor (error);
}
Otro escenario try-catch común aplicable son las devoluciones de llamada. Debido a que el código de la función de devolución de llamada es incontrolable, no sabemos qué tan bueno es el código y si se llamarán a otras API que lanzan excepciones. Para no hacer que se ejecuten otros códigos después de llamar a la devolución de llamada debido a los errores de devolución de llamada, es necesario volver a poner la llamada en try-catch .
listeners.forEach(function(listener) {intentar {
oyente();
} capt (error) {
reporterRor (error);
}
});
Para los lugares que try-catch no puede cubrir, si se produce una excepción, solo se puede atrapar a través de window.onerror .
window.onerror =function (errorMessage, scripturi, lino number) {
ReporterRor ({
Mensaje: ErrorMessage,
Script: Scripturi,
Línea: Linenumber
});
}
Tenga cuidado de no window.onerror inteligente y window.attachEvent window.addEventListener . Muchos navegadores solo implementan window.onerror . Animán, o solo la implementación window.onerror . Teniendo en cuenta que el borrador estándar window.onerror define window.onerror .
Supongamos que tenemos una función reportError para recopilar excepciones atrapadas y luego enviarlas al almacenamiento del lado del servidor en lotes para consulta y análisis, ¿qué información queremos recopilar? Más información útil incluye: Tipo de error ( name ), mensaje de error ( message ), dirección de archivo de script ( script ), número de línea ( line ), número de columna ( column ) y traza de pila ( stack ). Si se atrapa una excepción a través de try-catch , toda esta información está en el objeto Error (compatible con los navegadores convencionales), por lo que reportError también puede recopilar esta información. Pero si se captura a través de window.onerror , todos sabemos que esta función del evento tiene solo 3 parámetros, por lo que se pierde la información inesperada de estos 3 parámetros.
Si el objeto Error es creado por nosotros mismos, entonces error.message es controlado por nosotros. Básicamente, lo que message en error.message window.onerror (El navegador realmente se modificará ligeramente, como agregar 'Uncaught Error: ' prefijo). Por lo tanto, podemos serializar los atributos que nos preocupamos (como JSON.Stringify ) y almacenarlos en error.message , y luego leerlos ellos en window.onerror Por supuesto, esto se limita a objetos Error que creamos nosotros mismos.
Los fabricantes de navegadores también conocen las restricciones a las que las personas están sujetas cuando usan window.onerror y comienzan a agregar nuevos parámetros a window.onerror . Teniendo en cuenta que solo los números de fila y ningún número de columna parecen ser muy simétricos, es decir, primero agregó los números de columna y los colocó en el cuarto parámetro. Sin embargo, lo que todos están más preocupados es si pueden obtener la pila completa, por lo que Firefox dijo que sería mejor poner la pila en el quinto parámetro. Pero Chrome dijo que sería mejor poner todo el objeto Error en el quinto parámetro, y que puede leer cualquier atributo, incluidos atributos personalizados. Como resultado, Chrome se mueve más rápido, una nueva window.onerror Se implementa la firma de Anderror en Chrome 30, lo que resulta en la siguiente escritura del borrador estándar.
Regularidad de los atributoswindow.onerror = function(Errormessage,
SIRTURI,
Lino,
columnnumber,
error
) {
if (error) {
reporterRor (error);
} demás {
ReporterRor ({
Mensaje: ErrorMessage,
Script: Scripturi,
Línea: Linenumber,
Columna: Number de columna
});
}
}
Los nombres de los atributos del objeto script Error que discutimos anteriormente filename basan en los métodos Error nombres de Chrome. Por lo tanto, también necesitamos una función especial para normalizar el objeto Error , es decir, para asignar diferentes nombres de atributos a un nombre de atributo unificado. Para prácticas específicas, consulte este artículo. Aunque la implementación del navegador se actualizará, no será demasiado difícil para los humanos mantener dicha tabla de mapeo.
Similar es el formato de stack Trace. Esta propiedad guarda la información de la pila de una excepción cuando ocurre en forma de texto plano. . Nombre ( identifier ), archivo ( script ), número de línea ( line ) y número de columna ( column ).
Si también ha encontrado un error con el mensaje 'Script error.' La razón de esta restricción de seguridad es la siguiente: suponga que el HTML devuelto por un banquero en línea después de iniciar sesión es diferente del HTML visto por un usuario anónimo, un sitio web de terceros puede poner el URI de este banco en línea en script.src atributo script.src . Por supuesto, HTML no puede analizarse como JS, por lo que el navegador organizará una excepción, y este sitio web de terceros puede determinar si el usuario ha iniciado sesión analizando la ubicación de la excepción. Por esta razón, el navegador filtra todas las excepciones lanzadas por diferentes archivos de script de origen, dejando solo un mensaje sin cambios como 'Script error.'
Para los sitios web de una determinada escala, es normal que los archivos de script se coloquen en CDN y se colocan diferentes fuentes. Ahora, incluso si crea un pequeño sitio web usted mismo, los marcos comunes como JQuery y Backbone pueden hacer referencia directamente a la versión en el CDN público para acelerar las descargas de usuarios. Por lo tanto, esta restricción de seguridad causa algunos problemas, lo que hace que la información de excepción que recopilamos de Chrome y Firefox sea 'Script error.'
Si desea evitar esta restricción, solo asegúrese de que el archivo de script y la página en sí sean los mismos. ¿Pero no colocar los archivos de script en servidores que no están acelerados por CDN reducir la velocidad de descarga del usuario? Una solución es continuar colocando el archivo de script en el CDN, usar XMLHttpRequest para descargar el contenido a través de CORS y luego crear una etiqueta <script> para inyectarlo en la página. El código integrado en la página es, por supuesto, el mismo origen.
Esto es fácil de decir, pero hay muchos detalles para implementar. Para dar un ejemplo simple:
<script src="http://cdn.com/step1.js"></script><script>
(función step2 () {}) ();
</script>
<script src = "http://cdn.com/step3.js"> </script>
Todos sabemos que si hay dependencias en el paso 1, el paso 2 y el paso 3, debe ejecutarse estrictamente en este orden, de lo contrario puede ocurrir un error. El navegador puede solicitar archivos Step1 y Step3 en paralelo, pero el pedido se garantiza cuando se ejecuta. Si obtenemos el contenido del archivo del paso 1 y el paso 3 utilizando XMLHttpRequest , debemos garantizar el orden correcto propio. Además, no olvide el paso2.
Si ya tenemos un conjunto completo de herramientas para generar etiquetas <script> para diferentes páginas en el sitio web, necesitamos ajustar este conjunto de herramientas para hacer cambios en <script> etiquetas:
<script>SchedulerEmotescript ('http://cdn.com/step1.js');
</script>
<script>
programarInlinescript (Function Code () {
(función step2 () {}) ();
});
</script>
<script>
SchedulerEmotescript ('http://cdn.com/step3.js');
</script>
Necesitamos implementar las dos funciones de scheduleRemoteScript y scheduleInlineScript , y asegurarnos de que se definan antes de la primera etiqueta <script> que hace referencia al archivo de script externo, y luego las etiquetas <script> restantes se reescribirán en el formulario anterior. Tenga en cuenta que la función step2 que se ejecutó inmediatamente se colocó en una función code más grande. La función code no se ejecutará, es solo un contenedor, de modo que el código de paso 2 original se pueda retener sin escapar, pero no se ejecutará de inmediato.
A continuación, necesitamos implementar un mecanismo completo para garantizar que el contenido del archivo descargado por scheduleRemoteScript en función de la dirección y el código obtenido directamente por scheduleInlineScript se pueda ejecutar uno por uno en el orden correcto. No daré el código detallado aquí.
Obtener contenido a través de CORS e inyectar código en la página puede romper las restricciones de seguridad, pero introducirá un nuevo problema, es decir, conflictos de números de línea. Originalmente, el archivo de script único podría ubicarse a través de error.script , y luego el número de línea único podría ubicarse a través de error.line . Ahora, dado que <script> son códigos integrados en la página, múltiples etiquetas <script> no se pueden distinguir por error.script . .
Para evitar conflictos de números de línea, podemos desperdiciar algunos números de línea para que los intervalos de número de línea utilizados por el código real en cada etiqueta <script> no se superpongan entre sí. Por ejemplo, suponiendo que el código real en cada etiqueta <script> no exceda las 1000 líneas, luego puedo dejar que el código en la primera etiqueta <script> tome la línea 11000 y dejar que la segunda etiqueta <script> el código en el código Ocupa la línea 10012000 (1000 línea vacía insertada antes de insertar), el código de la tercera etiqueta <script> ocupa la línea 20013000 (línea vacía 2000 insertada antes de insertar), y así sucesivamente. Luego usamos el atributo data-* para registrar esta información para una fácil verificación de retroceso.
<scriptdata-src = "http://cdn.com/step1.js"
data-line-start = "1"
>
// código para el paso 1
</script>
<script data-line-start = "1001">
// '/n' * 1000
// código para el paso 2
</script>
<guión
data-src = "http://cdn.com/step3.js"
data-line-start = "2001"
>
// '/n' * 2000
// código para el paso 3
</script>
Después de este procesamiento, 5 un error.line error es 3005 , significa que el error.script 'http://cdn.com/step3.js' error.line Podemos completar esta verificación inversa del número de línea en la función reportError mencionada anteriormente.
Por supuesto, dado que no podemos garantizar que cada archivo de script tenga solo 1000 líneas, también es posible que algunos archivos de script sean significativamente inferiores a 1000 líneas, por lo que no hay necesidad de asignar fijamente 1000 líneas a cada etiqueta <script> . Podemos asignar intervalos basados en el número real de líneas de script, solo asegurarnos de que los intervalos utilizados por cada etiqueta <script> no se superpongan.
Las restricciones de seguridad impuestas por los navegadores en el contenido de diferentes fuentes no se limitan a la etiqueta <script> . Dado que XMLHttpRequest puede romper esta limitación a través de CORS, ¿por qué los recursos se hacen referencia directamente a través de etiquetas no permitidas? Esto ciertamente está bien.
La limitación de referirse a diferentes archivos de script de origen para etiquetas <script> también se aplica a referirse a diferentes archivos de imagen de origen para etiquetas <img> . Si una etiqueta <img> es una fuente diferente, una vez que se usa al dibujar <canvas> , <canvas> se convertirá en un estado de solo escritura, asegurando que el sitio web no pueda robar datos de imágenes no autorizados de diferentes fuentes a través de JavaScript. Más tarde, la etiqueta <img> resolvió este problema al introducir el atributo crossorigin . Si se usa crossorigin="anonymous" , es equivalente a Cors anónimo;
Dado que la etiqueta <img> puede hacer esto, ¿por qué la etiqueta <script> no puede hacer esto? Por lo tanto, el fabricante del navegador agregó el mismo atributo crossorigin a la etiqueta <script> para resolver las restricciones de seguridad anteriores. Ahora el soporte de Chrome y Firefox para esta propiedad es completamente gratuito. Safari tratará crossorigin="anonymous" como crossorigin="use-credentials" , y el resultado es que si el servidor solo admite CORS anónimo, Safari tratará la autenticación como falla. Debido a que el servidor CDN está diseñado para devolver solo el contenido estático por razones de rendimiento, es imposible devolver dinámicamente el encabezado HTTP requerido para autenticar CORS en función de las solicitudes.
El manejo de excepciones de JavaScript se ve simple y no es diferente de otros idiomas, pero no es tan fácil atrapar todas las excepciones y analizar las propiedades. Aunque algunos servicios de terceros ahora proporcionan servicios de Google Analytics que capturan excepciones de JavaScript, si desea comprender los detalles y principios, debe hacerlo usted mismo.