这是下划线中的一行代码。这一行的加号前缀是什么?
if (obj.length === +obj.length) { // plus prefix?
添加+
符号可以有效地将变量转换为数字,例如:
+"1" === 1;
但是,请注意
+"1" === "1"; // FALSE
+"1" == "1"; // TRUE
这是因为==
会将其操作数转换为相同的类型,而===
不会。
这意味着测试:
obj.length === +obj.length
本质上是试图测试是否obj.length
是数字。
在 Underscore 中,此代码试图确定未知类型的变量是否具有已调用的属性length
以及它是否为数字。假设是,如果这两个都是真的,你可以迭代变量是如果它是一个数组。
请注意,OP 的代码中有许多错误,其中最重要的是这种检测某事物是否为数组(或类似数组)的方法。以下对象会导致问题:
var footballField = {
covering: "astroturf",
condition: "muddy",
length: 100
};
我不提倡上述方法......只是解释别人的。
加号前缀将变量转换为数字。基本上,这obj.length === +obj.length
是 obj.length 真的是一个数字的健全性检查。如果obj.length
不是数字,例如 string "foo"
,则"foo" === +"foo"
等于 false ,因为+"foo"
输出为NaN
。
+ 前缀将值转换为数字。
这迫使 的值为obj.length
a Number
。本质上,这样做是为了确保length
类数组对象的默认值没有被覆盖,以便可以正确迭代。
breaker
在这种情况下不会做任何事情,因为即使没有等价比较,即使是另一个空对象也会在与 .. 进行比较时进行{}
评估。false
breaker
但是,breaker
没有在该上下文中使用,因为它是在函数之外.each
定义的,这看起来与您在此处显示的不同。相反,它用于从其他循环方法中强制“中断”:
_.every = _.all = function(obj, iterator, context) {
/* snip */
if (!(result = result && iterator.call(context, value, index, list)))
return breaker;
你可以看到,如果结果在“every”中不是真的,我们想立即打破。 _.every
调用_.each
,它会返回breaker
与自身比较时为真,允许立即中断。
如果对象没有length
属性(是一个常规的Object
,而不是一个Array
),那么调用obj.length
将返回undefined
。对 undefined 的测试会更清楚,但实现者选择首先将其转换为数字(因此它将变为NaN
),然后将其与原始值进行比较(使用严格比较,这将确保它会产生false
)。
更新:正如其他人指出的那样,这段代码似乎不仅关注Array
s,还关注“类数组”对象(即具有数字length
属性和数字索引的对象)。在这种情况下,instanceof Array
仅测试 for 是不够的(仅测试 forundefined
可能不是最佳选择,因为可能存在length
另一种类型的 but)。