5

所以我正在阅读关于洗牌的内容。然后我遇到了这个脚本

shuffle = function(o){ //v1.0
    for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
};

当我仔细观察时,for它甚至根本没有{}!但它很有效,就像魔术一样。我很想知道它是如何工作的。(还有一堆逗号。)

4

6 回答 6

11

后面的for ()可以是任何语句;可以是带有花括号的东西,也可以是单个表达式,也可以是空表达式。for (...);相当于for (...) {}。当然,这只能与自然终止的 for 循环一起使用,否则您将手上有一个无限循环。

逗号实际上是二级分号;它们生成大部分单独的语句,但它们可以在 for 循环中工作(以及其他地方;这是对它们的非常草率的定义)。

for (
     // initialisation: declare three variables
     var j, x, i = o.length;
     // The loop check: when it gets to ``!i``, it will exit the loop
     i;
     // the increment clause, made of several "sub-statements"
     j = parseInt(Math.random() * i),
     x = o[--i],
     o[i] = o[j],
     o[j] = x
)
    ; // The body of the loop is an empty statement

这可以以更易读的形式表示为:

for (
     // initialisation: declare three variables
     var j, x, i = o.length;
     // The loop check: when it gets to ``!i``, it will exit the loop
     i;
     // note the increment clause is empty
) {
     j = parseInt(Math.random() * i);
     x = o[--i];
     o[i] = o[j];
     o[j] = x;
}

作为一个while循环,可能是:

var j, x, i = o.length;
while (i) {
     j = parseInt(Math.random() * i);
     x = o[--i];
     o[i] = o[j];
     o[j] = x;
}
于 2012-06-13T04:05:29.860 回答
4

首先,我想指出这样的循环被认为是不好的风格,因为它非常不可读并且会引起很多混乱。这是一个典型的优化出错的例子。

查看规格,您会发现for(...)后面必须有一个声明。它可以是任何语句,包括块。所以所有这些都是有效的:

for (...)
    foo;  // expression statement

,

for(...)
    {
       // block statement
    }

,

for(...)
   if(...) // If statement
      foo;

, 而且当然

for (...)
    ;

因为 " ;" 是空语句。它什么也不做,但足以使for(...);语法有效。

现在,逗号。请注意,括号的内容必须是三个表达式,(每个都是可选的),用分号分隔。几乎“一切”都可以作为表达式,包括以逗号分隔的表达式列表。虽然鲜为人知,但这些工作基本上在 JS 中无处不在,不仅在for循环中。他们只是一个接一个地被评估。

所以,你的循环可以像这样重写

shuffle = function(o) {
    var j, x, i = o.length;
    while (i) { // for-loops are just while-loops in disguise
        j = parseInt(Math.random() * i), // even better: replace , by ;
        x = o[--i],
        o[i] = o[j],
        o[j] = x;
    }
    return o;
};

此外,x = o[--i]应写为i--; x = o[i].

于 2012-06-13T04:51:41.120 回答
4

对于我们不需要 {} 的单个语句,每个计算都在同一个语句中,但也在这个语句中;(句尾用了句终止符)表示下一条语句不属于for语句。无论逻辑是什么,for 语句都是一样的。

  for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);


  for(var j, x, i = o.length;// Initialization
  i;// Work until i is zero
 j = parseInt(Math.random() * i),
  x = o[--i], o[i] = o[j], o[j] = x);//Here he is    computing his logic
于 2012-06-13T04:01:51.200 回答
1

如果都在一行上,则不需要括号。很多时候,在括号内的第三部分中,您只会看到i++,或类似的东西。真的,你可以在那里做很多不同的事情。如果您能够在第三部分中打包所有内容,那么您甚至不需要for循环体。

for (first section; second section; third section);

第一节

变量被声明和初始化。这些变量包含在循环的范围内。

第二节

这是每次循环通过时检查的条件。

第三节

每次通过循环后运行的代码。它可以像增加一个变量一样简单,也可以像……那么复杂,只要语法正确,您可以在线放置任何东西。

于 2012-06-13T04:00:32.967 回答
1

看起来我没有正确阅读循环。

在这种情况下,评估发生在 for 循环的条件内。

所以一个 for 循环包含三个部分

for (initial variables; end cases; what to do every iteration)

您定义一些初始的东西并使用o它们传递给函数,定义一个结束情况,然后在每次迭代中计算一些东西。最后,o有一个新值并返回。

于 2012-06-13T04:00:39.647 回答
0

所有的工作实际上都是在for声明的括号内完成的。循环没有明确的主体,所以;最后只是说主体是一个空语句。

,逗号)运算符从左到右计算表达式,并返回最右边的值。

循环基本上相当于:

for(var j, x, i = o.length; i > 0;)
{
    j = parseInt(Math.random() * i--);
    x = o[i];
    o[i] = o[j];
    o[j] = x
} 
于 2012-06-13T04:05:21.203 回答