¿El "patrón OLOO (objetos enlazados con otros objetos)" de Kyle Simpson se diferencia de alguna manera del patrón de diseño del prototipo? Aparte de acuñarlo con algo que indique específicamente "vinculación" (el comportamiento de los prototipos) y aclarar que no hay nada de "copiar" sucediendo aquí (un comportamiento de clases), ¿qué introduce exactamente su patrón?
Aquí hay un ejemplo del patrón de Kyle de su libro, "No conoces JS: esto y prototipos de objetos":
var Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
var Bar = Object.create(Foo);
Bar.speak = function() {
alert("Hello, " + this.identify() + ".");
};
var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");
b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."
javascript
design-patterns
shmuli
fuente
fuente
Respuestas:
OLOO adopta la cadena de prototipos tal como está, sin necesidad de aplicar otras semánticas (confusas en mi opinión) para obtener el enlace.
Entonces, estos dos fragmentos tienen el mismo resultado EXACTO, pero llegan de manera diferente.
Forma de constructor:
Formulario OLOO:
En ambos fragmentos, un
x
objeto es[[Prototype]]
vinculado a un objeto (Bar.prototype
oBarObj
), que a su vez está vinculado al tercer objeto (Foo.prototype
oFooObj
).Las relaciones y la delegación son idénticas entre los fragmentos. El uso de memoria es idéntico entre los fragmentos. La capacidad de crear muchos "niños" (también conocidos como muchos objetos como
x1
throughx1000
, etc.) es idéntica entre los fragmentos. La actuación de la delegación (x.y
yx.z
) es idéntico entre los fragmentos. El rendimiento de creación de objetos es más lento con OLOO, pero la comprobación de la cordura que revela que el rendimiento más lento no es realmente un problema.Lo que sostengo que ofrece OLOO es que es mucho más simple simplemente expresar los objetos y vincularlos directamente, que vincularlos indirectamente a través del constructor /
new
mecanismos. Este último pretende ser sobre clases, pero en realidad es una sintaxis terrible para expresar delegación ( nota al margen: ¡también lo es laclass
sintaxis de ES6 !).OLOO simplemente está eliminando al intermediario.
Aquí hay otra comparación de
class
vs OLOO.fuente
Object.create(...)
es muchas veces más lento quenew
. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…Leí el libro de Kyle y lo encontré realmente informativo, particularmente los detalles sobre cómo
this
está encuadernado.Pros:
Para mí, hay un par de grandes ventajas de OLOO:
1. Sencillez
OLOO confía en
Object.create()
crear un nuevo objeto que está[[prototype]]
vinculado a otro objeto. No es necesario que comprenda que las funciones tienen unprototype
propiedad o preocuparse por cualquiera de los posibles errores relacionados que surgen de su modificación.2. Sintaxis más limpia
Esto es discutible, pero creo que la sintaxis de OLOO es (en muchos casos) más ordenada y concisa que el enfoque javascript 'estándar', particularmente cuando se trata de polimorfismo (
super
llamadas de estilo).Contras:
Creo que hay un poco de diseño cuestionable (uno que en realidad contribuye al punto 2 anterior), y eso tiene que ver con el sombreado:
La idea detrás de esto es que los objetos tienen sus propias funciones más específicas que luego delegan internamente a funciones más abajo en la cadena. Por ejemplo, puede tener un
resource
objeto con unasave()
función en él que envía una versión JSON del objeto al servidor, pero también puede tener unclientResource
objeto que tiene unstripAndSave()
función, que primero elimina las propiedades que no deberían enviarse al servidor. .El problema potencial es: si alguien más llega y decide hacer un
specialResource
objeto, sin estar completamente al tanto de toda la cadena del prototipo, podría razonablemente * decidir guardar una marca de tiempo para el último guardado bajo una propiedad llamadasave
, que oculta lasave()
funcionalidad base en elresource
objeto dos eslabones hacia abajo en la cadena del prototipo:Este es un ejemplo particularmente elaborado, pero el punto es que específicamente no sombrear otras propiedades puede llevar a algunas situaciones incómodas y al uso intensivo de un diccionario de sinónimos.
Quizás una mejor ilustración de esto sería un
init
método, particularmente conmovedor ya que OOLO evita las funciones de tipo constructor. Dado que todos los objetos relacionados probablemente necesitarán dicha función, puede ser un ejercicio tedioso nombrarlos adecuadamente, y la singularidad puede hacer que sea difícil recordar cuál usar.* En realidad, no es particularmente razonable (
lastSaved
sería mucho mejor, pero es solo un ejemplo).fuente
[[Prototype]]
sistema en sí, no OLOO específicamente.b.timeStampedSave();
lugar dea.timeStampedSave();
en la última línea del fragmento de código.La discusión en "You Don't Know JS: this & Object Prototypes" y la presentación de OLOO es estimulante y he aprendido mucho leyendo el libro. Los méritos del patrón OLOO están bien descritos en las otras respuestas; sin embargo, tengo las siguientes quejas de mascotas (o me falta algo que me impide aplicarlo de manera efectiva):
1
Cuando una "clase" "hereda" otra "clase" en el patrón clásico, las dos funciones pueden declararse con sintaxis similar ( "declaración de función" o "declaración de función" ):
Por el contrario, en el patrón OLOO, se utilizan diferentes formas sintácticas para definir la base y los objetos derivados:
Como puede ver en el ejemplo anterior, el objeto base se puede definir usando la notación literal de objeto, mientras que la misma notación no se puede usar para el objeto derivado. Esta asimetría me molesta.
2
En el patrón OLOO, la creación de un objeto consta de dos pasos:
Object.create
Llame a algún método personalizado no estándar para inicializar el objeto (que debe recordar, ya que puede variar de un objeto a otro):
Por el contrario, en el patrón de prototipo, utiliza el operador estándar
new
:3
En el patrón clásico, puedo crear funciones de utilidad "estáticas" que no se aplican directamente a un "instante" asignándolas directamente a la función de "clase" (a diferencia de su
.prototype
). Por ejemplo, como funciónsquare
en el siguiente código:Por el contrario, en el patrón OLOO todas las funciones "estáticas" están disponibles (a través de la cadena [[prototype]]) en las instancias del objeto también:
fuente
Como explica Kyle cuando dos objetos están
[[Prototype]]
vinculados, en realidad no dependen el uno del otro; en cambio, son objeto individual. Está vinculando un objeto a otro con un[[Prototype]]
vínculo que puede cambiar en cualquier momento que desee. Si toma dos[[Prototype]]
objetos vinculados creados a través del estilo OLOO como dependientes entre sí, también debe pensar lo mismo sobre los creados a través deconstructor
llamadas.Ahora piensa por un segundo, ¿piensas
foo
bar
ybaz
como dependientes el uno del otro?Ahora hagamos lo mismo con este
constructor
código de estilo.La única diferencia b / w este último y el código anterior es que en este último uno
foo
,bar
,baz
bbjects están vinculados a cada-otra a través de objetos arbitrarios de suconstructor
función (Foo.prototype
,Bar.prototype
,Baz.prototype
), pero en el anterior (OLOO
estilo) que están vinculados directamente. Ambas formas que sólo va a asociarfoo
,bar
,baz
uno con el otro, situado en la anterior e indirectamente en el último. Pero, en ambos casos, los objetos son independientes entre sí porque no es realmente como una instancia de ninguna clase que, una vez instanciada, no se puede hacer heredar de alguna otra clase. Siempre puede cambiar qué objeto debe delegar un objeto también.Entonces todos son independientes entre sí.
Sí, eso es posible
Usemos
Tech
como un objeto de utilidadcree tantos objetos como desee vinculados
Tech
-¿Cree que
html
,css
,js
objetos están conectados entre sí-? No, no lo son. Ahora veamos cómo pudimos haber hecho eso con laconstructor
funcióncree tantos objetos como desee vinculados
Tech.proptotype
-Algunas comprobaciones (evitando console.log) -
¿Cómo cree que estas
constructor
al estilo de objetos (html
,css
,js
) objetos difieren delOLOO
código de estilo? De hecho, tienen el mismo propósito. En elOLOO
estilo uno los objetos delegan aTech
(la delegación se estableció explícitamente) mientras que en elconstructor
estilo uno los objetos delegan aTech.prototype
(la delegación se estableció implícitamente). En última instancia, termina vinculando los tres objetos, sin ningún vínculo entre sí, con un objeto, usando directamenteOLOO
-style, indirectamente usandoconstructor
-style.No,
ObjB
aquí no es como una instancia (en lenguajes de base clásica) de ninguna claseObjA
. Debería decirse que elobjB
objeto se hace delegado aObjA
objeto en el momento de su creación " . Si hubiera usado el constructor, habría hecho el mismo 'acoplamiento', aunque indirectamente haciendo uso de.prototype
s.fuente
@Marcus @bholben
Quizás podamos hacer algo como esto.
Por supuesto, crear un objeto Point3D que se vincule al prototipo de un objeto Point2D es un poco tonto, pero eso no viene al caso (quería ser coherente con su ejemplo). De todos modos, en lo que respecta a las quejas:
La asimetría se puede arreglar con Object.setPrototypeOf de ES6 o el más mal visto
__proto__ = ...
que yo uso. También podemos usar super en objetos regulares ahora, como se ve enPoint3D.init()
. Otra forma sería hacer algo comoaunque no me gusta particularmente la sintaxis.
Siempre podemos simplemente envolver
p = Object.create(Point)
y luegop.init()
en un constructor. ejPoint.create(x,y)
. Usando el código anterior podemos crear unaPoint3D
"instancia" de la siguiente manera.Se me ocurrió este truco para emular métodos estáticos en OLOO. No estoy seguro de si me gusta o no. Requiere llamar a una propiedad especial en la parte superior de cualquier método "estático". Por ejemplo, hice que el
Point.create()
método fuera estático.Alternativamente, con los símbolos ES6 puede extender de forma segura las clases base de Javascript. Así que podría guardarse algo de código y definir la propiedad especial en Object.prototype. Por ejemplo,
fuente
@james emanon - Entonces, te refieres a la herencia múltiple (discutida en la página 75 en el libro "No conoces JS: esto y prototipos de objetos"). Y ese mecanismo lo podemos encontrar en la función "extender" de subrayado, por ejemplo. Los nombres de los objetos que mencionaste en tu ejemplo son una mezcla de manzanas, naranjas y dulces, pero entiendo el punto detrás. Desde mi experiencia, esta sería la versión OOLO:
Es un ejemplo simple, pero el punto que se muestra es que solo estamos encadenando objetos en una estructura / formación bastante plana y todavía tenemos la posibilidad de usar métodos y propiedades de múltiples objetos. Logramos lo mismo que con el enfoque de clase / "copiar las propiedades". Resumido por Kyle (página 114, "esto y prototipos de objetos"):
Entiendo que la forma más natural para usted sería establecer todos los objetos "padre" (cuidado :)) en un lugar / llamada de función en lugar de modelar toda la cadena.
Lo que requiere es un cambio en el pensamiento y el modelado de problemas en nuestras aplicaciones de acuerdo con eso. También me estoy acostumbrando. Espero que ayude y el veredicto final del propio Kyle sería genial. :)
fuente
@Marcus, al igual que tú, me ha gustado OLOO y tampoco me gusta la asimetría descrita en tu primer punto. He estado jugando con una abstracción para recuperar la simetría. Puede crear una
link()
función que se utilice en lugar deObject.create()
. Cuando se usa, su código podría verse así ...Recuerde que
Object.create()
tiene un segundo parámetro que se puede pasar. Aquí está la función de enlace que aprovecha el segundo parámetro. También permite un poco de configuración personalizada ...Por supuesto, creo que @Kyle no respaldaría el sombreado de la
init()
función en el objeto Point3D. ;-)fuente
Object.assign()
conObject.create()
, podemos simplificar enormemente lalink()
función anterior. En su lugar, se podría utilizar esto:function create(delegate, props) { return Object.assign(Object.create(delegate), props); }
. O mejor aún, podemos usar subrayado o Lodash para que sea realmente concisa:_.create(delegate, props)
.¿Hay alguna manera de OLOO más de "dos" objetos ... todos los ejemplos consisten en el ejemplo basado (ver el ejemplo de OP). Digamos que tenemos los siguientes objetos, ¿cómo podemos crear un "cuarto" objeto que tenga los atributos de los "otros" tres? ala ...
estos objetos son arbitrarios y pueden abarcar todo tipo de comportamientos. Pero la esencia es que tenemos 'n' número de objetos, y nuestro nuevo objeto necesita algo de los tres.
en lugar de los ejemplos dados ala:
PERO, nuestro newObject = (Botón, Bicicleta, Zapato) ......
¿Cuál es el patrón para que esto funcione en OLOO?
fuente
Object.assign()
- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Si escribe en ES5, puede utilizar Underscore's_.extend()
o Lodash's_.assign()
. Aquí hay un excelente video para explicar ... youtu.be/wfMtDGfHWpA . Si tiene propiedades en colisión, gana la última, por lo que el orden es importante.