2014-06-19 8 views
5

Sto lavorando per riscrivere il codice sottostante per molte delle funzioni standard di Underscore.js lavoro sulle mie competenze JavaScript e sono un po 'bloccato con lo _.every/_.all. Sembra che nella libreria stessa, la funzione _.every/ venga scritta solo utilizzando la funzione esistente _.each, ma sono incoraggiato a scrivere una versione utilizzando la mia versione di _.reduce (che incorpora già la mia versione di _.each). Ho fornito il codice per entrambe le funzioni di seguito.Come riscrivere _.every/_. Tutto da Underscore.js utilizzando _.reduce (e _.each)

Il primo test mia _.every funzione (vedi sotto pure) non è quella in cui sono passati tutti i falsi valori utilizzando la funzione _.identity (semplicemente valore inserito come argomento di ritorno) come iteratore:

Test:

it('fails for a collection of all-falsy results', function() { 
    expect(_.every([null, 0, undefined], _.identity)).to.equal(false); 
    }); 

ho alcune domande sul perché la mia funzione _.every sta fallendo la prova mostrato sopra, insieme a più di altri test (ad esempio, misti vero/falso valori, valori non definiti, etc.):

-Quando si chiama la funzione iteratore, devo usare iterator.call o iterator.apply? Se sì, quale uso e come posso specificare gli argomenti?

-Quale vantaggio è quello di utilizzare _.reduce qui anziché solo _.each, in particolare quando la libreria Underscore.js non utilizza _.reduce?

-Perché fa restituire la necessità di essere chiamato due volte, una volta che quando si chiama la funzione _.reduce, e una volta all'interno della funzione anonima definita all'interno _.reduce (ho anche chiesto questo quando si costruisce le funzioni che utilizzano la funzione _.map)? A me sembra che sto restituendo il risultato della funzione _.reduce, che sta già restituendo qualcosa.

_.every:

_.every = function(collection, iterator) { 
    // TIP: Try re-using reduce() here. 
    return _.reduce(collection, function(allFound, item) { 
     return iterator(item) && allFound; 
    }, true); 
    }; 

_.each:

_.each = function(collection, iterator) { 
    // define spec for arrays 
    if (Array.isArray(collection)) { 
    for(var i = 0; i < collection.length; i++) { 
     iterator(collection[i], i, collection); 
    } 
    } 

    // define spec for objects 
    else { 
    for(var key in collection) { 
     iterator(collection[key], key, collection); 
    } 
    } 
}; 

_.reduce:

_.reduce = function(collection, iterator, accumulator) { 

    // add condition to set accumulator if no explicit starting value is given. 
    if (arguments.length < 3) { 
     accumulator = collection[0]; 
    } 

    _.each(collection, function(value) { 
     accumulator = iterator(accumulator, value); 
    }); 

    return accumulator; 
    }; 

risposta

4

Il test non passa perché non è il ritorno false come previsto (anche se sta restituendo un valore falso).

_.every = function(collection, iterator) { 
    return _.reduce(collection, function(allFound, item) { 
     return iterator(item) && allFound; 
    }, true); 
}; 

Cosa succede quando si torna iterator(item) && allFound è che se iterator(item) è Falsey (ma non false), non tornerà false, ma il valore di iterator(item). Per verificarlo personalmente, apri un REPL e digita undefined && true; il risultato sarà undefined, non false.

Quindi, se si desidera che questo restituisca esplicitamente false e non solo un valore di falso, sarà necessario forzarlo su un valore booleano. È possibile eseguire Boolean(truthy_or_falsey_value) o !!truthy_or_falsey_value.Io di solito preferisco il secondo, in modo da cambiare l'implementazione nel seguente modo:

_.every = function(collection, iterator) { 
    return _.reduce(collection, function(allFound, item) { 
     return !!iterator(item) && allFound; 
    }, true); 
}; 

vostre altre domande:

Quando si chiama la funzione iteratore, ho bisogno di usare iterator.call o iterator.apply? Se sì, quale uso e come posso specificare gli argomenti?

Dipende da qual è il tuo obiettivo. e apply vengono principalmente utilizzati quando si desidera controllare il valore della parola chiave this nel corpo della funzione. Alcuni dei metodi di array incorporati di JavaScript (come Array.prototype.map e Array.prototype.filter) prendono uno thisArg, che è quello che viene fornito il callback utilizzando call o apply. Per quanto riguarda la differenza tra call e apply, è solo la modalità di gestione degli argomenti. Vedi this answer per maggiori dettagli.

Quali benefici c'è da utilizzando reduce qui piuttosto che solo each, soprattutto quando la biblioteca Underscore.js non usare reduce?

Probabilmente nessuno, o molto poco. Ci potrebbe essere una differenza di prestazioni, ma il modo migliore per scoprire sarebbe quello di profilare entrambi gli approcci.

Perché restituire la necessità di essere chiamato due volte, una volta che quando si chiama la funzione _.reduce, e una volta all'interno della funzione anonima definita all'interno _.reduce

Se si desidera una funzione - qualsiasi function - per restituire un valore, è necessario chiamare return da quella funzione. Non è possibile aspettarsi di chiamare return da una funzione interna e aspettarsi che la funzione di inclusione comprenda magicamente che si suppone, a sua volta, restituisca il valore della funzione chiamata. Alcune lingue predefinite per restituire il valore dell'ultima espressione nella funzione se non si chiama esplicitamente return, che è conveniente o confuso, a seconda della prospettiva. Se hai esperienza con un tale linguaggio (Ruby, per esempio), allora tutte le dichiarazioni return potrebbero sembrare un po 'eccessive per te.

Come nota editoriale, ritengo che iterator sia una scelta di denominazione insufficiente per la funzione di test. In realtà non sta iterando su qualcosa (la funzione per cui è un argomento sta facendo un'iterazione). Un nome migliore potrebbe essere il molto generico callback o cb. Il termine "predicate" indica una funzione che associa un valore a true o false, che è la mia terminologia preferita. Un'altra scelta comune è semplicemente test, poiché è, dopo tutto, solo una funzione che sta eseguendo un filtro binario sul suo argomento.