我正在浏览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循环只有条件,没有语句部分?我真的很想了解这种方法的流程。
答案 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}}。
现在让我们说1
和var i = 0
。第一个表达式返回if (++i && ++i) console.log(i)
,因此第二个表达式也会被调用,返回1
。 2
和1
都被视为真实,因此2
记录console.log(i)
真棒。让我们看看上面的相同示例,但在2
之前我们初始化if
。现在我们打电话给var i = -1
。第一个if (++i && ++i) console.log(i)
返回++i
falsity 。
要继续,您必须了解0
的工作原理。让我快速解释一下:如果你堆叠&&
,exp1 && exp2 && exp3 ... && expX
只会在exp2
返回真实时执行(评估)实例:exp1
,true
,1
)。仅当"some string"
真实时才会执行exp3
,exp2
当exp4
是真理时才会执行{em>等等...(直到我们点击exp3
)
现在让我们回到expN
。因此,第一个f (++i && ++i) console.log(i)
返回++i
错误,因此第二个0
未执行,整个++i
为condition
因此false
无法执行(如何完成增量,因此console.log(i)
现在等于i
。
现在,当你得到它时,我可以告诉你0
循环在while
检查方式中的工作方式相同。例如,condition
和var = -2
将被执行,直到while (++i)
返回 falsity (即++i
)。 0
将被执行2次。
那么这是如何运作的?
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;
}
<强>观察:强>
true
++i < l
,一个简单的迭代计算结果为真,直到i
的值小于所选元素的数量。 [i
已初始化为-1
,因为下一行中使用了++i
而i
在后续参考中被用作索引[/ li>]
.each
相同,其中函数内的return false
将破坏迭代。答案 2 :(得分:1)
如OP所述,while
条件之后没有声明。注意while循环后的分号,return this
仅在while循环完成后运行,即三个语句中的任何一个求值为false。如果你想知道的是while循环是如何工作的,Filip Bartuzi的答案可能是最完整的。我将尝试更广泛地回答each2
方法的作用。
正如文档中所述,它只是jQuery's #each或Array.prototype.forEach或Underscore's #each或lodash's #forEach的更有效版本,专为select2&#39;而设计。使用。它用于迭代任何包含在jQuery对象中的数组或其他iterable。所以它用于字符串数组以及jQuery集合。
它的工作方式是范围(this
)是它被调用的数组。它被赋予一个函数作为参数。这正是我之前提到的其他each
方法被调用的方式。为each2
提供的函数会为数组中的每个项调用一次。 while循环是一种hacky方式迭代数组中的每个项目,并调用该函数,将项目设置为上下文并将数组中的索引和项目作为jQuery对象作为第一个和第二个参数传递。必须评估while
循环条件中的每个语句以确定它是否为真,并且该过程用于实际为变量赋值,并递增i
。 c.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
之后,它将包含已添加的所有内容所有孩子,孙子等的处理