2010-10-23 11 views
15

Estoy buscando a través de la fuente MooTools para tratar de entender sus utilidades .implement() y .extend().¿Qué hace Function.prototype.overloadSetter() de MooTools?

La definición de cada una se refiere a una función definida de esta manera:

var enumerables = true; 
for (var i in {toString: 1}) enumerables = null; 
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; 

Function.prototype.overloadSetter = function(usePlural){ 
    var self = this; 
    return function(a, b){ 
     if (a == null) return this; 
     if (usePlural || typeof a != 'string'){ 
      for (var k in a) self.call(this, k, a[k]); 
      if (enumerables) for (var i = enumerables.length; i--;){ 
       k = enumerables[i]; 
       if (a.hasOwnProperty(k)) self.call(this, k, a[k]); 
      } 
     } else { 
      self.call(this, a, b); 
     } 
     return this; 
    }; 
}; 

Sin embargo, estoy teniendo un tiempo difícil entender lo que hace.

¿Puede explicar cómo funciona esta función y para qué sirve?

Respuesta

29

overloadSetter

overloadSetter, junto con overloadGetter, son dos métodos función decorador. La función overloadSetter se usa para transformar funciones que tienen la firma fn(key, value) en funciones que podrían aceptar argumentos de objeto, es decir: fn({key: value}).

Para hacer esto, overloadSetter debe envolver la función original. Esta función de contenedor tiene la firma fn(a, b), que es un atajo para fn(key, value). Esto efectivamente se convierte en la nueva versión sobrecargada de la función original.

Lo primero que hace esta función sobrecargada es comprobar si el argumento key pasado (a) es del tipo de cadena o no. Si no es una cadena, la función asume que estamos pasando un objeto. Por lo tanto, itera sobre cada par clave-valor en el objeto y le aplica la función original. Si es una cadena, por otro lado, simplemente aplica la función a los valores de los argumentos a y b.

Ejemplo

Para ilustrar, supongamos que tenemos la siguiente función:

var fnOrig = function(key, value){ 
    console.log(key + ': ' + value); 
}; 

var fnOver = fnOrig.overloadSetter(); 

fnOver('fruit', 'banana'); 
fnOver({'fruit': 'banana', 'vegetable': 'carrot'}); 

En la primera invocación, la función fnOver se invoca con dos argumentos, una clave y un valor. Cuando la función comprueba el tipo del valor de argumento a, verá que es una cadena. Por lo tanto, invocará simplemente la función original fnOrig: fnOrig.call(this, 'fruit', 'banana'). Nuestra salida de consola es 'fruit: banana'.

Para la segunda invocación, se invoca la función fnOver con un argumento de objeto. Como pasamos un objeto en lugar de una cadena, fnOver iterará a través de los miembros de este objeto e invocará la función fnOrig para cada uno de ellos. Por lo tanto, fnOrig se invocarán dos veces en este caso: fnOrig.call(this, 'fruit', 'banana') y fnOrig.call(this, 'vegetable', 'carrot'). Nuestra salida de consola es 'fruit: banana' y 'vegetable: carrot'.

Extras

Dentro de la función de contenedor, verá que hay un cheque por el valor de usePlural. Este es un argumento para el método overloadSetter. Si establece este valor en true, la nueva función tratará todos los argumentos como objetos. Esto significa que incluso si pasa un argumento de clave de cadena, aún se procesará como un objeto.

La otra cosa, el código enumerables que preludia la declaración del método real, está ahí, ya que corrige un problema con algunos navegadores en el que los Object métodos nativos no están enumerados en for/in bucles incluso si el objeto en sí implementa su propia versión de ella .

+6

aunque esto es algo poco probable de cambiar, debo señalar que este es un método privado/interno y escribir código que depende de la API puede no resistir el paso del tiempo (funciona en 1.3 por el momento) –

+0

no documentado, entonces no hay promesas utilizar bajo su propio riesgo :) – seanmonstar

+1

Gracias por la gran respuesta. @Dimitar, seanmonstar: Está bien, no estoy planeando usarlo, solo quería entender cómo funcionaba la función y qué hacía. –

3

La parte que me dejó rascándome la cabeza por un tiempo fue la parte

var enumerables = true; for (var i in {toString: 1}) enumerables = null;

, que resulta ser una prueba para el error DontEnum que algunos navegadores tienen. A primera vista parece que simplemente debe establecer enumerables a null, pero con el error DontEnum toString está suprimida (erróneamente, porque el objeto de prototype.toString tiene la bandera DontEnum) y enumerables se deja como true.

overloadSetter (o más bien la función resultante) luego tiene que comprobar uno a la vez para las siete propiedades que afecta el error DontEnum, para ver si existen en el argumento del objeto.

+0

Y si se pregunta qué pasa si intenta establecer la propiedad 'hasOwnProperty' en presencia del error DontEnum, tiene razón a: la comprobación de las propiedades afectadas usará el valor proporcionado para' hasOwnProperty', que es casi seguro que no es lo que querías, incluso si de alguna manera encuentras una buena razón para anular la función del prototipo. – LHMathies