Programación basada en prototipos

Ajustar Compartir Imprimir Citar
Estilo de programación orientada al objeto en la que la herencia se basa en la reutilización de objetos

La programación basada en prototipos es un estilo de programación orientada a objetos en el que la reutilización del comportamiento (conocida como herencia) se realiza a través de un proceso de reutilización de objetos existentes que sirven como prototipos. Este modelo también se puede conocer como programación prototípica, orientada a prototipos, sin clases o basada en instancias.

La programación basada en prototipos utiliza el proceso de objetos generalizados, que luego se pueden clonar y ampliar. Usando la fruta como ejemplo, una "fruta" objeto representaría las propiedades y funcionalidad de la fruta en general. Un "plátano" el objeto se clonaría de la "fruta" Se agregarían el objeto y las propiedades generales específicas de los bananos. Cada "plátano" el objeto se clonaría del genérico "banana" objeto. Compare con el paradigma basado en clases, donde una "fruta" clase se extendería con un "banana" clase.

El primer lenguaje de programación orientado a prototipos fue Self, desarrollado por David Ungar y Randall Smith a mediados de la década de 1980 para investigar temas de diseño de lenguajes orientados a objetos. Desde finales de la década de 1990, el paradigma sin clases se ha vuelto cada vez más popular. Algunos lenguajes actuales orientados a prototipos son JavaScript (y otras implementaciones de ECMAScript como JScript y Flash's ActionScript 1.0), Lua, Cecil, NewtonScript, Io, Ioke, MOO, REBOL y AHK.

Diseño e implementación

Douglas Crockford describe la herencia de prototipos en JavaScript como

Usted hace objetos prototipo, y luego... hacer nuevas instancias. Los objetos son mutables en JavaScript, por lo que podemos aumentar las nuevas instancias, dándoles nuevos campos y métodos. Estos pueden actuar como prototipos para objetos aún más nuevos. No necesitamos clases para hacer un montón de objetos similares... Los objetos heredan de objetos. ¿Qué podría estar más orientado al objeto que eso?

Los defensores de la programación basada en prototipos argumentan que alienta al programador a centrarse en el comportamiento de algún conjunto de ejemplos y solo luego preocuparse por clasificar estos objetos en objetos arquetípicos que luego se usan de manera similar a las clases. Muchos sistemas basados en prototipos fomentan la modificación de prototipos durante el tiempo de ejecución, mientras que solo unos pocos sistemas orientados a objetos basados en clases (como el sistema dinámico orientado a objetos, Common Lisp, Dylan, Objective-C, Perl, Python, Ruby, o Smalltalk) permiten alterar las clases durante la ejecución de un programa.

Casi todos los sistemas basados en prototipos se basan en lenguajes interpretados y tipificados dinámicamente. Sin embargo, los sistemas basados en lenguajes tipificados estáticamente son técnicamente viables. El lenguaje Omega discutido en Programación basada en prototipos es un ejemplo de tal sistema, aunque según el sitio web de Omega, incluso Omega no es exclusivamente estático, sino que su 'compilador puede elegir usar enlace estático donde sea posible y pueda mejorar la eficiencia de un programa."

Construcción de objetos

En los lenguajes basados en prototipos no hay clases explícitas. Los objetos heredan directamente de otros objetos a través de una propiedad prototipo. La propiedad prototipo se llama prototype en Self y JavaScript, o proto en Io. Hay dos métodos para construir nuevos objetos: creación de objetos ex nihilo ("de la nada") o a través de clonación de un objeto existente. El primero se admite a través de algún tipo de literal de objeto, declaraciones en las que los objetos se pueden definir en tiempo de ejecución a través de una sintaxis especial como {...} y se pasan directamente a una variable. Si bien la mayoría de los sistemas admiten una variedad de clonaciones, la creación de objetos ex nihilo no es tan prominente.

En lenguajes basados en clases, una nueva instancia se construye a través de la función constructora de una clase, una función especial que reserva un bloque de memoria para los miembros del objeto (propiedades y métodos) y devuelve una referencia a ese bloque. Se puede pasar un conjunto opcional de argumentos de constructor a la función y, por lo general, se mantienen en las propiedades. La instancia resultante heredará todos los métodos y propiedades que se definieron en la clase, que actúa como una especie de plantilla a partir de la cual se pueden construir objetos de tipo similar.

Los sistemas que admiten la creación de objetos ex nihilo permiten crear nuevos objetos desde cero sin clonar un prototipo existente. Dichos sistemas proporcionan una sintaxis especial para especificar las propiedades y comportamientos de nuevos objetos sin hacer referencia a objetos existentes. En muchos lenguajes de prototipo existe un objeto raíz, a menudo llamado Objeto, que se establece como el prototipo predeterminado para todos los demás objetos creados en tiempo de ejecución y que incluye métodos comúnmente necesarios, como toString () función para devolver una descripción del objeto como una cadena. Un aspecto útil de la creación de objetos ex nihilo es garantizar que los nombres de las ranuras (propiedades y métodos) de un nuevo objeto no tengan conflictos de espacio de nombres con el Objeto de nivel superior > objeto. (En el lenguaje JavaScript, se puede hacer esto usando un prototipo nulo, es decir, Object.create(null)).

Clonación se refiere a un proceso mediante el cual se construye un nuevo objeto copiando el comportamiento de un objeto existente (su prototipo). El nuevo objeto lleva entonces todas las cualidades del original. A partir de este momento, el nuevo objeto se puede modificar. En algunos sistemas, el objeto secundario resultante mantiene un vínculo explícito (a través de delegación o semejanza) con su prototipo, y los cambios en el prototipo hacen que los cambios correspondientes sean evidentes en su clon. Otros sistemas, como el lenguaje de programación similar a Forth Kevo, no propagan los cambios del prototipo de esta manera y, en cambio, siguen un modelo más concatenativo en el que los cambios en los objetos clonados no se propagan automáticamente entre los descendientes.

// Ejemplo de verdadero estilo de herencia prototípico // en JavaScript.// creación de objetos utilizando el literal // notación de objeto {}.const Foo = {} Nombre: "foo", uno: 1, dos.: 2 };// Otro objeto.const bar = {} dos.: "dos", tres: 3 };// Object.setPrototypeOf() es un método introducido en ECMAScript 2015.// Por la simplicidad, finjamos // que la siguiente línea funciona independientemente de la // motor utilizado:Objeto.setPrototipoOf()bar, Foo); // foo es ahora el prototipo de bar.// Si tratamos de acceder a las propiedades de foo de bar - A partir de ahora, tendremos éxito. bar.uno; // Resuelve a 1.// Las propiedades del objeto infantil también son accesibles.bar.tres; // Resolves to 3.// Propiedades propias sombra prototipo propiedadesbar.dos.; // Resuelve a "dos"bar.Nombre; // no afectado, resuelve "foo"Foo.Nombre; // Resolves to "foo"

Para otro ejemplo:

const Foo = {} uno: 1, dos.: 2 };// bar.[[prototipo]] = fooconst bar = Objeto.crear()Foo);bar.tres = 3;bar.uno; // 1bar.dos.; // 2bar.tres; // 3

Delegación

En lenguajes basados en prototipos que usan delegación, el tiempo de ejecución del lenguaje es capaz de enviar el método correcto o encontrar el dato correcto simplemente siguiendo una serie de punteros de delegación (desde el objeto hasta su prototipo) hasta que se encuentre una coincidencia. Todo lo que se requiere para establecer este comportamiento compartido entre objetos es el puntero de delegación. A diferencia de la relación entre clase e instancia en los lenguajes orientados a objetos basados en clases, la relación entre el prototipo y sus derivados no requiere que el objeto secundario tenga una memoria o una similitud estructural con el prototipo más allá de este enlace. Como tal, el objeto secundario puede continuar modificándose y enmendándose con el tiempo sin reorganizar la estructura de su prototipo asociado como en los sistemas basados en clases. También es importante tener en cuenta que no solo se pueden agregar o cambiar datos, sino también métodos. Por esta razón, algunos lenguajes basados en prototipos se refieren tanto a los datos como a los métodos como "ranuras" o "miembros".

Concatenación

En la creación de prototipos concatenativos, el enfoque implementado por el lenguaje de programación Kevo, no hay punteros visibles ni enlaces al prototipo original a partir del cual se clona un objeto. El objeto prototipo (principal) se copia en lugar de vincularse y no hay delegación. Como resultado, los cambios en el prototipo no se reflejarán en los objetos clonados.

La principal diferencia conceptual de este arreglo es que los cambios realizados en un objeto prototipo no se propagan automáticamente a los clones. Esto puede verse como una ventaja o una desventaja. (Sin embargo, Kevo proporciona primitivas adicionales para publicar cambios en conjuntos de objetos en función de su similitud, los llamados semejanzas de familia o mecanismo de familia clonada, en lugar de a través del origen taxonómico, como es típico en el modelo de delegación). A veces también se afirma que la creación de prototipos basada en la delegación tiene una desventaja adicional en el sentido de que los cambios en un objeto secundario pueden afectar la operación posterior del objeto principal. Sin embargo, este problema no es inherente al modelo basado en delegación y no existe en los lenguajes basados en delegación como JavaScript, que aseguran que los cambios en un objeto secundario siempre se registren en el objeto secundario y nunca en los principales (es decir, el objeto secundario y #39;el valor sombrea el valor del padre en lugar de cambiar el valor del padre).

En implementaciones simples, la creación de prototipos concatenativos tendrá una búsqueda de miembros más rápida que la creación de prototipos basada en delegación (porque no hay necesidad de seguir la cadena de objetos principales), pero a la inversa usará más memoria (porque se copian todas las ranuras, en lugar de allí). siendo una sola ranura apuntando al objeto principal). Sin embargo, implementaciones más sofisticadas pueden evitar este problema, aunque se requieren compensaciones entre velocidad y memoria. Por ejemplo, los sistemas con prototipos concatenados pueden usar una implementación de copia en escritura para permitir el intercambio de datos detrás de escena, y Kevo sigue este enfoque. Por el contrario, los sistemas con prototipos basados en delegación pueden utilizar el almacenamiento en caché para acelerar la búsqueda de datos.

Crítica

Los defensores de los modelos de objetos basados en clases que critican los sistemas basados en prototipos a menudo tienen preocupaciones similares a las preocupaciones que los defensores de los sistemas de tipos estáticos para lenguajes de programación tienen de los sistemas de tipos dinámicos (ver tipo de datos). Por lo general, tales preocupaciones involucran la corrección, la seguridad, la previsibilidad, la eficiencia y la falta de familiaridad del programador.

En los primeros tres puntos, las clases a menudo se consideran análogas a los tipos (en la mayoría de los lenguajes orientados a objetos tipificados estáticamente cumplen esa función) y se proponen para proporcionar garantías contractuales a sus instancias y a los usuarios de sus instancias, que se comportarán de una manera determinada.

Con respecto a la eficiencia, la declaración de clases simplifica muchas optimizaciones del compilador que permiten desarrollar métodos eficientes y búsquedas de variables de instancia. Para el lenguaje Self, se dedicó mucho tiempo de desarrollo a desarrollar, compilar e interpretar técnicas para mejorar el rendimiento de los sistemas basados en prototipos frente a los sistemas basados en clases.

Una crítica común que se hace contra los lenguajes basados en prototipos es que la comunidad de desarrolladores de software no los conoce, a pesar de la popularidad y la penetración en el mercado de JavaScript. Este nivel de conocimiento de los sistemas basados en prototipos parece estar aumentando con la proliferación de marcos JavaScript y el uso complejo de JavaScript a medida que la Web madura. ECMAScript 6 introdujo clases como azúcar sintáctica sobre la herencia basada en prototipos existente de JavaScript, proporcionando una forma alternativa de crear objetos y lidiar con la herencia.

Lenguajes compatibles con la programación basada en prototipos

  • Actor-Based Concurrent Language (ABCL): ABCL/1, ABCL/R, ABCL/R2, ABCL/c+
  • Agora
  • AutoHotkey
  • Cecil y Diesel de Craig Chambers
  • ColdC
  • COLA
  • Lisp común
  • Cyan
  • ECMAScript
    • ActionScript 1.0, utilizado por Adobe Flash y Adobe Flex
    • E4X
    • JavaScript
    • JScript
    • TipoScript
  • Io
  • Ioke
  • Jsonnet
  • Logtalk
  • LPC
  • Lua
  • M2000
  • Maple
  • MOO
  • Neko
  • NewtonScript
  • Nim
  • Nix
  • Objeto Lisp
  • Obliq
  • Omega
  • OpenLaszlo
  • Perl, con la Clase: Módulo prototipo
  • Python con prototipo.py.
  • R, con el paquete proto
  • REBOL
  • Rojo (lengua de programación)
  • Ruby (lengua de programación)
  • Yo
  • Seph
  • Pizarra (lengua de programación)
  • SmartFrog
  • ¡Aguanta!
  • Etoys
  • TADS
  • Tcl con extensión snit
  • Umajin