首页 > 解决方案 > 如何找出 foreach 循环中发生的错误?

问题描述

我正在按照教程使用模块构建预算计算。对于模块budgetController中的函数calcTotal之一,该人使用forEach 方法,我想将其重写为for 循环。在这个函数中,我试图计算用户在网站上输入的所有费用和收入的总和。该值被传递给budgetController 模块中的data.object。我在下面的完整代码中插入了不同的注释,以使其尽可能容易理解。

    calcTotal = function(type){
    var sum = 0;

    data.allItems[type].forEach(function(cur){
    sum += cur.value;});

    data.totals[type] = sum;
    }

    var data = {
    allItems: {
    exp: [],
    inc: []
    },
    totals : {
    exp: 0,
    inc: 0 
    },
    budget: 0,
    percentage: -1
    };

上面的代码工作正常,但我尝试对 for 循环做同样的事情,但由于某种原因无法正常工作。

任何人都可以将函数 calcTotal 中的 forEach 方法重写为 for 循环,这样我就可以看到我做错了什么?这是完整的代码:

var budgetController = (function(){

var Expense = function(id, description, value){
this.id = id;
this.description = description;
this.value = value;
};

var Income = function(id, description, value){
this.id = id;
this.description = description;
this.value = value;
};


calcTotal = function(type){
var sum = 0;

data.allItems[type].forEach(function(cur){
sum += cur.value;});

data.totals[type] = sum;
}

var data = {
allItems: {
exp: [],
inc: []
},
totals : {
exp: 0,
inc: 0 
},
budget: 0,
percentage: -1
};

return{

addItem: function(type, des, val){
var newItem
var ID = 0;
if(data.allItems[type].length > 0 ){
ID = data.allItems[type][data.allItems[type].length - 1].id +1;
}
else{ID = 0};
//create new item based on exp or inc type
if (type === "exp"){
newItem = new Expense(ID, des, val)
}
else if(type === "inc"){
newItem = new Income(ID, des, val);
}
//Push it into our data structure
data.allItems[type].push(newItem);
//returning the new element
return newItem;
},
calculateBudget: function(){
//calculate total income and expenses
calcTotal("exp");
calcTotal("inc");
// calculate the totalBudget
data.budget = data.totals.inc - data.totals.exp;
// calculate the pecentage;
data.percentage = Math.round((data.totals.exp / data.totals.inc) * 100);
},
testing: function(){
console.log(data);
},
getBudget: function(){

return{
budget: data.budget,
expenses: data.totals.exp,
income: data.totals.inc,
percentage: data.percentege

}
}

}

})()


var UIcontroller = (function(){

getDOM = {
inputValue: ".add__value",
inputDescription: ".add__description",
inputType: ".add__type",
addButton: ".add__btn",
expensesList: ".expenses__list",
incomeList: ".income__list"
};
return {
getInput: function(){

return{
value: parseFloat(document.querySelector(getDOM.inputValue).value),
description: document.querySelector(getDOM.inputDescription).value,
type: document.querySelector(getDOM.inputType).value,

};


},

getDomStrings: function(){
return getDOM;
},
displayListItem: function(type, obj){

var html, newHtml, element

if(type === "exp"){

element = getDOM.expensesList;

html = '<div class="item clearfix" id="expense-%id%><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__percentage">21%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
}
else if(type === "inc"){

element = getDOM.incomeList;

html = '<div class="item clearfix" id="expense%id%"><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__percentage">10%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
}
newHtml = html.replace("%id%", obj.id);
newHtml = newHtml.replace("%description%", obj.description);
newHtml = newHtml.replace("%value%", obj.value)

document.querySelector(element).insertAdjacentHTML("beforeend", newHtml);
},


clearFields :function(){
var fields, arrayField
fields = document.querySelectorAll(getDOM.inputValue +"," + getDOM.inputDescription);
arrayField = Array.prototype.slice.call(fields);

fields.forEach(function(current, index, array){
current.value = "";
});
arrayField[0].focus();
}



}
})()


var controller = (function(budgetCntrl, cntrlUI){
var updateBudget = function(){
var budget

// Calculate the Budget

var calcBudget = budgetCntrl.calculateBudget();
// Return the Budget
budget = budgetCntrl.getBudget();
console.log(budget);
//Display the Budget in UI
}


var addItem = function(){
var input, newItem, addItems, clearFields
// Get the file input data
input = cntrlUI.getInput();
// add new Item to the budget Controller
newItem;
if(input.description !=="" &&  !isNaN(input.value) && input.value > 0){    
newItem = budgetCntrl.addItem(input.type, input.description, input.value);
// display Items in the user interface
addItems = cntrlUI.displayListItem(input.type, newItem);
// clear Fields
clearFields = cntrlUI.clearFields();
updateBudget();

// calculate the budget
// display Budget in the user interface
}
}
var setupEventListener = function(){
var DOM = cntrlUI.getDomStrings();
document.querySelector(DOM.addButton).addEventListener("click", addItem);
}
return{
init: function(){
console.log("app has started");
setupEventListener();
}
}


})(budgetController, UIcontroller)

controller.init();

我希望我很清楚。

标签: javascript

解决方案


这段代码:

calcTotal = function(type){
var sum = 0;

data.allItems[type].forEach(function(cur){
sum += cur.value;});

data.totals[type] = sum;
}

可以重写为:

function calcTotal(type){
   var sum = 0;
   for (let i = 0; i < data.allItems[type].length; i++) {
     sum += data.allItems[type][i].value;
   }
   data.totals[type] = sum;
}

完整来源:https ://jsfiddle.net/gpj40raf/2/

但是,我可以给你一些代码审查建议吗?

  • calcTotal取决于封闭环境中定义的数据。这不是一个好主意。最好data作为参数传递(在某些情况下使用闭包很好,但这不是其中之一)。完整代码中的错误之一是calcTotal取决于下面定义的值。由于 JavaScript 提升,这将起作用,但不是一个好的做法。

  • 请注意,forEach代码依赖于每个值都在一个value属性中,但代码的其余部分假设值是数字(即calculateBudget)。

  • 总数的计算可以很容易地抽象出来,而不依赖于特定的data“形状”。例如:data.totals['type']=calcTotal(data.allItems['type'])。这很容易理解发生了什么。

  • 看看数组函数map// reducefilter他们所做的是以更具声明性的方式抽象某些模式。例如,要对值求和,您可以使用:(values.reduce((total, value)=>total+value, 0)在一行中,您可以表示与 相同calcTotal)。

  • 看看 ES6 结构。今天所有的新浏览器都支持这一点,您将能够使用const/ let、字符串文字以及classforExpenseIncome.... 代码将更短且易于阅读。


推荐阅读