最佳实践:循环内的函数

时间:2016-04-30 16:47:57

标签: javascript function loops

我得到了以下完美的代码。它的作用是:在表格中,当您将鼠标悬停在任何表格单元格上时,它会突出显示相应的表格标题单元格和表格第一列单元格。

  // Row & Column Highlight
 (function() {

     var gridCellRow = null,
         gridCellCol = null,
         tableElement = document.getElementsByClassName('inner_table');
     for (var i = 0, len_i = tableElement.length; i < len_i; i++) {
         if (tableElement[i].getElementsByClassName('row_label_cell').length > 0) {
             var gridCell = tableElement[i].getElementsByClassName('input_cell');
             for (var j = 0, len_j = gridCell.length; j < len_j; j++) {
                 function gridCellParents(currentCell) {
                     return gridCellRow = currentCell.parentNode.firstElementChild,
                         gridCellCol = currentCell.parentNode.parentNode.rows[0].cells[currentCell.cellIndex];
                 }
                 gridCell[j].addEventListener('mouseover', (function() {
                     gridCellParents(this);
                     gridCellRow.classList.add('highlight');
                     gridCellCol.classList.add('highlight');
                 }));
                 gridCell[j].addEventListener('mouseout', (function() {
                     gridCellRow.classList.remove('highlight');
                     gridCellCol.classList.remove('highlight');
                 }));
             }
         }
     }

 }());

然而,JSHint告诉我,

for (var j = 0, len_j = gridCell.length; j < len_j; j++) {
 function gridCellParents(currentCell) {
     return gridCellRow = currentCell.parentNode.firstElementChild,
         gridCellCol = currentCell.parentNode.parentNode.rows[0].cells[currentCell.cellIndex];
 }

不是最佳实践&#34;函数声明不应放在块中。使用函数表达式或将语句移动到外部函数的顶部。&#34;

以及

gridCell[j].addEventListener('mouseover', (function() {
gridCellParents(this);
gridCellRow.classList.add('highlight');
gridCellCol.classList.add('highlight');
}));
gridCell[j].addEventListener('mouseout', (function() {
    gridCellRow.classList.remove('highlight');
    gridCellCol.classList.remove('highlight');
}));
}

不是最佳做法&#34;不要在循环中制作函数。&#34;

那么我如何正确地根据最佳实践建立这个整体功能呢?

2 个答案:

答案 0 :(得分:2)

  

功能减速不应该在循环内,因为它没有   感觉一遍又一遍地重新创造同样的功能   “连续流动”(不同于其他相同功能的情况)   可能会在更复杂的代码中再次创建)。主要原因是   因为hoisting而且强烈反对javascript   在循环中编写函数声明的原则。

一个很好的起点,有一个更有序的代码:

     // Row & Column Highlight
(function() {
    var gridCellRow,
        gridCellCol,
        gridCell,
        tableElement = document.getElementsByClassName('inner_table');   

    function gridCellParents(currentCell) {
        gridCellRow = currentCell.parentNode.firstElementChild,
        gridCellCol = currentCell.parentNode.parentNode.rows[0].cells[currentCell.cellIndex];
    }

    function onMouseEnter() {
        gridCellParents(this);
        gridCellRow.classList.add('highlight');
        gridCellCol.classList.add('highlight');
    }

    function onMuoseLeave() {
        gridCellRow.classList.remove('highlight');
        gridCellCol.classList.remove('highlight');
    }


    for (var i = 0, len_i = tableElement.length; i < len_i; i++) {
        if (tableElement[i].getElementsByClassName('row_label_cell').length > 0) {
            gridCell = tableElement[i].getElementsByClassName('input_cell');                    
            for (var j = 0, len_j = gridCell.length; j < len_j; j++) {
                gridCell[j].addEventListener('mouseenter', onMouseEnter);
                gridCell[j].addEventListener('mouseleave', onMuoseLeave);
            }
        }
}}());

正如您所看到的,我已将您的活动修改为mousentermouseleave,这可能更适合您的需求,并且可以提升整体效果。

更新 - 委托版本:

 // Row & Column Highlight
(function() {
    var gridCell,
        tableElement = document.querySelectorAll('.inner_table');   

    function getCellParents(cell){
        return {
            row : cell.parentNode.firstElementChild,                       // row
            col : cell.parentNode.parentNode.rows[0].cells[cell.cellIndex] // col
       }; 
    }

    function updateGridCellParents(cell, state) {
        state = state ? 'add' : 'remove';

        var parents = getCellParents(cell);

        parents.row.classList[state]('highlight');
        parents.col.classList[state]('highlight');
    }

    funciton checkTarget(target){
        // make sure the element is what we expected it to be
        return target.className.indexOf('input_cell') != 0;
    }

    function onMouseEvents(e){
        checkTarget(e.target) && updateGridCellParents(e.target, e.type == "mouseover");
    }

    document.body.addEventListener('mouseover', onMouseEvents);
    document.body.addEventListener('mouseout', onMouseEvents);
})();

答案 1 :(得分:2)

除了上一个答案之外,我认为重要的是还要说明为什么这是一种不好的做法。

在循环内创建函数时的问题是它们经常使用依赖于循环迭代的值。让我们举个例子。

&#13;
&#13;
// Create three function, that writes their number
var funcs = [];
for(var i=0; i<3; i++){
  funcs.push(function(){
    document.write(i);
  });
}

// Call them.
funcs.forEach(function(f){
  f();
});
&#13;
&#13;
&#13;

可以预期上面的代码写1然后是2然后3.但是,因为JS中的变量不是块作用域而是函数作用域(新的letconst除外),所有这三个函数的闭包实际上将使用完全相同的i:3,它给出的最后一个值(因此它仍然具有的值)。

由于这种行为,这很容易出错。因此,不建议这样做。

如果您需要创建一个取决于循环值的函数,您可以使用工厂。

&#13;
&#13;
// Create a factory function that returns a
// function that writes the argument.
function writerFactory(msg){
  return function(){
    document.write(msg);
  }
}

// Create three functions, that write their number.
var funcs = [];
for(var i=0; i<3; i++){
  funcs.push(writerFactory(i));
}

// Call them.
funcs.forEach(function(f){
  f();
});
&#13;
&#13;
&#13;

这次,每个函数都有一个不同的闭包:每次调用工厂创建的闭包。他们都可以访问不同的msg