Underscore之奇葩写法.

cover-image
最近看一下underscore, 顺便也看了下源码, 不过第一个函数就碰到了奇葩:

1
obj.length === +obj.length

看了半天愣是没懂…

###0x00 分析.
结论先: 这条语句可以用来判断obj是数组还是对象.

别急啦~我先来抄一段underscore的each函数实现.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return obj;
if (nativeForEach && obj.forEach === nativeForEach) { //这边判断原生forEach若存在则使用它
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) { //HERE!我们关注一下这里
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
return obj;
};

我也是第一次看到这种写法, 甚至刚开始让我怀疑我的js语法学的是不是有问题了.

我们先看两个地方:

  1. === 全等符

  2. + 号


  1. -> 我们知道全等符是用来判断其左右两个变量在值与类型上是不是”完全相等的”. 出于性能以及各种奇葩的问题, 在捣鼓拉斯的js语言精粹里他就强烈推荐尽量使用===来代替==.

  2. -> 再来看看+号, 这个操作符能自动转换它右边的操作值为number类型. 不知道同学们在做js的数学运算的有没有碰到过这个数: NaN, 它的意思是Not A Number. 一般在对undefined进行数学操作的时候会返回NaN这个值, 比如 a = undefined + 1; //-> a == NaN. 其实就是这个意思, 如果obj是array/ string/ function, 那么它就拥有length这个number类型的属性, 左边和右边是全等的. 如果obj是对象, 那么obj.length就是undefined, 那么+undefined就是NaN ,undefined === NaN自然就是false了.
    (关于NaN, Js还有一个奇葩的遗留问题, NaN === NaN的结果是False, 这也是判断一个数是不是NaN的方法)

###0x01 为什么这样写
我们来实现一下好理解一些的版本:

1
typeof obj.length === "number" && !isNaN(obj.length)

其实用typeof来判断array和对象也是可行的, 我们用jsperf来测试一下这两个语句的性能;

这里存在两种情况, 判断obj是Array或者obj是对象:


我的测试环境是 Safari 7.0.2 @ OS X 10.9.2, Chrome @ OSX 也大同小异.

OK, 结论是在判断obj是对象的时候, 使用typeof性能占优势.

###0x03 结论
我们大概可以猜测到作者在写underscore的时候考虑到each方法大部分时候面对的是array, 所以选择了一种对array来说性能占优的方式.


参考: