Saltar enlaces

Pronto podremos finalmente desterrar JavaScript a ShadowRealm

Va a ser difícil ser coherente en esto. Está bien, ya veo. soy un Redactor técnico profesional. Cara positiva; todos los negocios. Ejem: si ha estado siguiendo el trabajo realizado por TC39 (el organismo de estándares responsable de mantener y desarrollar los estándares de JavaScript), es posible que haya encontrado algunas preguntas. Su trabajo reciente en ShadowRealmssnooker. ¡Lo siento! ¡Lo siento, estoy bien! Es simplemente, guau, qué gran nombre, “ShadowRealms”. Bien, espera, déjame empezar desde el principio. Quizás esto ayude.

Es muy probable que hayas visto JavaScript descrito como “de un solo subproceso” en algún momento; esto suele ocupar un lugar destacado en una lista de conceptos básicos de JavaScript, junto con “distingue entre mayúsculas y minúsculas”, “no distingue espacios en blanco” y “malo en matemáticas”. eso es correctoen un sentido estrictamente “informático”, pero todavía me molesta un poco cada vez que lo veo.

Quiero decir, JavaScript no es exactamente multiproceso. El script siempre se ejecuta de forma muy lineal: de arriba a abajo, de izquierda a derecha, un contexto de ejecución tras otro, finalizando la pila de llamadas y viceversa. Es solo que terminas aprendiendo algo como trabajador de internetque (para no ser demasiado específico al respecto) le permite ejecutar código JavaScript en otro hilo. Aquí es donde creo que el marco “JavaScript es de un solo subproceso” se vuelve menos útil, porque ni siquiera JavaScript es de subprocesos múltiples. idiomaun JavaScript solicitud Puede aprovechar el subproceso múltiple.

Es un marco mejor, y técnicamente preciso, para hablar JavaScript. campo Es de un solo hilo. Un dominio se refiere al entorno en el que se ejecuta el código: una pestaña del navegador es un dominio, y dentro de ese dominio hay un único hilo que ejecuta JavaScript. linea principal. Un trabajador web es un hilo de trabajo. JavaScript se ejecuta en todos los dominios iframe Correr Eso iframe El hilo principal del dominio. Por ejemplo, no podemos descargar la ejecución de una única función a otro hilo: JavaScript es sí mismo De un solo hilo, como lenguaje. Pero las aplicaciones JavaScript pueden abarcar múltiples dominios y utilizar múltiples subprocesos de ejecución, y cada dominio puede comunicarse con otros dominios de maneras específicas.

Cada dominio de JavaScript tiene su propio entorno global. En una pestaña del navegador, un objeto global es Window Lo mismo ocurre con las interfaces y las no homogéneas. iframe En esta pestaña del navegador, el objeto global es Window “Propiedad” de eso iframe:

<html>
  <head></head>
  <body>
    <iframe id="theIframe"></iframe>
  </body>

  <script>
  ( () => {
    console.log( window.globalThis );
    // Result: Window {}

    console.log( theIframe.contentWindow.globalThis );
    // Result: Window {}
  })();

  </script>
</html>

Estos no son mismo Objetos globales:

<html>
  <head></head>
  <body>
    <iframe id="theIframe"></iframe>
  </body>

  <script>
  ( () => {
    console.log( window.globalThis === theIframe.contentWindow.globalThis );
    // Result: false
  })();
  </script>
</html>

Páginas exteriores y páginas interiores. iframe Son dos campos independientes, ambos de un solo subproceso. Cada campo tiene sus propios objetos globales y sus propios objetos internos:

<html>
  <head></head>
  <body>
    <iframe id="theIframe"></iframe>
  </body>

  <script>
    (() => {
      console.log( window.Array );
      /* Result (expanded):
        function Array()
        from: function from()
        fromAsync: function fromAsync()
        isArray: function isArray()
        length: 1
        name: "Array"
        of: function of()
        prototype: Array ()
        Symbol(Symbol.species): undefined
        <prototype>: function ()
      */

      console.log( theIframe.contentWindow.Array );
      /* Result (expanded):
        function Array()
        from: function from()
        fromAsync: function fromAsync()
        isArray: function isArray()
        length: 1
        name: "Array"
        of: function of()
        prototype: Array ()
        Symbol(Symbol.species): undefined
        <prototype>: function ()
      */

      console.log( window.Array === theIframe.contentWindow.Array );
      // Result: false

    })();
  </script>
</html>

Entonces, como era de esperar, cualquier propiedad global definida en el contexto de un dominio no estará disponible en otro dominio:

<html>
  <head></head>
  <body>
    <iframe id="theIframe"></iframe>
  </body>

  <script>
  function globalFunction() {};

  console.log( window.globalFunction );
  // Result: function globalFunction()

  console.log( theIframe.contentWindow.globalFunction );
  // Result: undefined
  </script>
</html>

“No disponible” – o, según se mire, no disponible interferencia y un objeto global de otro reino. Si ha estado trabajando con JavaScript por algún tiempo, sabrá que no importa cuán meticulosamente administremos el alcance, el entorno global puede volverse bastante confuso a pesar de nuestros mejores esfuerzos. Por supuesto, parte de esto es culpa nuestra (las vinculaciones de variables perdidas nos suceden a todos), pero gran parte del desorden es el resultado de decisiones tempranas de diseño en el lenguaje mismo, como la declaración de función en el ejemplo anterior. Cuando se considera la asombrosa cantidad de JavaScript sobre el que no tenemos control y que se puede acumular en un proyecto promedio (desde marcos hasta bibliotecas de ayuda de terceros, polyfills, análisis de usuarios y anuncios), el potencial de conflicto es, por decir lo menos.

Dada la contaminación del alcance global que ha plagado el lenguaje desde tiempos inmemoriales (los años 90), no es difícil imaginar un caso de uso para descargar el código en un ámbito que pueda actuar como un espacio aislado para ejecutar JavaScript; no queremos impactar ni vernos afectados por nada que haya alterado el alcance global. Es posible que deseemos ejecutar partes del conjunto de pruebas en una “sala limpia” donde Actuación Las pruebas no interfieren potencialmente con los resultados de sus pruebas, ni los datos simulados entran en conflicto con los datos reales, ni ejecutan código donde queremos mantenernos alejados del dominio que contiene la aplicación JavaScript en sí, para evitar que las bibliotecas de terceros hagan lo mismo. necesidad Acceder al medio ambiente global no lo estropeará, pero no servirá de nada.

No podemos hacer esto con dominios, como son ahora; recuerde, JavaScript es de un solo subproceso, porque cada dominio campo Tiene un solo subproceso y la comunicación entre estos subprocesos es limitada. Es cierto que, al igual que con los casos de uso, no podemos reutilizar un dominio alternativo, ejecutar código en su único subproceso y luego entrelazar los resultados de esa ejecución nuevamente en el subproceso principal del dominio principal. Por definición, esta es una ejecución de subprocesos múltiples, que no solo viola la naturaleza fundamental de JavaScript, sino que permítanme decirlo de esta manera: JavaScript permite que se ejecuten múltiples subprocesos simultáneamente. Significa que nos traerá nuevos problemas..

Desinstalar el código de esta manera requiere un nuevo tipo Dominio: un dominio que tiene sus propios objetos globales e internos, pero No Tiene su propio subproceso: un dominio, y el código descargado en él aún se ejecutará en el subproceso principal del dominio que “posee” el script. Un oscuro reflejo de nuestro propio reino; ¡Un reino al que la luz nunca podrá llegar, donde sólo pueden habitar las sombras fugaces de nuestros códigos exiliados! Imagínese un trueno a lo lejos; tal vez también me imagines usando una capa, tal vez dejo caer mi copa de vino al suelo. Ya sabes, diviértete. ¿Cómo no pudiste? Es decir, se llaman:

Tierras Sombrías

propuesto API del Reino de las Sombras introdujo un especial aislamientonada más. ShadowRealm puede No Tener su propio contexto de ejecución: el código descargado a ShadowRealm existirá en un pseudo-reino con sus propios objetos globales e integrados. El código continúa ejecutándose en el mismo hilo que el código que creó ShadowRealm; No estamos obligados a comunicarnos de un lado a otro ni a compartir recursos entre dos hilos separados de forma limitada. En resumen, el script se ejecuta de la misma manera que si estuviera restringido a un solo ámbito, pero está aislado de los elementos intrínsecos, API, objetos globales y cualquier otra cosa que nuestro script posea de ese ámbito externo. completo a ese objeto global.

Esto suena complicado, pero la API sugerida sería muy sencilla en la práctica:

// Create a ShadowRealm:
const shadow = new ShadowRealm();

function globalFunction() {};

console.log( globalthis.globalFunction );
// Result: function globalFunction()

// Evaluate `globalThis.globalFunction` inside the ShadowRealm:
console.log( shadow.evaluate( 'globalThis.globalFunction' ) );
// Result: undefined

notas: Tenga en cuenta que este código aún es teórico: aún no existe en el estándar ES-262 ni en los navegadores.

globalFunction Como vimos antes, está definido en el objeto global fuera del reino, pero no está definido en el objeto global dentro del recién creado ShadowRealm; no importa lo que hagamos, el objeto global de ShadowRealm permanece en su estado original. externo es. Por supuesto, lo contrario también es cierto:

// Create a ShadowRealm:
const shadow = new ShadowRealm();

// Declare a global function inside the ShadowRealm:
shadow.evaluate( 'function globalFunction() {};' );

// It doesn't exist in the outer realm's global object:
console.log( globalthis.globalFunction );
// Result: undefined

// But when we evaluate `globalThis.globalFunction` inside the ShadowRealm:
console.log( shadow.evaluate( 'globalThis.globalFunction' ) );
// Result: function globalFunction()

Hemos declarado la función en ShadowRealm y podemos llamarla haciendo referencia a la variable del objeto ShadowRealm. Esta función permanece aislada del objeto global externo y de cualquier otro objeto ShadowRealm:

// Create a ShadowRealm:
const firstShadow = new ShadowRealm();
const secondShadow = new ShadowRealm();

// Declare a global function inside the ShadowRealm referenced by `secondShadow`:
secondShadow.evaluate( 'function globalFunction() {};' );

// It doesn't exist in the outer realm's global object:
console.log( globalthis.globalFunction );
// Result: undefined

// It doesn't exist in the global object of the ShadowRealm referencd by `firstShadow`:
console.log( firstShadow.evaluate( 'globalThis.globalFunction' ) );
// Result: undefined

// It only exists within the ShadowRealm referenced by `secondShadow`:
console.log( secondShadow.evaluate( 'globalThis.globalFunction' ) );
// Result: function globalFunction()

Es decir, “aislado” hasta cierto punto. ShadowRealm no proporciona un verdadero límite de seguridad, porque el código ejecutado dentro de ShadowRealm aún puede hacer inferencias sobre el código ejecutado en otros ámbitos. a ellos capaz considerado ser vertical Los límites, el código que se ejecuta dentro de un ShadowRealm no pueden interferir directamente con otro reino, a menos que, por supuesto, lo permitamos. Aunque el código descargado en ShadowRealm no interfiere con los objetos fuera de él, todavía somos libres de usar los resultados de estas operaciones tal como lo haríamos con los resultados de las mismas operaciones en el reino anfitrión:

// Create a ShadowRealm:
const shadow = new ShadowRealm();

// Create a binding that calls a function inside the ShadowRealm:
const shadowFunction = shadow.evaluate( '( value ) => globalThis.someValue = value );

// ...and call our wrapped function using that binding:
shadowFunction( "Hello from the ShadowRealm!" );

// Executing this function in the host realm doesn't _change_ anything here, of course:
console.log( globalThis.someValue );
// Result: undefined

// But we can grab the result from the ShadowRealm:
const shadowValue = shadow.evaluate( 'globalThis.someValue' );

// And use it here in the host realm:
console.log( shadowValue );
// Result: Hello from the ShadowRealm!

¡Salas limpias desechables ilimitadas! Dimensión de bolsillo, podemos ejecutar cualquier código que queramos sin preocuparnos de que ese código interfiera con el alcance de cualquier otro ShadowRealm. o El reino exterior: el “reino de la luz”, por así decirlo.

Ahora, algunos de ustedes, especialmente aquellos que han estado haciendo esto desde los primeros días de JavaScript, pueden sentirse ofendidos por estos ejemplos. Es comprensible si crees que la API de ShadowRealm es simplemente gótica. evalno estarías completamente equivocado: además de ejecutarse en el contexto de ShadowRealm, lo que has visto aquí hasta ahora son básicamente llamadas indirectas. eval ——Incluso si está sujeto a las mismas limitaciones unsafe-eval Política de seguridad de contenido regla.

Sin embargo, no se preocupe por su flujo de trabajo: si bien estos son ilustrativo Por ejemplo, esta no es la única forma de utilizar ShadowRealms. La propuesta incluye importValue Método en el prototipo del objeto ShadowRealm que le permite importar dinámicamente un módulo y luego recuperar y usar los valores y funciones exportados:

// spookycode.js
export function greeting() {
 return "Hello from the ShadowRealm!";
}
async function shadowGreeter() {
  // I INVOKE THE DARK POWER OF THE SHADOWREALM- ahem. Sorry.
  const shadow = new ShadowRealm();

  /* 
  * `importValue` returns a promise that resolves with the value of the function 
  * specified in the second argument: 
  */
  const shadowGreet = await shadow.importValue( "./spookycode.js", "greeting" );

  // Call our wrapped function, annnnd...
  shadowGreet();
}

shadowGreeter();
// Result: Hello from the ShadowRealm!

La sombra aún no ha caído

Me alegra decir que ahora has visto en general En este punto, hemos aprendido sobre la API ShadowRealms propuesta. La propuesta solo incluye los dos métodos que ve aquí: evaluate y importValue — Dos maneras expulsar Evaluar el código dentro del contexto de una instancia de ShadowRealm mientras aún implementar Este código está en el contexto del hilo del dominio host.

Pero repito: ninguno de estos está listo para su uso todavía. La especificación propuesta se encuentra actualmente en Etapa 2.7 — “Aprobado en principio y en proceso de verificación”, lo que significa que puede cambiar únicamente en función de los comentarios (si los hay) de las pruebas y las implementaciones de prueba en los navegadores. Al leer este artículo, avanzarás. Cuando la propuesta llegue a la Fase 3 y comencemos a ver su implementación en los navegadores, estará listo para probarla usted mismo. No solo eso, cuando el aterrador poder de ShadowRealm se desate en la web, ¡estarás listo para comandar a su oscuro y aterrador señor! El mundo en el que se basa nuestro código se verá sacudido porque——Está bien, está bien, lo siento. Verás, ¡no puedo soportarlo más! Quiero decir, “Reino de las Sombras”, por llorar a gritos.

Home
Account
Cart
Search
¡Hola! ¡Pregúntame lo que quieras!
Explore
Drag