2015-02-08 16 views
5

Provare a ottenere la media di un array.Perché la funzione media basata sulla riduzione restituisce NaN?

Array.prototype.average = function() { 
    var sum = 0; 
    this.reduce(function(a, b) { 
     sum = a + b; 
    }); 
    return sum/this.length; 
}; 

[2, 15, 7].average(); 

Perché il ritorno chiamata di funzione averageNaN?

+1

dal codice, si potrebbe supporre che si prendono di mira un browser moderno. Suggerirei di usare 'Object.defineProperty' per estendere' Array.prototype'. (e testando che il metodo non esiste prima). Si potrebbe persino rendere il codice più generico per funzionare '(call/apply)' con altri oggetti. – Xotic750

risposta

10

Il tuo programma non ha funzionato perché, a ha il valore accumulato dalla precedente chiamata di funzione. La prima volta, verranno utilizzati i primi due valori dell'array. Quindi sum diventerà 17 (2 + 15). Poiché non si restituisce nulla dalla funzione, verrà restituito undefined, per impostazione predefinita, e verrà utilizzato come valore per a nella chiamata successiva. Quindi, la valutazione va come questo

a: 2,   b: 15 => 17 
a: undefined, b: 7 => NaN 

Quindi, sum avrà NaN, dal momento undefined + 7 rende così. Qualsiasi operazione numerica su NaN, darà sempre NaN, ecco perché NaN/this.length, fornisce NaN. È possibile correggere il programma, semplicemente restituendo il valore corrente di sum ogni volta che viene chiamata la funzione, in modo che alla chiamata di funzione successiva, a abbia il valore accumulato correttamente.

Array.prototype.average = function() { 
    var sum = 0; 
    this.reduce(function(a, b) { 
     sum = a + b; 
     return sum; 
    }); 
    return sum/this.length; 
}; 

Ma non stiamo facendo uso del potere e la flessibilità di reduce qui. Qui ci sono due punti importanti da considerare quando si utilizza reduce.

  1. reduce accetta un secondo parametro che indica il valore iniziale da utilizzare. Quando è possibile, specificalo.

  2. Il primo parametro nella funzione passata a reduce accumula il risultato e questo verrà restituito, infine, sfruttarlo. Non è necessario utilizzare una variabile separata per tenere traccia dei risultati.

Così il vostro codice sarà meglio così

Array.prototype.average = function() { 

    var sum = this.reduce(function(result, currentValue) { 
     return result + currentValue 
    }, 0); 

    return sum/this.length; 

}; 

console.log([2, 15, 7].average()); 
# 8 

reduce in realtà funziona in questo modo. Esso scorre l'array e passa il valore corrente come secondo parametro alla funzione e il risultato accumulato corrente come primo parametro e il valore restituito dalla funzione verrà memorizzato nel valore accumulato.Così, la somma è effettivamente trovato in questo modo

result: 0 , currentValue: 2 => 2 (Initializer value `0`) 
result: 2 , currentValue: 15 => 17 
result: 17, currentValue: 7 => 24 

Da quando è a corto di valori dalla matrice, 24 verrà restituito come risultato della reduce, che sarà memorizzato in sum.

6

La funzione aggiunta anonima non restituisce alcun valore, reduce lavora con funzioni che restituiscono un valore:

Prova:

Array.prototype.average = function() { 
    var sum = this.reduce(function (a, b) { 
     return a + b; 
    }, 0); 
    return sum/this.length; 
}; 

Un'altra possibilità è che l'array contiene stringhe invece di numeri, in tal modo si potrebbe volere costringerli ai numeri con return (+a) + (+b); come se si dispone di "10.0" e "20.0", l'aggiunta di loro dà "10.020.0", che diviso per qualsiasi numero di nuovo dà NaN.

1

Qualsiasi valore venga restituito da riduzione, diventa il primo parametro nella chiamata successiva, quindi è necessario restituire qualcosa. Inoltre, se stai estendendo i prototipi di proposito, assicurati di controllare prima l'esistenza per non sovrascrivere il metodo di qualcun altro.

Non è necessario creare altre variabili nel corpo della funzione poiché possono essere tutte implementate in un'unica riga.

if(!Array.prototype.average) { 
    Array.prototype.average = function() { 
    return this.reduce(function(a, b){ return a + b; })/this.length; 
    }; 
} 

Si noti inoltre che il secondo parametro di ridurre non è molto utile quando sommando i numeri, a meno che non si sta cercando di riassumere da un numero diverso da zero, che non è esattamente somma di una serie di numeri.

C'è più informazioni MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

2

Hai già la risposta alla tua domanda fornito da altri, ma ho pensato che avevo appena spieghi il mio commento con un esempio.

if (!Array.prototype.average) { 
 
    Object.defineProperty(Array.prototype, 'average', { 
 
     value: function() { 
 
      if (typeof this === 'undefined' || this === null) { 
 
       throw new TypeError('Cannot convert argument to object'); 
 
      } 
 

 
      var object = Object(this); 
 

 
      return [].reduce.call(object, function (result, currentValue) { 
 
       return +(currentValue) + result; 
 
      }, 0)/object.length; 
 
     } 
 
    }); 
 
} 
 

 
var out = document.getElementById('out'); 
 

 
out.textContent += [2, 15, 7].average() + '\n'; 
 
out.textContent += [].average.call({ 
 
    0: '2', 
 
    1: '15', 
 
    2: '7', 
 
    length: 3 
 
}) + '\n'; 
 
out.textContent += [].average.call('123') + '\n';
<pre id="out"></pre>

Problemi correlati