Select2 each2方法 - 它是如何工作的?

时间:2014-10-22 17:40:21

标签: javascript jquery jquery-select2

我正在浏览Select2(source code)并找到each2方法原型:

$.extend($.fn, {
  each2 : function (c) {
    var j = $([0]), i = -1, l = this.length;
    while (
      ++i < l
       && (j.context = j[0] = this[i])
       && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
     );
     return this;
   }
});

我的问题是 - 这种方法有用吗?我的意思是 - 为什么while循环只有条件,没有语句部分?我真的很想了解这种方法的流程。

3 个答案:

答案 0 :(得分:4)

当您将表达式置于条件中时(例如:if (i)if (i == null)if (++i)if (i < 2)))表达式会在其表达式之前得到评估。检查&#39;是true还是false

实例:

我们有var i = 0;现在你打电话给if (++i) console.log(i)。表达式++i返回1(在javascript [ 0不是]中被理解为 true ),因此console.log(i)记录{{ 1}}。

现在让我们说1var i = 0。第一个表达式返回if (++i && ++i) console.log(i),因此第二个表达式也会被调用,返回121都被视为真实,因此2记录console.log(i)

真棒。让我们看看上面的相同示例,但在2之前我们初始化if 。现在我们打电话给var i = -1。第一个if (++i && ++i) console.log(i)返回++i falsity

要继续,您必须了解0的工作原理。让我快速解释一下:如果你堆叠&&exp1 && exp2 && exp3 ... && expX只会在exp2返回真实时执行(评估)实例:exp1true1)。仅当"some string" 真实时才会执行exp3exp2exp4是真理时才会执行{em>等等...(直到我们点击exp3

现在让我们回到expN。因此,第一个f (++i && ++i) console.log(i)返回++i 错误,因此第二个0未执行,整个++icondition因此false无法执行(如何完成增量,因此console.log(i)现在等于i

现在,当你得到它时,我可以告诉你0循环在while检查方式中的工作方式相同。例如,conditionvar = -2将被执行,直到while (++i)返回 falsity (即++i)。 0将被执行2次。

TL; DR低于!!!

那么这是如何运作的?

while (++1)

我认为最好的解释方法是重写它(:

   while (
      ++i < l
       && (j.context = j[0] = this[i])
       && c.call(j[0], i, j) !== false
     );

或者不那么神奇:

   while ( ++i < l ) {
       if (!(j.context = j[0] = this[i])) break;
       if (!(c.call(j[0], i, j) !== false)) break;
   }

答案 1 :(得分:3)

代码的目的是向jQuery对象添加一个新函数each2,该函数与.each类似。此.each2函数的用法与在{j}对象上执行的.each相同,使用函数以2 arg(索引和值)回调并返回jQuery对象。

神秘的while循环,

while (
    ++i < l
    && (j.context = j[0] = this[i])
    && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
  );
  return this;
}

<强>观察:

  1. 继续“仅当”所有3个条件的评估结果为true
  2. 第一个条件:++i < l,一个简单的迭代计算结果为真,直到i的值小于所选元素的数量。 [i已初始化为-1,因为下一行中使用了++ii在后​​续参考中被用作索引[/ li>]
  3. 第二个条件:它实际上是一个初始化+ null / undefined检查。对于所有有效值,条件的计算结果为true;如果存在无效的上下文,则该条件将失败。
  4. 第三个条件:如果回调函数返回false,则此条件的目的允许您中断循环。与.each相同,其中函数内的return false将破坏迭代。

答案 2 :(得分:1)

如OP所述,while条件之后没有声明。注意while循环后的分号,return this仅在while循环完成后运行,即三个语句中的任何一个求值为false。如果你想知道的是while循环是如何工作的,Filip Bartuzi的答案可能是最完整的。我将尝试更广泛地回答each2方法的作用。

正如文档中所述,它只是jQuery's #eachArray.prototype.forEachUnderscore's #eachlodash's #forEach的更有效版本,专为select2&#39;而设计。使用。它用于迭代任何包含在jQuery对象中的数组或其他iterable。所以它用于字符串数组以及jQuery集合。

它的工作方式是范围(this)是它被调用的数组。它被赋予一个函数作为参数。这正是我之前提到的其他each方法被调用的方式。为each2提供的函数会为数组中的每个项调用一次。 while循环是一种hacky方式迭代数组中的每个项目,并调用该函数,将项目设置为上下文并将数组中的索引和项目作为jQuery对象作为第一个和第二个参数传递。必须评估while循环条件中的每个语句以确定它是否为真,并且该过程用于实际为变量赋值,并递增ic.call(j[0], i, j) !== false部分允许函数通过返回false来提前终止循环。如果函数返回false,则while循环将停止,否则它将一直持续到i大于l,这意味着数组中的每个项都已被使用。之后返回this只会在.each2之后将另一个方法链接到数组。

基本上while循环可以重写为:

var j = $([0]), i = 0, l = this.length, continue = true;
while (i < l) {
  i++;
  j.context = j[0] = this[i];
  continue = c.call(j[0], i, j);
  if (!continue) {
    break;
  }
}

但可能存在一些性能原因导致浏览器无法对其进行优化。

它比普通each方法更脆弱。例如,如果数组中的元素具有诸如0的假值,则(j.context = j[0] = this[i])将为假,从而导致循环终止。但它仅用于select2代码的特殊情况,不应该发生。

让我们来看几个例子。

function syncCssClasses(dest, src, adapter) {
    var classes, replacements = [], adapted;

    classes = $.trim(dest.attr("class"));

    if (classes) {
        classes = '' + classes; // for IE which returns object

        $(classes.split(/\s+/)).each2(function() {
            if (this.indexOf("select2-") === 0) {
                replacements.push(this);
            }
        });
    }
    ...

^此代码从dest DOM元素获取类。这些类被拆分为一个字符串数组(该字符串在空格字符上拆分,这是\s+正则表达式),每个类名都是数组中的一个项。这里不需要特殊的jQuery工作,因此不提供由each2调用的函数的2个参数。如果课程以&#39; select2 - &#39;开头该类被添加到名为replacements的数组中。 &#39; select2 - &#39;正在复制类,非常简单。

group.children=[];
$(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });

^为简洁起见,我没有包含此方法的其余部分,但它是process方法的一部分。在这种情况下,each2被用于递归process一个节点及其所有孩子。 $(datum.children)是一个jQuery选择,每个孩子都有一个选择。 (名为childDatum)将依次处理,并且它的孩子将经历相同的过程。 group.children数组将用作集合,无论添加到该数组中的每个childDatum都可用,因此在运行each2之后,它将包含已添加的所有内容所有孩子,孙子等的处理