Skip to content

08. Cómo funciona JavaScript

Carlos Jaramillo edited this page Oct 21, 2019 · 9 revisions

¿Como llegá un script al navegador?

El navegador interpreta el archivo HTML y cuando termina de transformarlo al DOM (Document Object Model - representación que hace el navegador de un documento HTML) se dispara el evento DOMContentLoaded el cual indica que el documento está disponible para ser manipulado. Todo script que carguemos en nuestra página tiene un llamado y una ejecución.

  • async Realiza la petición de forma asíncrona. Pero cuando esta acaba, detiene la carga del DOM para ejecutar el script.
  • defer Realiza la petición de forma asíncrona. Pero ejecuta el script cuando ya se cargó todo el documento
  • scripts embebidos: el browser carga linea a linea, cuando encuentra un código entre scripts detiene la carga para ejecutar el script.

Hay que tener en cuenta que cuando carga una página y se encuentra un script a ejecutar toda la carga se detiene. Por eso se recomienda agregar tus scripts justo antes de cerrar el body para que todo el documento esté disponible.

¿Qué hace un JS Engine?

  • Recibe código fuente
  • Parsea el código y produce un Abstract Syntax Tree (AST)
  • Se compila a bytecode y empieza a ejecutarse
  • Se optimiza a machine code y se reemplaza el código base
-> JavaScript Source Code  - to:
  -> Parser  - to:
    -> Abstract Syntax Tree  - to:
            --Optimize--
-> Interpreter  - to:
  -> bytecode  - through 'Profiling data procesor' to:        //In Bytecode start to run
-> Optimizing Compiler  - to:
  -> Optimized code //Generate Machine Code

Parsers (Descomponer)

-> Código fuente
  -> Tokens //Parser
    -> Abstract Syntax Tree

Un SyntaxError es lanzado cuando el motor de javascript se encuentra con parte de código que no forman parte de la sintaxis del lenguaje al momento de analizar código (parser).

Notas google:

  • Parsing es 15-25% del proceso de ejecución
  • La mayoría del JS en una página nunca se ejecuta entonces el Bundling y Code Splitting son muy importantes

Parser de V8

Eager Parsing:

  • Encuentra errores de sintaxis
  • Crea el AST
  • Construye scopes

Lazy Parsing:

  • Doble de rápido que el eager parser
  • No crea el AST
  • Construye los scopes parcialmente

Tokens

Aquí o Aquí

var helloWorld = "Hello" + " World";
var answer = 6 * 7;
[
    {
        "type": "Keyword",
        "value": "var"
    },
    {
        "type": "Identifier",
        "value": "helloWorld"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "String",
        "value": "\"Hello\""
    },
    {
        "type": "Punctuator",
        "value": "+"
    },
    {
        "type": "String",
        "value": "\" World\""
    },
    {
        "type": "Punctuator",
        "value": ";"
    },
    {
        "type": "Keyword",
        "value": "var"
    },
    {
        "type": "Identifier",
        "value": "answer"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "Numeric",
        "value": "6"
    },
    {
        "type": "Punctuator",
        "value": "*"
    },
    {
        "type": "Numeric",
        "value": "7"
    },
    {
        "type": "Punctuator",
        "value": ";"
    }
]

Abstract Syntax Tree (AST)

El AST es un gráfo (estructura en forma de árbol). Donde vamos a tener una raíz que será nuestro programa y lo vamos a ir descomponiendo en partes, todo esto lo vamos a poder hacer siguiendo los tokens que produce el parser, esto se usa en muchas cosas, no solo para ejecutar un programa javascript, también lo usamos para transformar código de una forma a otra que es como lo que hace babel o prettier. Pruebalo Aquí

Se usa en:

  • Javascript Engine
  • Bundlers: Webpack, Rollup, Parcel
  • Transpilers: Babel
  • Linters: ESLint, Prettify
  • Type Checkers: TypeScript, Flow
  • Syntax Highlighters

Crear regla eslint:

const pi = 3.1415;
const halft_pi = 1.356;
// Variables constantes
// Variables que guarden un número 
// El nombre de la variable tiene que estar en UPPER CASE
export default function (context) {
    return {
        VariableDeclaration(node) {
            // Tipo de variable const
            // asegurarnos que el valor es un número  
            if (node.kind === "const" && typeof declaration.init.value === "number") {
                const declaration = node.declarations[0];
                if (declaration.id.name !== declaration.id.name.toUpperCase()) {
                    context.report({
                        node: declaration.id,
                        message: "El nombre de la constante debe estar en Mayúsculas",
                        //Opcional, autoarreglar el error
                        fix: function (fixer) {
                            return fixer.replaceText(declaration.id,
                                declaration.id.name.toUpperCase());
                        }
                    })
                }
            }
        }
    }
};

Qué hace un JS Engine

  • Recibe código fuente
  • Parsea el código y produce un Abstract Syntax Tree (AST)
  • Se compila a bytecode y se ejecuta
  • Se optimiza a machine code y se reemplaza el código base

Bytecode

  • Código parecido a assembly
  • Portatil
  • Ejecutado por una virtual machine

Machine Code

  • Binario
  • Instrucciones especificas a una arquitectura o procesador

Para aprovechar los motores de JavaScript:

  • Las funciones reciban los mismos tipos de datos
  • Las estructuras de datos (Arreglos y Objectos) mantengan la misma forma.

Así el motor de JS podrá optimizar el código que se ejecuta constantemente enviándolo de ByteCode a OptimizedCode.

function add(a, b) {
  return a + b;
}

for (let i = 0; i < 1000; i++) {
  add(i, i);
}

Se ira ejecutando el código hasta que esté preparado para ser optimizado, convirtiéndose en una Hot Function.

add(1,"hola");

Romperá la optimización y seguira ejecutando bytecode

Motor de diferentes navegadores

Event Loop

Hace que js parezca ser multi-hilo a pesar de que corre en un solo proceso.

Js se organiza usando las siguientes estructuras de datos:

  • Stack (apilar): Apila de forma organizada las diferentes instrucciones que se llaman. Lleva así un rastro de dónde está el programa, en que punto de ejecución nos encontramos.
  • Memory Heap: Guarda información de las variables y del scope de manera desorganizada.
  • Task Queue (cola de tareas): Aquí se agregan las tares que ya están listas para pasar al stack y ser ejecutadas. El stack debe estar vacío para que esto suceda.
  • Schedule Tasks (tareas programadas): Aquí se agregan a la cola, las tareas programadas para su ejecución.
  • MicroTask Queue: Aquí se agregan las promesas. Esta Queue es la que tiene mayor prioridad.

El Event Loop es un loop que está ejecutando todo el tiempo y pasa periódicamente revisando las queues y el stack moviendo tareas entre estas dos estructuras.

function moreAsync() {
  console.log('start');
  setTimeout(() => console.log('setTimeout'), 0);
  Promise.resolve('Promise 1').then(msg => console.log(msg));
  Promise.resolve('Promise 2').then(msg => console.log(msg));
  console.log('end');
}
moreAsync();

Como es en pila, se saldrá el moreAsync del Stack y luego el anonymous, pasará el anonymous(del timeout) a task queue, promise 1 pasará al stack, luego promise2, y luego anonymous(task queue)

Clone this wiki locally