esto y ScopeChain en EcmaScript

Hola Habr!

En un art铆culo anterior, examinamos la teor铆a general de OOP aplicada a EcmaScript y la falacia popular de los desarrolladores novatos con respecto a las diferencias entre OOP en JS y lenguajes cl谩sicos.

Hoy hablaremos de otros dos conceptos EcmaScript igualmente importantes, a saber, la relaci贸n de la entidad con el contexto de ejecuci贸n ( esta es la conexi贸n) y la relaci贸n de la entidad con el contexto generador ( ScopeChain ).

隆Entonces comencemos!

esto


En las entrevistas en respuesta a la pregunta: "Cu茅ntanos m谩s sobre esto ". Los desarrolladores principiantes, por regla general, dan respuestas muy vagas: " este es el objeto" antes del punto "que se us贸 para llamar al m茅todo", " este es el contexto en el que se llam贸 a la funci贸n", etc.

De hecho, la situaci贸n con este concepto, que es central para EcmaScript, es algo m谩s complicada. Vamos a resolverlo en orden.

Digamos que tenemos un programa JavaScript que tiene variables declaradas globalmente; funciones globales; funciones locales (declaradas dentro de otras funciones), funciones devueltas de funciones.

const a = 10; const b = 20; const x = { a: 15, b: 25, } function foo(){ return this.a + this.b; } function bar () { const a = 30; return a + b; } function fooBaz(){ function test () { return this.a + this.b; } return test(); } function fooBar() { const a = 40; const b = 50; return function () { return a + b; } } fooBar()(); 

Al transferir el control al c贸digo ejecutable, se realiza una entrada en el contexto de ejecuci贸n. C贸digo ejecutable: este es cualquier c贸digo que ejecutamos en un momento dado, puede ser un c贸digo global o un c贸digo de cualquier funci贸n.

El contexto de ejecuci贸n es una abstracci贸n que tipifica y delimita el c贸digo. Desde el punto de vista de esta abstracci贸n, el c贸digo se divide en global (cualquier secuencia de comandos conectada, secuencias de comandos en l铆nea) y c贸digo de funci贸n (el c贸digo de las funciones anidadas no pertenece al contexto de las funciones principales).

Hay un tercer tipo: EvalCode. En este art铆culo, lo descuidamos.

L贸gicamente, el conjunto de contextos de ejecuci贸n es una pila que funciona seg煤n el principio de 煤ltimo en entrar, primero en salir (lifo). La parte inferior de la pila es siempre el contexto global, y la parte superior es el ejecutable actual. Cada vez que se llama a una funci贸n, se realiza una entrada en su contexto. Cuando una funci贸n se completa, su contexto termina. Los contextos gastados se eliminan de la pila de forma secuencial y en orden inverso.

Echa un vistazo al c贸digo de arriba. Tenemos una llamada a la funci贸n fooBar en c贸digo global. En la funci贸n fooBar, devolvemos una funci贸n an贸nima que llamamos inmediatamente. Los siguientes cambios ocurren con la pila: un contexto global entra en 茅l - cuando se llama a fooBar, su contexto se pone en la pila - se termina fooBar , devuelve una funci贸n an贸nima y se elimina de la pila - se llama a una funci贸n an贸nima , su contexto se pone en la pila - una funci贸n an贸nima cumple, devuelve un valor y su contexto se elimina de la pila: al final del script, el contexto global se elimina de la pila.

El contexto de ejecuci贸n se puede representar condicionalmente como un objeto. Una de las propiedades de este objeto ser谩 el entorno l茅xico (LO).

El entorno l茅xico contiene:

  • todas las declaraciones de variables de contexto
  • todas las declaraciones de funciones
  • todos los par谩metros formales de la funci贸n (si estamos hablando del contexto de funciones)

Al ingresar al contexto de ejecuci贸n, el int茅rprete escanea el contexto. Todas las declaraciones de variables y declaraciones de funciones se elevan hasta el comienzo del contexto. Las variables se crean de forma indefinida y las funciones est谩n completamente listas para usar.

隆Esto tambi茅n es una propiedad del contexto de ejecuci贸n, pero no del contexto en s铆 mismo, como responden algunos entrevistadores novatos! esto se define al ingresar al contexto y permanece sin cambios hasta el final de la vida 煤til del contexto (hasta que el contexto se elimine de la pila).

En el contexto de ejecuci贸n global, esto est谩 determinado por el modo estricto : cuando el modo estricto est谩 desactivado, contiene un objeto global (en el navegador se aproxima al nivel superior en el objeto de la ventana), con 'uso estricto' no est谩 definido.

esto en el contexto de funciones: 隆la pregunta es mucho m谩s interesante!
esto en funciones est谩 determinado por la persona que llama y depende de la sintaxis de la llamada. Por ejemplo, como sabemos, hay m茅todos que permiten que esto se arregle r铆gidamente cuando se llama ( llamar , aplicar ) y un m茅todo que le permite crear un contenedor con "arreglado esto" ( enlace ). En estas situaciones, declaramos esto expl铆citamente y no puede haber ninguna duda sobre su definici贸n.

Con una llamada de funci贸n normal, 隆la situaci贸n es mucho m谩s complicada!

Uno de los tipos integrados de EcmaScript, ReferenceType , nos ayudar谩 a comprender c贸mo se fija esto en las funciones. Este es uno de los tipos internos disponibles a nivel de implementaci贸n. L贸gicamente, es un objeto con dos propiedades base (una referencia a un determinado objeto base para el que se devuelve un ReferenceType), propertyName (una representaci贸n de cadena del identificador del objeto para el que se devuelve un ReferenceType).

ReferenceType se devuelve para todas las declaraciones de variables, declaraciones de funciones y referencias de propiedades (este es el caso que nos interesa desde el punto de vista de entender esto).

La regla para definir esto para funciones se llama de la manera habitual:
Si ReferenceType est谩 a la izquierda de los corchetes de activaci贸n de la funci贸n, la base de este ReferenceType se coloca en this funci贸n. Si cualquier otro tipo est谩 a la izquierda de los corchetes, entonces this es un objeto global o undefined (en realidad null , pero dado que nulo no tiene un valor espec铆fico desde el punto de vista de ecmascript, entonces se convierte en un objeto global, una referencia a la cual es igual a undefined dependiendo del modo estricto).

Veamos un ejemplo:

 const x = 0; const obj = { x: 10, foo: function() { return this.x; } } obj.foo();//  10 ..    ReferenceType  base     obj const test = obj.foo;//       test();//  0 ..  test()   .test(),..  base    ,       0. 

Creo que el m茅todo de definici贸n se ilustra claramente. Ahora considere algunos casos menos obvios.

Expresiones Funcionales


Volvamos a nuestro ReferenceType por un segundo. Este tipo tiene un m茅todo GetValue incorporado que devuelve el tipo verdadero del objeto recibido a trav茅s de ReferenceType. En la zona de expresi贸n, GetValue siempre se activa.

Un ejemplo:

 (function (){ return this;// this     undefined    strict mode })() 

Esto se debe al hecho de que GetValue siempre se activa en la zona de expresi贸n. GetValue devuelve un tipo de funci贸n, y a la izquierda de los corchetes de activaci贸n no hay un tipo de referencia. Recuerde nuestra regla para determinar esto : si cualquier otro tipo est谩 a la izquierda de los corchetes, entonces un objeto global se coloca en this o undefined (en realidad null , pero dado que nulo no tiene un cierto valor desde el punto de vista de ecmascript, entonces se convierte en un objeto global , el enlace al cual puede ser igual a indefinido dependiendo del modo estricto) .

Las zonas de expresi贸n son: asignaci贸n (=), operadores || u otros operadores l贸gicos, operador ternario, inicializador de matriz, lista separada por comas.

 const x = 0; const obj = { x: 10, foo: function() { return this.x; } } obj.foo(); //        //  ? (obj.foo)(); // ,    , GetValue   // ? (obj.foo = obj.foo)(); //        GetValue,     Fuction,   ReferenceType,   0   (   this) //  ||    ,    ..? (obj.foo || obj.foo)();// 0    ,     //  [obj.foo][0]();// 0    ,     // .. 

Situaci贸n id茅ntica en expresiones funcionales con nombre. Incluso con una llamada recursiva a este objeto global o undefined

esto anida funciones llamadas en el padre


Tambi茅n una situaci贸n importante!

 const x = 0; function foo() { function bar(){ return this.x; } return bar(); } const obj = {x:10}; obj.test = foo; obj.test();// undefined 

Esto se debe a que la llamada a bar() equivalente a la llamada a LE_foo.bar , y el objeto del entorno l茅xico LE_foo.bar indefinido como este.

Funciones de constructor


Como escrib铆 arriba:
esto en funciones est谩 determinado por la persona que llama y depende de la sintaxis de la llamada.

Invocamos funciones de constructor utilizando la nueva palabra clave. La peculiaridad de este m茅todo de activaci贸n de funciones es que se llama al m茅todo de funci贸n interna [[construcci贸n]] , que realiza ciertas operaciones (隆el mecanismo para crear entidades por parte de los dise帽adores se discutir谩 en el segundo o tercer art铆culo sobre OOP!) Y llama al m茅todo interno [[llamada]] , que anula en esta instancia creada de la funci贸n constructora.

Cadena de alcance


La cadena de alcance tambi茅n es una propiedad del contexto de ejecuci贸n como este. Es una lista de objetos de los entornos l茅xicos del contexto actual y todos los contextos generadores. Es en esta cadena que la b煤squeda de variables se produce al resolver nombres de identificadores.

Nota: esto asocia una funci贸n con un contexto de ejecuci贸n y ScopeChain con contextos secundarios.

La especificaci贸n establece que ScopeChain es una matriz:

  SC = [LO, LO1, LO2,..., LOglobal]; 

Sin embargo, en algunas implementaciones, como JS, la cadena de alcance se implementa a trav茅s de listas vinculadas .

Para comprender mejor ScopeChain, discutiremos el ciclo de vida de las funciones. Se divide en la fase de creaci贸n y la fase de ejecuci贸n.

Cuando se crea una funci贸n, se le asigna la propiedad interna [[ALCANCE]] .
En [[ALCANCE]] , se registra una cadena jer谩rquica de objetos de entornos l茅xicos de contextos superiores (generadores). Esta propiedad permanece sin cambios hasta que el recolector de basura destruye la funci贸n.

隆Presta atenci贸n! [[SCOPE]] , a diferencia de ScopeChain, es una propiedad de la funci贸n en s铆 misma, no de su contexto.

Cuando se llama a una funci贸n, su contexto de ejecuci贸n se inicializa y se llena. El contexto se fija con ScopeChain = LO (de la funci贸n en s铆) + [[SCOPE]] (cadena jer谩rquica de contextos que afectan a LO).

Resoluci贸n de nombres de identificadores : sondeo secuencial de objetos LO en la cadena ScopeChain de izquierda a derecha. El resultado es un ReferenceType cuya propiedad base apunta al objeto LO en el que se encontr贸 el identificador, y PropertyName ser谩 una representaci贸n de cadena del nombre del identificador.

隆As铆 se organiza el cierre debajo del cap贸! Un cierre es esencialmente el resultado de una b煤squeda en ScopeChain para todas las variables cuyos identificadores est谩n presentes en la funci贸n.

 const x = 10; function foo () { return x; } (function (){ const x = 20; foo();// 10 ..     <b><i>[[SCOPE]]</i></b> foo          })() 

El siguiente ejemplo ilustra el ciclo de vida [[ALCANCE]] .

 function foo () { const x = 10; const y = 20; return function () { return [x,y]; } } const x = 30; const bar = foo();//   ,   foo    bar();// [10,20] .. [[SCOPE]]    foo          

Una excepci贸n importante es la funci贸n constructora . Para este tipo de funci贸n, [[ALCANCE]] siempre apunta a un objeto global.

Adem谩s, no olvide que si uno de los eslabones de la cadena ScopeChain tiene un prototipo, la b煤squeda tambi茅n se llevar谩 a cabo en el prototipo.

Conclusi贸n


Vamos a presentar las ideas clave en teor铆a:

  • esta es la relaci贸n de la entidad con el contexto de ejecuci贸n
  • ScopeChain es la relaci贸n de una entidad con todos los contextos de desove
  • this y ScopeChain son propiedades de contexto de ejecuci贸n
  • la persona que llama determina estas funciones y depende de la sintaxis de la llamada
  • ScopeChain es el entorno l茅xico del contexto actual + [[Alcance]]
  • [[Alcance]]: esta es una propiedad de la funci贸n en s铆 misma, contiene una cadena jer谩rquica de entornos l茅xicos de contextos generadores

Espero que el art铆culo haya sido 煤til. Hasta futuros art铆culos, amigos!

Source: https://habr.com/ru/post/468943/


All Articles