2010-10-23 12 views
15

Sto guardando attraverso il sorgente MooTools per cercare di capire i suoi programmi di utilità .implement() e .extend().Cosa fa Function.prototype.overloadSetter() di MooTools?

La definizione di ogni fa riferimento a una funzione definita in questo modo:

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; 
    }; 
}; 

Tuttavia, sto avendo un momento difficile comprendere ciò che fa.

Puoi spiegare come funziona questa funzione e cosa fa?

risposta

29

overloadSetter

overloadSetter, insieme con overloadGetter, sono due metodi funzione decoratore. La funzione overloadSetter viene utilizzata per trasformare le funzioni che hanno la firma fn(key, value) in funzioni che potrebbero accettare argomenti oggetto, ad esempio: fn({key: value}).

Per fare ciò, overloadSetter deve includere la funzione originale. Questa funzione wrapper ha la firma fn(a, b) che è una scorciatoia per fn(key, value). Questa diventa effettivamente la nuova versione sovraccaricata della funzione originale.

Per prima cosa questa funzione di overload è controllare se l'argomento passato key (a) è di tipo stringa o no. Se non è una stringa, la funzione presuppone che stiamo passando un oggetto. Quindi itera su ogni coppia chiave-valore nell'oggetto e applica la funzione originale ad esso. Se si tratta di una stringa, d'altra parte, si applica semplicemente la funzione ai valori degli argomenti a e b.

Esempio

Per illustrare, diciamo abbiamo la seguente funzione:

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

var fnOver = fnOrig.overloadSetter(); 

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

Nella prima chiamata, la funzione fnOver viene richiamato con due argomenti, una chiave e un valore. Quando la funzione controlla il tipo del valore dell'argomento a, vedrà che si tratta di una stringa. Pertanto, invocherà semplicemente la funzione originale fnOrig: fnOrig.call(this, 'fruit', 'banana'). La nostra uscita della console è 'fruit: banana'.

Per la seconda chiamata, la funzione fnOver viene richiamato con un argomento oggetto. Poiché abbiamo passato un oggetto anziché una stringa, fnOver eseguirà l'iterazione attraverso i membri di questo oggetto e richiamerà la funzione fnOrig per ognuno di essi. Pertanto, fnOrig verrà invocato due volte in questo caso: fnOrig.call(this, 'fruit', 'banana') e fnOrig.call(this, 'vegetable', 'carrot'). L'output della nostra console è 'fruit: banana' e 'vegetable: carrot'.

Extra

All'interno della funzione wrapper, vedrai che ci sia un controllo per il valore di usePlural. Questo è un argomento per il metodo overloadSetter stesso. Se si imposta questo valore su true, la nuova funzione tratterà tutti gli argomenti come oggetto. Ciò significa che anche se si passa un argomento chiave di stringa, verrà comunque elaborato come oggetto.

L'altra cosa, il codice enumerables che prelude alla dichiarazione di metodo effettivo, è lì perché risolve un problema con alcuni browser in cui i nativi Object metodi non sono elencati in for/in loop anche se l'oggetto in sé implementa la propria versione di esso .

+6

anche se questo è un po 'improbabile_ per cambiare, devo sottolineare che questo è un metodo privato/interno e il codice di scrittura che dipende dall'API potrebbe non superare la prova del tempo (funziona al momento 1.3) –

+0

non documentato, quindi niente promesse. utilizzare a proprio rischio :) – seanmonstar

+1

Grazie per l'ottima risposta. @Dimitar, seanmonstar: Va bene, non ho intenzione di usarlo, volevo solo capire come funzionava la funzione e cosa ha fatto. –

3

La parte che mi aveva graffiare la mia testa per un po 'era la parte

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

, che si rivela essere un test per il bug DontEnum che alcuni browser hanno. A prima vista sembra che deve solo impostare enumerables a null, ma con il bug DontEnum toString viene soppresso (a torto, perché l'oggetto prototype.toString ha la bandiera DontEnum) e enumerables è lasciato come true.

overloadSetter (o piuttosto la funzione risultante) quindi deve controllare uno alla volta per le sette proprietà che il bug di DontEnum influisce, per vedere se esistono nell'argomento oggetto.

+0

E se ti chiedi cosa succede se cerchi di impostare la proprietà 'hasOwnProperty' in presenza del bug di DontEnum, hai ragione: il controllo per le proprietà interessate utilizzerà il valore fornito per' hasOwnProperty', che non è quasi certamente quello che volevi, anche se in qualche modo trovi una buona ragione per scavalcare la funzione del prototipo. – LHMathies