【问题标题】:Select2 each2 method - how does it work?Select2 each2 方法 - 它是如何工作的?
【发布时间】:2014-10-22 17:40:20
【问题描述】:

我正在查看 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循环,没有语句部分?我真的很想了解这个方法的流程。

【问题讨论】:

  • 您有机会查看答案吗?删除评论,因为这不是单行解决方案,如果不清楚,需要澄清。

标签: javascript jquery jquery-select2


【解决方案1】:

当您将表达式放入条件中时(例如:if (i)if (i == null)if (++i)if (i &lt; 2)),表达式在“检查”之前得到评估是 true 还是 false .

活生生的例子:

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

现在假设我们有var i = 0if (++i &amp;&amp; ++i) console.log(i)。第一个表达式返回1,所以第二个也被调用,它返回212 都被视为 truth 所以 console.log(i) 记录 2

太棒了。让我们看一下与上面相同的示例,但在 if 之前我们初始化 var i = -1 .现在我们打电话给if (++i &amp;&amp; ++i) console.log(i)。第一个++i 返回0,这是虚假

要继续,您必须了解&amp;&amp; 的工作原理。让我快速解释一下:如果你堆栈exp1 &amp;&amp; exp2 &amp;&amp; exp3 ... &amp;&amp; expX,那么exp2 将仅在exp1 返回真相 时执行(评估)(例如:true1"some string")。 exp3 只会在 exp2truthy 时执行,exp4exp3 是真的时才会执行等等......(直到我们点击 expN

现在让我们回到f (++i &amp;&amp; ++i) console.log(i)。所以第一个++i 返回0 这是falsity 所以第二个++i 没有被执行,整个conditionfalse 所以console.log(i) 不会被执行(无论如何增量已完成,因此 i 现在等于 0

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

TL;DR BELOW!!!

那么它是如何工作的呢?

   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;
   }

或者更神奇:

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

【讨论】:

  • 但是在上面的while循环示例中究竟会执行什么?没有陈述,只有条件。这是我有问题的部分。假设我的循环执行了 2 次 - 执行了哪一部分?
  • 有。我相信(j.context = j[0] = this[i])是这个算法的核心。 c.call(j[0], i, j) 也可以在这里做“事情”。
  • 你确定可以这样重写吗?你能为这种转变提供某种来源吗?
  • 如果你假设(j.context = j[0] = this[i]); 总是返回truth,而不是yes。否则:你不能。编辑:实际上你不能假设。我改变了答案
  • while 循环以分号结束,因此在该循环中没有执行任何内容,除了那个空语句,执行的是在控制表达式中:)
【解决方案2】:

代码的目的是向 jQuery 对象添加一个新函数each2,其工作方式类似于.each。这个.each2 函数的用法与.each 相同,后者在一个jQuery 对象上执行,接受一个用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 &lt; l,一个简单的迭代计算结果为真,直到i 的值小于选定元素的计数。 [i 被初始化为-1,因为在下一行使用了++i,而i 在后面的引用中被用作索引]
  3. 第二个条件:实际上是初始化+空/未定义检查。对于所有有效值,条件都被评估为真,并且在存在无效上下文时失败。
  4. 第三个条件:这个条件的目的是让你在回调函数返回假时跳出循环。与.each 相同,其中函数内部的return false 将中断迭代。

【讨论】:

  • 你是对的,除了两件事。 each2 适用于数组(或任何长度可以迭代的东西。而您的第一个观察结果是错误的,它仅在所有 3 个条件都为真时继续。
  • @RustyToms 谢谢,但是,jQuery 对象有长度,它可以被迭代但是我不会说.each2 是为此而生的。专门为该插件构建的.each2,因此其使用可能仅限于用作实用功能。我的第一个观察只是指出,while 继续,除非任何条件评估为假,不确定“直到”和“仅当”在该上下文中有何不同。
  • 我的意思是 each2 不限于 jQuery 对象,但是是的,我猜是因为 each2 扩展了 $ (jQuery 对象)你是对的,它是特定的 jQuery 对象。我认为您也正确地考虑了您的第一次观察,但是您的语法是错误的。 “直到”和“仅当”是相反的。当您说“继续直到所有 3 个条件都为真”时,您是说如果所有 3 个条件都为真,它将立即停止。但反之亦然,只要这三个都是真的,它只会继续下去。所以只要把“直到”换成“如果”,那么你的答案是正确的。
  • @RustyToms 我明白了,谢谢。改为“仅当”。
【解决方案3】:

正如 OP 所指出的,while 条件之后没有语句。注意 while 循环后面的分号,return this 仅在 while 循环完成后运行,即当三个语句中的任何一个评估为 false 时。如果您只想知道 while 循环是如何工作的,Filip Bartuzi 的回答可能是最完整的。我将尝试就each2 方法的作用给出更广泛的答案。

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

它的工作方式是作用域 (this) 是调用它的数组。它被赋予一个函数作为参数。这正是我之前提到的其他each 方法的调用方式。为数组中的每个项目调用一次提供给each2 的函数。 while 循环是一种遍历数组中每个项目并调用函数的方法,将项目设置为上下文并将数组中的索引和项目作为 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 个参数。如果该类以“select2-”开头,则该类被添加到名为replacements 的数组中。 'select2-' 类正在被复制,非常简单。

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

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

【讨论】:

    猜你喜欢
    • 2015-09-02
    • 2015-07-27
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-04
    • 1970-01-01
    相关资源
    最近更新 更多