
Esta es una historia sobre una pieza muy especial de JavaScript, el lenguaje artificial
más utilizado en el mundo hoy (2019).
El artículo presenta una especie de visión filosófica de la herencia en JavaScript, y solo me atrevo a esperar que se base en la fuente de conocimiento más impresionante: la vida misma en todas sus manifestaciones. No sé si esto fue una inspiración al crear el diseño de la cadena prototipo en JavaScript.
Pero si es así, entonces es tan significativo y poderoso que cuando empiezo a pensar en ello, a veces se hace aún más difícil respirar ...
(todos los enlaces están subrayados )
¡También estoy seguro de que ninguno de nosotros dudará de que
Brendan Ike (Eich), el autor del lenguaje de programación JavaScript, es un genio excepcional! Y no solo porque a menudo repite:
¡Apuesta siempre por JavaScript!
¡Empecemos! Y nuestro primer punto de partida será la Imaginación, en la que primero apagaremos todos los prejuicios, omisiones y otros efectos secundarios.
Nos dirigimos de
regreso al futuro , la era que precedió a la creación de Internet moderna a principios de la década de 1990.
Desde la época de los
primeros hackers que inventaron todo lo que nosotros (
los empleados de TI ) estamos
usando ahora, nos hemos movido a un bosquejo impresionante: sobre la guerra entre los navegadores
Netstcape Navigator 2 e
Internet Explorer 3.
Java acaba de salir, y casi todo, desde Internet moderno aún no se ha inventado o No pre-abierto. Es posible que, como yo, en esos "buenos viejos tiempos" eras joven, y pudieras recordar este maravilloso sentimiento de participación en todo el esplendor que se está creando ante tus ojos.
Por lo tanto, tiene una
PC muy poderosa en el
Intell Pentium 200 MMX más moderno con 32Mb de RAM, Windows 3.11 o incluso Windows 95 y ¡mira hacia el futuro con esperanza! Y, por supuesto, ambos
navegadores también
están instalados. Usted tiene un
acceso telefónico , a través del cual se conecta a la red para recibir nueva basura, también, para estudiar o simplemente chatear, chatear. Aunque, espere un momento, aún no puede chatear directamente en el navegador, lo más probable es que esté utilizando sistemas de entrega de mensajes retrasados, algo como
EMail o quizás
UseNet , o, muy posiblemente, ya haya dominado la entrega instantánea a través de
IRC .
Pasan un par de años y literalmente TODO cambia ... De repente ves la animación de los copos de nieve en las páginas web felicitándote por Año Nuevo y Navidad. Por supuesto, le interesa cómo se hace esto y descubre un nuevo lenguaje: JavaScript. Como el
HTML no es nuevo para usted, comienza a aprender este atractivo techno-craft. Pronto descubres
CSS y resulta que esto también es importante, ya que todo está hecho de una combinación de estos tres: HTML, JavaSript y CSS. Wow
Casi al mismo tiempo, es posible que note un par de cosas geniales en Windows,
CScript y
HTA aparecieron en él, e incluso entonces fue posible crear aplicaciones de escritorio completas directamente en JS (y esto todavía funciona).
Y así comenzó a hacer su primer servidor web, posiblemente en
Perl o
C ~
C ++ . Quizás incluso comenzaste a usar
sistemas operativos tipo Unix y lo hiciste en
bash . Y todo ello "girando girando" gracias a la
interfaz de puerta de enlace común (no lo confunda con ese otro
CGI ).
PHP todavía casi no existe, pero tal vez te guste pronto.
Era 200x. Ahora está haciendo
ASP en
JScript . Esto es muy similar al JavaScript que funciona dentro de sus páginas web. ¡Esto es genial! Está considerando crear su propio
motor de plantillas , una especie de parodia de
XML . Y luego, de repente, alguien llama a
AJAX todas estas formas divertidas de cargar
dinámicamente el contenido que ha estado utilizando durante varios años. Y todos ellos ahora piensan que solo hay
XMLHTTPRequest , pero recuerda que los datos se pueden transferir a
BMP ,
IFrame o incluso insertando la
etiqueta <script> . Y, de repente, alguien habla con entusiasmo sobre
JSON y lo útil que es, cuando has estado manejando datos durante una
eternidad con algo como esto:
document.write("<" + "script src=" + path + ">");
Ahora no es todo lo que importa, pero aún puedes recordar
cómo ...
Cuando recupera sus sentidos, de vez en cuando comienza a encontrarse con
Rhino o
Nashorn en un intento de satisfacer los deseos de los clientes de Java que utilizan
Alfresco o
Asterisk . Y ya has escuchado sobre el inminente advenimiento de JavaScript en el mundo de los microchips, y estas noticias te inspiran mucho. Y, por supuesto, ahora tienes
jQuery y
Backbone .
Al ver las nevadas del próximo 2010, ya sabes que en tu mundo todas las reglas del juego pronto cambiarán, ya que el "Jugador No. 1" ha entrado en el campo:
Node.js. Y los próximos 10 años que pasarás con este nuevo juguete, e incluso ahora, en 2019, aún no puedes tener suficiente de lo genial que es.
En general, estás contento con todo, todo te conviene, todos estos juguetes y juegos en ellos constituyen una gran parte de los intereses de tu vida.
Pero hay una pequeña pregunta que te haces día tras día, noche tras noche durante dos décadas:Si tuviera que hacer esto, ¿cómo explicaría Empathy usando JavaScript?
Usted sabe que uno de los temas más complicados en JavaScript es la
herencia de prototipos y la cadena de prototipos . Y le encanta este tema, puede explicar cómo funciona y funciona todo, simplemente porque lo aprendió casi desde el principio, incluso antes
de que naciera la
primera versión de la Norma , y donde, como recordará, hay
4.2 .1 Objetos :
ECMAScript admite herencia basada en prototipos. Cada constructor tiene un prototipo asociado, y cada objeto creado por ese constructor tiene una referencia implícita al prototipo (llamado prototipo del objeto) asociado con su constructor. Además, un prototipo puede tener una referencia implícita no nula a su prototipo, y así sucesivamente; Esto se llama la cadena prototipo .
Cada objeto se crea con una referencia implícita al prototipo. Esto se llama la cadena de herencia prototipo, que puede continuar durante el tiempo que desee.Wow ... Y si de repente, como yo, pudieras pensar que este es uno de los mejores inventos de
Compuer Science , ¿cómo expresarías el efecto que la lectura de esta declaración tuvo en ti?
Volvamos al principio. En el patio de 1995. Eres Brendan Ike y
necesitas inventar un nuevo lenguaje de programación. Quizás te guste
Lisp o
Scheme , al menos algunas de sus partes favoritas. Y se enfrenta a la necesidad de resolver el problema de la
herencia , ya que debe pretender que el lenguaje tiene una cierta implementación de
OOP .
Pensemos : necesita mezclar todas las cosas que le gustan, tal vez también algunas que no le gustan, y el cóctel resultante debe ser lo suficientemente bueno como para que nadie note un truco hasta que haya una necesidad real de entender cómo funciona. arreglado por dentro.
Y ahora la pregunta es:
¿qué pasaría con la herencia?Volvamos a la realidad por un momento. ¿Qué sabemos todos sobre la herencia? Algunas piezas obvias de respuestas a esta pregunta:
- La mayoría de las formas de vida se basan en el genoma . Es un depósito de información sobre las características probables y el supuesto comportamiento de los seres vivos. Cuando eres una criatura viviente, llevas una parte del genoma dentro de ti, puedes distribuirlo, pero lo recibiste de generaciones anteriores.
- Puedes crear una criatura viva usando dos técnicas: mezclar (genomas) de dos antepasados o, posiblemente, usar la clonación monoica de uno de ellos. Por supuesto, hoy somos tecnologías disponibles que permiten mezclar los genomas de más de una criatura, pero esto es mucho menos obvio y no tan natural.
- El factor tiempo es importante. Si no hay una propiedad heredada, o no la hay, nuestra única salida es crearla desde cero. Además de esto, también existe el Legado, lo que pasa al ser de sus ancestros no a través del genoma, sino a través de las leyes de propiedad, y esto también puede ser significativo.
Volviendo al pasado, y la pregunta correcta que debemos hacernos ahora es:
Y, de hecho, herencia ¿Qué queremos obtener?Bueno, en cualquier caso, en el proceso de resolver el problema de la herencia, necesitamos al menos llenar el vacío entre la programación y la vida real, de lo contrario, en general, no tendremos derecho a llamarlo Herencia.
Ahora terminemos: estamos en la década de 1995, y tenemos una PC poderosa con solo 32 megabytes de RAM, y estamos tratando de crear un lenguaje interpretado, por lo que debemos cuidar mucho esta memoria. Cada pieza de datos, especialmente los
objetos String , consume mucha memoria, y deberíamos poder declarar esta pieza solo una vez, y siempre que sea posible en el futuro, siempre usemos solo punteros al área de memoria ocupada por esta pieza.
Resumimos: una pregunta muy difícil.Existe una opinión popular: "
JavaScript se crea a partir de objetos ". Usando esta trivialidad, podemos responder fácilmente a la pregunta "
de qué " heredar y "
qué ": de objetos a objetos. En aras de la cuestión de guardar memoria, resulta que todos los datos deben almacenarse en estos objetos, y todos estos enlaces de datos también deben almacenarse en las propiedades heredadas de estos objetos. Quizás ahora esté claro por qué en 1995 realmente necesitábamos crear un diseño basado en una cadena de prototipo: ¡ahorra memoria el mayor tiempo posible! Y en general, creo que esto todavía puede ser un aspecto muy importante.
Según el diseño indicado y la opinión "
todo es un Objeto ", podemos intentar clonar algo así. Pero, ¿qué es la clonación aquí ahora? Creo que siguiendo los requisitos de nuestros requisitos, podemos asumir algo como
Structural o Surface Copy , en algo similar a los predecesores de
Object.assign moderno.
Implementemos una copia estructural simple en 1995 usando
for (var i in) {} , ya que el estándar
ya permitía esto :
Como puede ver, este enfoque todavía "funciona", aunque en general, por supuesto, recomendaría mirar el módulo de
extensión profunda para una comprensión más detallada de cómo hacer la clonación en JavaScript, pero para los fines del artículo, una aplicación consistente es muy adecuada para nosotros descrito por
cloneProps , porque podríamos usarlo en aquellos tiempos antiguos:
- clonando objetos usando el constructor: usando el constructor, cree al menos dos clones diferentes
- clonando un constructor de un constructor: implementamos el uso del comportamiento de un constructor de otro constructor
var SomeConstructor = function () { this.a = 'cloned'; }; var AnotherConstructor = function () {
- clonando un constructor usando un objeto: usaremos el mismo objeto para implementar la clonación en al menos dos constructores
var existentObject = { foo : 'bar' }; var SomeConstructor = function () { cloneProps(foo, this); }; var OtherConstructor = function () { cloneProps(foo, this); };
- Clonando un Objeto de otro Objeto: use un objeto para crear varios de sus clones . No hay nada que describir aquí, solo tómalo como es nuestro cloneProps del primer ejemplo anterior.
Con la clonación, en general, todo es simple, como vemos, todo está claro y en general, pero ...¿Es tan fácil para nosotros hacer la Herencia de Entidades, usando una combinación de sus predecesores?
- Herencia de un objeto usando el Constructor: este es el propósito de los diseñadores, solo mostraremos cómo se diseñó originalmente .
var existentObject = { foo : 'bar' }; var SomeConstructor = function () {}; SomeConstructor.prototype = existentObject; var inheritedObject = new SomeConstructor();
- Herencia del Constructor de otro Constructor: sin duda, el primero que notó esto fue un Genio sobresaliente. Con todo, este es otro ejemplo clásico de todas partes .
var FirstConstructor = function () { this.foo = 'bar'; }; var InheritedConstructor = function () { FirstConstructor.call(this); }; InheritedConstructor.prototype = { bar : 'foo' }; InheritedConstructor.prototype.constructor = FirstConstructor; var inherited = new InheritedConstructor();
sería posible decir algo sofisticado, pero ¿por qué?
- Herencia del Constructor del Objeto: de nuevo, solo usamos .prototype = object cada vez que lo necesitamos, no hay nada que describir, siempre necesitamos asignar Constructor.prototype ya que se supone que coloca el objeto allí, y mediante un enlace implícito obtenemos todas sus propiedades .
- Heredar un objeto de un objeto: lo mismo. Acabamos de poner el primer objeto en Constructor.prototype y tan pronto como digamos nuevo Constructor crearemos una copia heredada en la que habrá referencias implícitas a las propiedades de nuestro primer objeto.
Y, por supuesto, en todas estas situaciones con herencia, tendremos la oportunidad de verificar usando la
instancia de qué constructor creamos los objetos, aunque, por supuesto, debe tenerse en cuenta que la
instancia de sí mismo apareció en los estándares casi cuatro años después.
Es cierto que quedaba un detalle tan pequeño del párrafo 4.2.1:
ser capaz de hacer esto por el tiempo que sea necesario, como dice:
y así sucesivamente
Bueno,
tratemos de hacer que la herencia sea realmente interminable , utilizando la tecnología
de 1995 .
De hecho, imaginemos que tenemos dos entidades y
no constructores, sino objetos simples. Y queríamos heredar uno del otro, y luego quizás del otro, y del otro, y así sucesivamente ...
Pero como?
Echa un vistazo un poco más lejos, más profundo.
La respuesta correcta aquí nuevamente es: ¿
Herencia de lo que necesitamos crear?Después de todo, no necesitamos estas Entidades por nuestra cuenta. Necesitamos sus propiedades: memoria de consumo de datos asociada; y también, probablemente necesitemos algún comportamiento: métodos que usen estos datos. Y será igual de
honesto , si tenemos la oportunidad de verificar dónde y dónde heredamos y usar qué. En general, sería genial si pudiéramos reproducir el diseño inherente de los patrones de herencia en el futuro, lo que implica que si heredamos uno del otro muchas veces, siempre obtendremos el mismo resultado, de acuerdo con lo que escribimos (contrato) . Aunque todavía es así, puede ser tan útil para nosotros arreglar de alguna manera el momento de la creación, ya que nuestras entidades "anteriores" pueden cambiar con el tiempo, y los "herederos", que les respetan en esto, aún cambian con ellos. puede que no quiera hacerlo.
Y, dado que todo nuestro código es una combinación de datos y comportamiento, ¿será generalmente normal usar el mismo método, combinando datos y presentación, al diseñar un sistema de herencia?
En cuanto a mí, todo esto se parece a lo que vemos al observar la Vida en todas sus formas increíbles. ¡Desde el primer unicelular, hasta el multicelular y sus descendientes, y hasta los animales, las personas, el humanismo y las tribus, las civilizaciones, el intelecto y sus formas artificiales, y más allá del espacio, la galaxia y las estrellas! y:
"... Todo lo que tenemos que hacer es asegurarnos de seguir hablando ..."
(todo lo que necesitamos hacer es continuar la comunicación)Increíble en su consideración, una cita de
Stephen Hawking , posteriormente popularizada en
esta obra maestra de
Pind Floyd .
Los lenguajes de programación basados en el
paso de mensajes y un concepto
basado en Flow implementado a través de una API interna sólida le permiten pasar de
datos simples a abstracciones más altas, descripción y todo lo demás. Creo que esto es puro arte, y cómo funciona en particular en estructuras JavaScript profundamente ocultas a través de relaciones implícitas entre datos en cadenas de prototipos.
Imagine nuevamente a dos antepasados, se comunican y en un momento sus emociones y sentimientos crean un hijo. El niño crece, conoce a otro niño, se comunican, y aparece el siguiente descendiente, y más y más y más ... Y siempre necesitamos dos padres, de lo contrario no es natural, ya será ingeniería genética. Dos, ni más ni menos. Un descendiente recibe lo mismo que su Legado, por lo que es simple y comprensible.
Entiendo que sonará extraño, pero sí, tenemos todo lo que necesitamos para crear este modelo de Herencia en 1995. Y la base de todo esto es precisamente
4.2.1 Objetos , referencia implícita a través de prototipo.
Y eso es exactamente, tal como es, combinando
ParentObject con
ParentConstructor especificando
.prototype y luego
Constructor probablemente nos creará un
ChildObject , por supuesto, si decimos la palabra mágica "
nuevo ":
var ParentObject = { foo : 'bar' }; var ParentConstructor = function () {}; ParentConstructor.prototype = ParentObject; var ChildObject = new ParentConstructor();
Podemos discernir aquí a nuestros dos antepasados. En el momento en que dijimos la palabra mágica "
nuevo " les pedimos que chatearan. Si no quieren comunicarse, Life se detendrá, el proceso caerá con un error y el
compilador (intérprete) nos lo informará.
Por supuesto, sí, pero pedimos el
árbol de herencia o dejamos que sea obviamente mucho más simple, al menos para el árbol
genealógico . Y la respuesta sigue siendo la misma ... nuestro
objeto hijo crece y se
convierte en un objeto padre , luego se encuentra con un nuevo
objeto constructor y tan pronto como decimos la codiciada palabra "
nuevo " - magia:
Y podemos continuar haciendo esto hasta el infinito. Y, tal vez, realmente creo que esta fue la idea principal al desarrollar el diseño de la cadena de prototipos, porque como todos sabemos, este enfoque crea algunos problemas
muy claros pero no menos desagradables ...
1: Comunidad ... Como puedes comprobar por ti mismo, especificando en
.prototype ParentConstructor ' a u
AnotherConstructor' a es un
Contrato social muy serio y estricto en nuestra Tribu. Crea una referencia a las
propiedades ParentObject (
.foo ) para los Herederos:
ChildObject y
SequentialChildObject . Y si nos deshacemos de esta indicación, estos enlaces desaparecerán. Si ideamos y reasignamos esta referencia a algún otro objeto, nuestros herederos heredarán instantáneamente otras propiedades. Por lo tanto, combinando antepasados a través de
.prototype , probablemente podríamos decir que estamos creando algún tipo de
célula de la sociedad , porque estos "antepasados" pueden reproducir muchos descendientes idénticos cada vez que les preguntamos acerca de esto usando
nuevos . Y así, habiendo destruido la "familia", estamos arruinando las cualidades hereditarias de sus descendientes, tal drama; ^)
Tal vez todo esto es hablar de
Legacy en nuestro código, ¡deberíamos ocuparnos de esto cuando creamos un código
seguro y
compatible ! Por supuesto, en 1995 no se discutieron
SOLIDOS , el
Principio de Liskov y el
Diseño por Contrato y
GRASP , pero es obvio que estas metodologías no se crearon "desde cero", todo comenzó mucho antes.
2: Familia ... Podemos verificar fácilmente que nuestro
ParentObject puede ser muy frívolo con otros Constructos. Esto no es justo, pero podemos usar tantos Constructores como queramos en la Herencia de nuestro ParentObject y así crear tantas familias como queramos. Por otro lado, cada Constructor está muy asociado con ParentObject a través de la tarea .prototype, y si no deseamos dañar a nuestros herederos, debemos mantener esta conexión el mayor tiempo posible. Podríamos llamarlo el arte de la tragedia en la historia de nuestra tribu. Aunque, por supuesto, esto nos protege de la amnesia: el olvido de lo que heredamos y de quién, y por qué nuestros herederos obtienen tal
Legado . Y, alabando al gran
Mnemosyne !, podemos realmente probar fácilmente nuestro Prototype Chain
Tree y encontrar
artefactos de lo que hicimos mal.
3: Vejez ... Nuestro
ParentObject y
Constructor son ciertamente susceptibles a daños durante el ciclo de vida de nuestro programa. Podemos tratar de solucionar esto, pero nadie está a salvo de los errores. Y todos estos cambios pueden dañar a los herederos. Debemos cuidar las
pérdidas de
memoria . Por supuesto, podemos destruir partes innecesarias del código en tiempo de ejecución y liberar memoria que ya no se usa en nuestro
Ciclo de vida . Además, debemos deshacernos de todas las posibilidades de crear
paradojas temporales en las cadenas de prototipos, ya que de hecho es bastante simple heredar el ancestro de su propio descendiente. Pero esto ya puede ser muy peligroso, ya que tales técnicas de coquetear con el pasado del futuro pueden crear montones completos de
Heisenbags difíciles de reproducir, especialmente si tratamos de medir algo que puede cambiar con el tiempo.
Crónica de decisiones
Que sea simple, obvio y no muy útil, pero en lugar de pensar en nuestro Constructor y ParentObject como mamá y papá, describámoslos como un
huevo y ...
polen . Luego, en el futuro, cuando creamos el
cigoto usando la palabra atesorada "
nuevo ", ¡ya no dañará nuestra imaginación!
En el momento en que hagamos esto, inmediatamente nos libraremos de los tres problemas anteriores. Por supuesto, para esto necesitamos la capacidad de crear los cigotos por sí mismos, lo que significa que necesitamos la Fábrica de Diseñadores. Y ahora llámenlo como quieran, madres, padres, qué diferencia hace, porque la conclusión es que si vamos a decir "
nuevo ", entonces debemos crear una jaula de diseñador de flores "nueva", ponerle polen, y solo esto nos permitirá para cultivar un nuevo
Snowdrop "correcto" en el lejano y nevado 2020m:
var Pollen = { season : 'Spring' };
(y sí, no te olvides de verificar la temporada con este snowdrop, de lo contrario los copos de nieve caerían o caerían, y un snowdrop sería una flor de primavera ...)Por supuesto, la Complejidad Ciclomática de las decisiones que intentas crear usando este enfoque será bastante comparable al Riddle Einstein . Por lo tanto, aquí "preparé" una biblioteca , puede ayudar con la creación de cadenas de diseñadores y memorización (nota del editor: bueno, takoe, toma un pastel de un estante, bla-bla-bla ) ...Y aunque no puedo probarlo, este enfoque se ha utilizado con bastante éxito de vez en cuando durante dos décadas, si necesita estar 146% seguro de que todo es normal con la herencia. Puede ver fácilmente por sí mismo que se prueba, reproduce y admite de manera elemental (Nota del editor : sí, en este momento, dejó todo y fue a asegurarse ).Por supuesto, esta no es toda la historia, simplemente declaramos el hecho: JavaScript está diseñado lo suficientemente bien como para describir el Gráfico Genealógico directamente a través de la Herencia. Por supuesto, aquí no tocamos astutamente el tema de la degradación de la Clase , pero estoy seguro de que usted mismo puede reemplazar fácilmente FlowerEggCell con FlowerEggCellClass dentro de FlowersFactory: la esencia seguirá siendo la misma si desea verificar sus flores a través de la instancia , verá que todos son descendientes de FlowerEggCell a los que se refiere a través de FlowerZygote . Y, por supuesto, ahora puede cambiar las propiedades del propio FlowerZygote , ya que esto no hará ningún daño al FlowersFactory en sí mismo , seguirá siendo capaz de crear otros nuevos constructores FlowerEggCell o FlowerEggCellClass en el futuro de acuerdo con el diseño original de " referencia " que haya puesto allí.Espero que este artículo haya disipado todas las dudas sobre la importancia de la palabra .prototipo la próxima vez que vea nulo this ,
. call (null ,
. apply (null . bind (null code style
(. .:
Sorrow , , , ).
!
!
V