2011-09-21 10 views

Respuesta

16

Mixins no funcionan con instanceof sino que se extienden do. Mixins permite herencia múltiple pero fingiendo, no encadenando correctamente los prototipos.

Mostraré un ejemplo de Ext-JS pero el concepto se aplica a cualquier biblioteca de clases que proporcione mixins, todas solo copian propiedades al objeto en lugar de encadenar el prototipo.

Ext.define('Ext.Window', { 
    extend: 'Ext.Panel', 
    requires: 'Ext.Tool', 
    mixins: { 
     draggable: 'Ext.util.Draggable' 
    } 
}); 

Ext.Window instanceof Ext.Panel //true 
Ext.Window instanceof Ext.util.Draggable // false 

Mixins son una gran manera de añadir alguna funcionalidad a un objeto sin tener que recurrir a la herencia. Si tiene que heredar algo para obtener alguna funcionalidad, entonces no puede usar la funcionalidad de dos clases. Many people believe it's evil.

Ext-JS experimentó ese problema cuando quisieron agregar la funcionalidad Labelable a FieldSet y otras que no se ingresaron como campos. No había forma de que se pudiera beneficiar del comportamiento Labelable dentro de Field ya que no podían extender Field ya que también tenía todo el comportamiento de entrada.

+1

Esta respuesta es bastante específica de Ext.js y no se aplica a muchas otras bibliotecas de JavaScript (jQuery y Subrayado son solo dos ejemplos). – natlee75

+0

@ natlee75 Mi respuesta es que las mixinas no usan la cadena de prototipos, y falla las pruebas 'instanceof', Ext-JS es una implementación de ellas (te permiten verificar si algo es mixin de una manera diferente). Por lo que yo sé, jquery o guión bajo no implementan la mezcla poniéndolas en la cadena del prototipo (solo copian propiedades). ¿Su comentario implica que jquery y guión bajo pueden tener mixins que funcionen correctamente con instanceof? Elabore/ –

+2

No, pero sus funciones 'extend' tampoco funcionan. Solo las "bibliotecas de clase" (como 'Ext.define') usan el término" extender "para la herencia prototípica. – Bergi

0

Editar para mayor claridad: Estos a veces son la misma cosa y otras veces no; mixin es un nombre de un patrón utilizado para reutilizar funciones entre múltiples objetos, y extender es más el algoritmo/método utilizado para hacerlo. Hay casos en los que son idénticos (por ejemplo, subrayados _.extend) y casos en los que difieren (la extensión de backbone.js).

En el caso en que difieran, extendería generalmente el prototipo al objeto que se está extendiendo, mientras que mixin copiaría una lista de métodos en el objetivo.

+0

Excepto cuando desee el comportamiento que le dan dos clases. No se puede usar la herencia para eso a menos que vaya a la herencia múltiple, que tiene su propio conjunto de problemas. –

+0

No estoy seguro de cómo está relacionado, ya que extender y mixin no están directamente relacionados con la herencia, etc., son solo un método para imitar ese comportamiento. – taxilian

+0

Solo estoy explicando el caso en el que no tienen el mismo comportamiento. –

2

Definitivamente puede crear mixins utilizando extends.

Mixins ofrecen todos los beneficios de la herencia múltiple, sin jerarquía (herencia de prototipos en JavaScript). Ambos le permiten reutilizar una interfaz (o conjunto de funciones) en múltiples objetos. Con mixins no encuentras el "problema de diamante" con el que puedes toparte con las relaciones entre padres e hijos.

El problema de diamante ocurre cuando un objeto hereda la misma función (o incluso el nombre de la función) de dos objetos. ¿Por qué? Si uno de esos dos objetos modificó la función, agregando funcionalidad (es decir, en Java llamada "super"), JavaScript ya no sabe cómo interpretar/combinar los dos métodos. Los mixins son una forma de evitar esta jerarquía. Definen la funcionalidad que puedes pegar en cualquier lugar. Mixins también típicamente no contiene datos propios.

Así que podría, por ejemplo, escribir un mixin con $.extend() en jQuery. var newmixin = $.extend({}, mixin1, mixin2) combinaría dos interfaces y las aplanaría (sobrescribir conflictos de nombres).

Aquí hay 3 cosas a considerar:

  1. ¿La combinación de las dos interfaces tiene "jerarquía", es decir. relación padre/hijo. En JavaScript esto significaría herencia prototípica.
  2. ¿Las funciones están copiadas o referenciadas? Si el método original cambia, ¿los métodos heredados también cambiarán?
  3. ¿Qué sucede cuando se combinan dos métodos del mismo nombre? ¿Y cómo se tratan estos conflictos?
5

El método extend es bastante común entre bibliotecas de JavaScript y generalmente es un método que simplemente permite que el código consumidor agregue todas las propiedades y métodos "propios" de uno o más objetos en un objeto de destino. El código suele ser bastante sencillo: itere sobre todas las claves propias de cada argumento más allá del primero y copie el valor almacenado allí al primer argumento.

"Mixin" hace referencia a un patrón de diseño en el que utiliza un objeto como un tipo de contenedor para un conjunto particular de propiedades y métodos que desea compartir en muchos objetos de su sistema. Por ejemplo, puede tener getters y setters de ancho y alto que puedan aplicarse a todos los componentes de UI en su aplicación y así crearía, en el caso de JavaScript, una función que puede ser instanciada con "new" o un objeto literal que tiene estos métodos. Luego puede usar una función de tipo "extender" para copiar estos métodos en cualquier cantidad de objetos en su sistema.

Underscore tiene un método de mezcla que es esencialmente una extensión donde todos los métodos de los objetos pasados ​​se agregan al objeto base de subrayado para su uso en el encadenamiento. jQuery hace algo similar con su método extend de jQuery.fn.

Personalmente, me gusta mantener extendido como está, un comportamiento de tipo "copiar todo de estos objetos a este objeto" teniendo un método de mezcla separado que acepta solo un único objeto fuente y luego trata todos los argumentos adicionales como nombres de propiedades y métodos para copiar (si solo se pasan el destino y la fuente sin más argumentos, entonces actúa como una extensión de fuente única).

p. Ej.

function mixin(target, source) { 
    function copyProperty(key) { 
     target[key] = source[key]; 
    } 

    if (arguments.length > 2) { 
     // If there are arguments beyond target and source then treat them as 
     // keys of the specific properties/methods that should be copied over. 
     Array.prototype.slice.call(arguments, 2).forEach(copyProperty); 
    } else { 
     // Otherwise copy all properties/methods from the source to the target. 
     Object.keys(source).forEach(copyProperty); 
    } 
} 
+1

¿Puede decirme por qué haría 'Array.prototype.slice.call (argumentos , 2) 'sobre' arguments.slice (2) '. Solo quiero saber cuándo es más útil. Gracias de antemano – cantfindaname88

+3

@ cantfindaname88: 'arguments', mientras que' Array'-like, no es una instancia 'Array', por lo que no tiene el método' .slice() '. –

Cuestiones relacionadas