2013-02-14 16 views
16

metodi JavaScript Array come forEach hanno un parametro thisArg, che viene utilizzato come contesto per invocare la richiamata:Perché Array.prototype.reduce non ha un parametro thisObject?

array.forEach(callback[, thisArg]) 

come fanno every, some, filter e map. Tuttavia, reduce e reduceRight non hanno questo parametro. C'è qualche ragione particolare per questo, o qualche ragione per cui non è necessario?

Ad esempio, si consideri la seguente implementazione di composizione funzionale utilizzando reduceRight:

function compose() { 
    var fns = [].slice.call(arguments,0); 
    return function result() { 
     return fns.reduceRight(
      function (prev,cur){ 
       return [cur.apply(this,prev)]; 
      }, 
      arguments 
     )[0]; 
    }; 
} 

Vorrei rendere questo "questo-consapevole", quindi le funzioni da composti sono chiamati nel contesto in cui la funzione restituito da compose viene invocato. Attualmente sembrano essere invocati nel contesto dell'oggetto globale. Potrei fare il vecchio var self=this; nella parte superiore della funzione result, e usarlo come primo argomento della chiamata , dove attualmente ho this, ma che non sarebbe necessario se lo reduce avesse un argomento thisArg.

Mi manca qualcosa qui e c'è qualcosa di reduce che rende questo non necessario o inutile?

UPDATE

@kangax Sì, che mi venne in mente. Lungi da me criticare il design dell'API, ma la firma per lo reduce mi sembra un po 'strana. Il secondo argomento opzionale funziona in modo diverso rispetto ai normali argomenti facoltativi, che in genere hanno solo un valore predefinito; invece la sua presenza o assenza modifica il comportamento, essenzialmente sovraccarico della funzione basata sulla firma (numero di argomenti). Quando il secondo parametro è assente, il primo elemento dell'array diventa il valore iniziale e la prima chiamata al callback è contro il secondo valore. Mi sembra che questo comportamento potrebbe essere facilmente emulato semplicemente chiamando

array.slice(1).reduce(fn,array[0]) 

invece di costruire nelle regole speciali per il caso in cui si omette il secondo argomento, che a sua volta, se la vostra presunzione è corretto, reso anche essenzialmente impossibile capire dove specificare l'argomento thisArg. Poi di nuovo, sono sicuro che tali questioni erano già state discusse mentre le specifiche erano state messe a tacere, e potrebbero esserci buone ragioni per un simile approccio.

+0

Probabilmente perché il secondo argomento in 'reduce' è il valore iniziale – kangax

+0

C'è una ragione per cui è necessario farlo con' reduceRight'? Penso che si possa fare altrettanto facilmente con 'forEach' su un array' reverse'd. Edit: Credo che il mio punto sia che non stai cercando di ridurre la gamma di funzioni, ma piuttosto di eseguire un set di input attraverso le funzioni. – tJener

+1

@tJener Ovviamente lo si può fare con forEach (come fa undercore.js), ma ho trovato una certa eleganza nell'uso di riduci/Destra. –

risposta

4

Essa diventa disordinato con due argomenti opzionali, poiché reduce (Right) comprende già due funzionalità (vedi Wikipedia), che si distinguono in lingue puri (per esempio chiamato foldl e foldl1 in Haskell).Per citare Brendan Eich:

Quindi questo significherebbe ridurre accetta un argomento di callback e due argomenti opzionali: thisObject e init. Quale dovrebbe venire prima? Il più comune è probabilmente init, ma poi si separa il callback arg dall'argomento "thisObject", che è forse ok. Più argomenti opzionali sono un pò disordinato in questo modo ...

In alternativa, potremmo semplicemente eliminare quel "thisObject" argomento in più, dal momento che le persone possono sempre [vincolante uso].

non credo che sia un grosso problema, dal momento che questi di ordine superiore funzioni funzionali sono per lo più utilizzati con lamdba--funzione espressioni in ogni caso (come nel tuo esempio). Certo, c'è un po 'di incoerenza, ma possiamo conviverci. Immagine l'alternativa:

array.reduce(callback[, initialValue[, thisArg]]) 

Non può essere realmente usato, non possiamo davvero definire "if an initialValue was provided", in quanto questo significa arguments.length < 2 - siamo riusciti a passare undefined letteralmente pure. Quindi, questo significa

array.reduce(callback[, thisArg[, initialValue]]) 

che è brutto da quando abbiamo sempre bisogno di passare null o qualcosa da thisArg se volevamo solo un valore iniziale.

notato che già nel tuo commento a Kangax ("Le seconde funzioni opzionali argomento in modo diverso rispetto al normale argomenti opzionali, [...] la sua presenza o assenza cambia il comportamento"), ma non in grado di supportare la sua dichiarazione

questo comportamento può essere facilmente emulato semplicemente chiamando array.slice(1).reduce(fn,array[0])

come che a) non funziona con un'espressione complessa (concatenato) invece della variabile array e b) è ingombrante.

3

Sulle Es-discuss mailing list che trovate in risposta http://article.gmane.org/gmane.comp.lang.javascript.ecmascript4.general/4770

Gli altri metodi con callback prendere un 'thisArg' non perché è necessario o addirittura utile, ma per la compatibilità, perché già lo fanno nelle implementazioni esistenti che forniscono queste funzioni.

+0

Risultato di ricerca piacevole, tuttavia trovo le altre mail dalla discussione più rilevanti – Bergi

+0

Al momento della discussione (marzo 2009), EcmaScript 5 era praticamente stabile, subendo solo modifiche editoriali. JavaScript come sappiamo oggi è stato risolto entro la fine del 2008, insieme a qualsiasi progetto razionale. È così che ho letto la domanda dell'OP. –

2

si può sempre utilizzare questo metodo:

array.reduce(callback.bind(context), initialValue); 

... al fine di collegare un contesto specifico alla funzione di callback.

Problemi correlati