¿Qué es esto aquí? Operación interna de objetos JavaScript


Foto: Curiosa Liliana Saeb (CC BY 2.0)


JavaScript es un lenguaje de paradigmas múltiples que admite programación orientada a objetos y enlaces dinámicos. La vinculación dinámica es un concepto poderoso que le permite cambiar la estructura del código JavaScript en tiempo de ejecución, pero esta potencia y flexibilidad adicionales se logran a costa de cierta confusión, la mayoría de las cuales está relacionada con this comportamiento en JavaScript.


Enlace dinámico


Con el enlace dinámico, la definición del método para llamar se produce en tiempo de ejecución y no en tiempo de compilación. JavaScript hace esto con this y la cadena de prototipos. En particular, dentro del método, this se determina durante la llamada, y el valor de this será diferente dependiendo de cómo se definió el método.


Juguemos un juego. La llamo "¿Qué es this aquí?"


 const a = { a: 'a' }; const obj = { getThis: () => this, getThis2 () { return this; } }; obj.getThis3 = obj.getThis.bind(obj); obj.getThis4 = obj.getThis2.bind(obj); const answers = [ obj.getThis(), obj.getThis.call(a), obj.getThis2(), obj.getThis2.call(a), obj.getThis3(), obj.getThis3.call(a), obj.getThis4(), obj.getThis4.call(a) ]; 

Piense cuáles answers los valores en la matriz de answers y verifique sus respuestas con console.log() . Adivinado?


Comencemos con el primer caso y continuemos en orden. obj.getThis() devuelve undefined , pero ¿por qué? Las funciones de flecha nunca tienen su propio this . En cambio, siempre toman this del ámbito léxico ( aprox. Lexical esto ). Para la raíz del módulo ES6, la región léxica tendrá un valor undefined de this . obj.getThis.call(a) tampoco está definido por la misma razón. Para las funciones de flecha, this no se puede anular, incluso con .call() o .bind() . this siempre se tomará del dominio léxico.


obj.getThis2() obtiene el enlace durante la llamada al método. Si antes no existía esta vinculación para esta función, entonces puede vincularse a this (ya que esta no es una función de flecha). En este caso, this es un objeto obj que está vinculado en el momento en que se llama al método . o sintaxis de acceso a la propiedad [squareBracket] . ( nota vinculante implícito )


obj.getThis2.call(a) poco más complicado. El método call() llama a una función con este valor dado y argumentos opcionales. En otras palabras, el método obtiene el enlace .call() parámetro .call() , por lo que obj.getThis2.call(a) devuelve el objeto a . ( nota vinculante explícita )


En el caso de obj.getThis3 = obj.getThis.bind(obj); estamos tratando de obtener una función de flecha con this límite, que, como ya hemos descubierto, no funcionará, por lo que undefined estamos undefined para obj.getThis3() y obj.getThis3.call(a) respectivamente.


Para los métodos regulares, puede enlazar, por lo que obj.getThis4() devuelve obj , como se esperaba. Ya obtuvo su enlace aquí obj.getThis4 = obj.getThis2.bind(obj); , y obj.getThis4.call(a) tiene en cuenta el primer enlace y devuelve obj lugar de a .


Bola torcida


Resolveremos el mismo problema, pero esta vez usamos una class con campos públicos para describir el objeto (las innovaciones de la Etapa 3 en el momento de escribir este artículo están disponibles en Chrome de forma predeterminada y con @babel/plugin-offer-class-properties ):


 class Obj { getThis = () => this getThis2 () { return this; } } const obj2 = new Obj(); obj2.getThis3 = obj2.getThis.bind(obj2); obj2.getThis4 = obj2.getThis2.bind(obj2); const answers2 = [ obj2.getThis(), obj2.getThis.call(a), obj2.getThis2(), obj2.getThis2.call(a), obj2.getThis3(), obj2.getThis3.call(a), obj2.getThis4(), obj2.getThis4.call(a) ]; 

Piensa en las respuestas antes de continuar.


Estas listo


Todas las llamadas excepto obj2.getThis2.call(a) devuelven una instancia del objeto. obj2.getThis2.call(a) devuelve a . Las funciones de flecha todavía obtienen this de su entorno léxico. Hay una diferencia en cómo se define this desde el entorno léxico para las propiedades de clase. En el interior, la inicialización de las propiedades de clase se ve así:


 class Obj { constructor() { this.getThis = () => this; } ... 

En otras palabras, la función de flecha se define dentro del contexto del constructor. Como se trata de una clase, la única forma de crear una instancia es utilizar la new palabra clave (la omisión de una new generará un error). Una de las cosas más importantes que hace la new palabra clave es crear una nueva instancia del objeto y vincularlo en el constructor. Este comportamiento, combinado con otros comportamientos que mencionamos anteriormente, debería explicar el resto.


Conclusión


¿Tuviste éxito? Una buena comprensión de cómo se comporta this en JavaScript le ahorrará mucho tiempo depurando problemas complejos. Si cometió un error en las respuestas, esto significa que necesita practicar un poco. Practique con ejemplos, luego regrese y pruebe nuevamente hasta que pueda ejecutar la prueba y explicar a otra persona por qué los métodos devuelven lo que devuelven.


Si fue más difícil de lo que esperaba, entonces no está solo. Le pregunté a muchos desarrolladores sobre este tema y creo que hasta ahora solo uno de ellos ha hecho frente a esta tarea.


Lo que comenzó como una búsqueda de métodos dinámicos que podría redirigir con .call() , .bind() o .apply() ha vuelto mucho más complicado con la adición de métodos de clase y funciones de flecha. Quizás deberías concentrarte una vez más en esto. Recuerde que las funciones de flecha siempre toman this del alcance léxico, y la class realidad está limitada léxicamente por el constructor de la clase bajo el capó. Si alguna vez duda de this , recuerde que puede usar un depurador para verificar su valor.


Recuerde que al resolver muchas tareas de JavaScript puede hacerlo sin this . En mi experiencia, casi todo se puede redefinir en términos de funciones puras que toman todos los argumentos utilizados como parámetros explícitos ( this puede considerarse como una variable implícita). La lógica descrita a través de funciones puras es determinista, lo que lo hace más comprobable. Además, con este enfoque, no hay efectos secundarios, por lo tanto, a diferencia de los momentos de manipulación, es poco probable que rompa algo. Cada vez que this establece esto, algo que depende de su valor puede romperse.


Sin embargo, a veces this es útil. Por ejemplo, para intercambiar métodos entre una gran cantidad de objetos. Incluso en la programación funcional, this se puede utilizar para acceder a otros métodos de objeto con el fin de implementar las transformaciones algebraicas necesarias para construir nuevas álgebras sobre las existentes. Por lo tanto, se puede obtener this.map() usando this.map() y this.constructor.of() .




Gracias por la ayuda con la traducción de wksmirnowa y VIBaH_dev

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


All Articles