2015-02-08 5 views
5

Попытка получить среднее значение массива.Почему моя сокращенная средняя функция возвращает NaN?

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

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

Почему average функция возврата вызова NaN?

+1

Из кода, можно было бы предположить, вы ориентируетесь современный браузер. Я бы предложил использовать 'Object.defineProperty' для расширения' Array.prototype'. (и тестирование того, что метод не существует в первую очередь). Вы даже можете сделать код более общим для работы '(call/apply)' с другими объектами. – Xotic750

ответ

10

Ваша программа не работает, потому что a имеет накопленное значение из предыдущего вызова функции. В первый раз будут использоваться первые два значения массива. Таким образом, sum станет 17 (2 + 15). Поскольку вы ничего не возвращаете из функции, по умолчанию будет возвращен undefined, который будет использоваться как значение для a, в следующем вызове. Таким образом, оценка идет как этот

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

Итак, sum будет NaN, так как undefined + 7 делает это так. Любая цифровая операция на NaN, всегда будет давать NaN, поэтому NaN/this.length, дает вам NaN. Вы можете исправить свою программу, просто вернув текущее значение sum всякий раз, когда вызывается функция, так что при следующем вызове функции a будет иметь надлежащее накопленное значение.

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

Но мы не делаем использование силы и гибкости reduce здесь. Вот два важных момента, которые следует учитывать при использовании reduce.

  1. reduce принимает второй параметр, который говорит, что начальное значение будет использоваться. По возможности укажите это.

  2. Первый параметр в функции, переданной в reduce, накапливает результат и, наконец, будет возвращен, используйте это. Не нужно использовать отдельную переменную, чтобы отслеживать результаты.

Так что ваш код будет выглядеть лучше, как этот

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 фактически работает как это. Он выполняет итерацию через массив и передает текущее значение в качестве второго параметра функции и текущий накопленный результат в качестве первого параметра, а значение, возвращаемое функцией, будет храниться в накопленном значении.Таким образом, сумма фактически находится как этот

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

Поскольку выбежала из значений из массива, 24 будут возвращены в результате reduce, который будет храниться в sum.

6

Ваш анонимная функция добавления не возвращает никакого значения, reduce работает с функциями, которые возвращают значение:

Try:

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

Другая возможность состоит в том, что ваш массив содержит строки вместо чисел, таким образом, вам может захотеть принудить их к цифрам с return (+a) + (+b); как будто у вас есть "10.0" и "20.0", добавив их вместе, давая "10.020.0", который поделен на любое число, снова дает NaN.

1

Независимо от того, какое значение возвращается из сокращения, оно становится первым параметром в следующем вызове, поэтому вам нужно что-то вернуть. Кроме того, если вы намеренно расширяете прототипы, убедитесь, что сначала проверяете наличие, чтобы вы не переопределяли чужой метод.

Нет необходимости создавать какие-либо другие переменные в теле функции, так как все они могут быть реализованы в одной строке.

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

Также обратите внимание, что второй параметр снижения не очень полезно при суммировании числа, если вы не пытаетесь подвести итог из числа, отличного от нуля, который точно не суммирующий набора чисел.

Существует больше информации о MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

2

У вас уже есть ответ на ваш вопрос, предоставленной другими, но я думал, что просто расширить на мой комментарий с примером.

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>

Смежные вопросы