Нужно добавить специальную функцию обертку, чтобы изолировать переменные.
item.addEventListener('change', (function(item, i) // функция обертка для функции обработчика, содержит переменные, которые нужно изолировать
{
return function() { //возврат функции-обработчика для лисенера
if(item.checked) {
selectedContactValues.push(parseInt(item.dataset.price));
countElements[i].value = 1;
} else {
selectedContactValues.splice(selectedContactValues.indexOf(item.dataset.price),1);
countElements[i].value = 0;
}
resultElement.textContent = sumArray(selectedContactValues);
}; // конец функции обработчика событий, переменные которые нужно пропустить через замыкание
})(item, i) // конец функции обертки
);
ПС: Возможно, тут нужно клонировать объект item:
})(JSON.parse(JSON.stringify(item)), i) // конец функции обертки
Данный способ склонирует только структуру и данные объекта без ссылок на функции и ссылок на DOM.
ППС: Или брать arr[i] внутри обертки в качестве item, тогда изолировать придется только i:
item.addEventListener('change', (function(i) // функция обертка для функции обработчика, содержит переменные, которые нужно изолировать
{
return function() { //возврат функции-обработчика для лисенера
let item = arr[i]; // берем item из внешнего массива arr, но по изолированному индексу i
if(item.checked) {
selectedContactValues.push(parseInt(item.dataset.price));
countElements[i].value = 1;
} else {
selectedContactValues.splice(selectedContactValues.indexOf(item.dataset.price),1);
countElements[i].value = 0;
}
resultElement.textContent = sumArray(selectedContactValues);
}; // конец функции обработчика событий, переменные которые нужно пропустить через замыкание
})(i) // конец функции обертки
);