为什么这段代码这么慢?

时间:2009-05-10 10:32:57

标签: javascript performance

jsc.tools.road.correctType = function() {
    for(row = jsc.data.selection.startX - 1; row <= jsc.data.selection.endX + 1; row++) {
        for(col = jsc.data.selection.startY - 1; col <= jsc.data.selection.endY + 1; col++) {
            if(jsc.data.cells[row-1][col].type != "road" && jsc.data.cells[row+1][col].type != "road" && jsc.data.cells[row][col].type == "road") {
                jsc.ui.addClassToCell("horz", row, col);
            }
            else {
                jsc.ui.removeClassFromCell("horz", row, col);
            }
            if(jsc.data.cells[row][col-1].type != "road" && jsc.data.cells[row][col+1].type != "road" && jsc.data.cells[row][col].type == "road") {
                jsc.ui.addClassToCell("vert", row, col);
            }
            else {
                jsc.ui.removeClassFromCell("vert", row, col);
            }
        }
    }
};

// Elsewhere
jsc.ui.addClassToCell = function(class, x, y) {
    $("#" + x + "-" + y).addClass(class);
};
jsc.ui.removeClassFromCell = function(class, x, y) {
    $("#" + x + "-" + y).removeClass(class);
};

上面的代码运行得非常慢。我无法弄清楚为什么。它使用的是jQuery 1.3.2。有什么方法可以优化它吗?

编辑:代码是我作为个人项目制作的javascript游戏的一部分。它基本上是一个Simcity克隆。这段代码检查道路的每个部分的相邻单元,并将类(以及背景图像)改变为正确的单元以使道路图像正确排列,例如,水平,垂直和交叉(无类)道路图像。

编辑2:提供一些上下文的更多细节。

jsc.data.cells是一个200 x 200的数组。每个数组元素都是一个具有如此属性的对象(默认显示):{type:null,develop:false,powered:false,watered:false,hasTransport:false,wealth:0,quality:0}。

它的对手是在UI中,它基本上是一个巨大的表。 (再次200 x 200)。每个单元格在整个程序中都添加了许多CSS类,以更改背景图像(例如,将其更改为道路,.com.developed以使其成为开发的商业区域)。表格单元格的每个都具有#x-y形式的id,即jsc.ui.addClassToCell和jsc.ui.removeClassFromCell编辑。

编辑3:修正了以数字开头的ID。现在尝试一些优化。

5 个答案:

答案 0 :(得分:4)

使用O()表示法的简短估计:

for(row) ... O(N)
for(col) ... O(N)
$().addClass/removeClass ... O(N^2)

$()甚至在嵌套for。

中被调用两次

所以你最终得到O(N ^ 4)

您可以通过在jsc.data.cells [row] [col]的as属性中缓存计算出的类来优化它,例如

jsc.data.cells[row][col].horz = 1; // don't set class "horz" if not present
jsc.data.cells[row][col].vert = 1;

并在HTML表格中创建单元格时使用缓存数据,而不是为每个单元格调用$()。

答案 1 :(得分:2)

我只能提供一些提示,但不知道它们是否有用。无法测试您的代码。

1-st:在本地函数范围内声明变量。我的意思是行和col变量,你声明为全局变量(缺少var语句)。对全局变量的访问需要更长时间(AFAIK),而不是本地范围变量。

var row = jsc.data.selection.startX-1;

var col = jsc.data.selection.startY-1;

2-nd:缓存对公共对象的引用。在这里,您可以存储jsc.data和/或jsc.data.selection和jsc.data.cells的引用。 IIRC,对象属性的访问是线性的。

jsc.tools.road.correctType = function() {
   var data = jsc.data, selection = data.selection, cells = jsc.data.cells, ui.jsc.ui;

   for(var row = selection.startX - 1, endX = selection.endX + 1, endY = selection.endY + 1; row <= endX; ++row) {
      for(var col = selection.startY - 1; col <= endY; ++col) {
         if(cells[row-1][col].type != "road" && cells[row+1][col].type != "road" && cells[row][col].type == "road") {
            ui.addClassToCell("horz", row, col);
         } else {
            ui.removeClassFromCell("horz", row, col);
         }
         if(cells[row][col-1].type != "road" && cells[row][col+1].type != "road" && cells[row][col].type == "road") {
            ui.addClassToCell("vert", row, col);
         } else {
            ui.removeClassFromCell("vert", row, col);
         }
      }
   }
};

我还将endY变量的声明移动到外部循环,因此不会在每次访问内部循环时计算它。

- 编辑

希望您知道,ID属性值cannot start with a number与您一样,例如。 #2-3

答案 2 :(得分:2)

根据您选择的大小,您可能会进行大量的条件检查和DOM编辑。

通过注释掉addClassToCell和removeClassFromCell的内容并比较运行时间,您可以了解条件检查或dom编辑是否花费最多时间,从而确定哪一个是优化的最佳候选者。

答案 3 :(得分:2)

通常你可以显着优化这些循环;

for( var x = 0; x < someMethod(); x++ ) {
  //... do stuff
}

用这样的东西换掉它们

var x = someMethod();
while( x-- ) {
  //...do stuff
}

虽然它在语义上略有不同,但只要你不依赖循环中的顺序(顺序相反),它通常会很好地运作

即使你不能改变顺序,你也可以通过移动实际循环的someMethod调用OUT来显着改进代码,因为在许多JS实现中,每次迭代都会调用一次...

答案 4 :(得分:1)

使用memoizer或本地缓存来存储您已创建的jQuery对象。这将减少$函数调用的数量。

var cache = {}, selector;
for (/* … */) {
    selector = "#" + x + "-" + y;
    if (!cache[selector]) {
        cache[selector] = $(selector);
    }
    // cache[selector] refers to the same object as $("#" + x + "-" + y)
}