该项目的 GitHub 地址为 underscore-analysis,所有文章都在这里,并将不断更新。如果你觉得我的解读还可以,对你学习 js 有一定的帮助,欢迎 Watch && Star,你的关注和肯定必定会促使我投入更多的时间和经历做好这个系列。如果你愿意的话,也欢迎 Fork,我们一起来将这个系列做好做大,一起成长。
前面两篇文章我们了解了 underscore 的大致框架,以及它为了后面更好地定义方法做的一些基础设置。从这篇文献开始,我将深入学习它的 API。 首先学习的是它的对象方法部分,这篇文章将主要解读这部分关于数据判断的方法。
1. 数据类型判断
1.1 常规判断
先来看一下 underscore 的源码:
|
这里调用了 underscore 的 each 方法(这个方法在 第二篇文章 中有过说明),这个方法为第一个数组参数中的每个字符串元素添加一个 “is” 前缀后,将这个字符串作为 underscore 库的方法保存,每个方法的函数体内都调用了 toString (var toString = Object.prototype.toString
)方法。
为什么要调用这个方法呢,我们首先来看一下 MDN 关于 Object.prototype.toString() 描述:
Every object has a toString() method that is automatically called when the object is to be represented as a text value or when an object is referred to in a manner in which a string is expected. By default, the toString() method is inherited by every object descended from Object. If this method is not overridden in a custom object, toString() returns “[object type]”, where type is the object type.
翻译成中文大致是: js 中每个对象都有一个 toString 方法,当对象需要被转成字符串时,就会自动调用它。 默认情况下,每个对象的 toString 方法都是从 Object 上继承的,如果当前对象没有重新定义 toString 方法,既没有覆盖从 Object 上继承的 toString 方法时,那么调用该对象的方法时,会返回 “[object type]”, 这里的字符串 type 表示当前对象的类型
我们可以在每个对象上调用默认 toString 方法,来判断对象的类型:
|
总结: 对于 Arguments, Function, String, Number, Date, RegExp, Error 这些数据类型,underscore 都是通过 Obect.prototype.toString
方法来判断其类型的
1.2 补充 _.isArguments(obj)
上面虽然已经定义了该方法,但是由于 IE9 之前的版本在此处有 bug,对于 Arguments 类型,通过 toString 方法返回的是 “[object object]”, 而不是 “[object Arguments]”,所以还要进行修正
|
1.3 补充 _.isFunction(obj)
修正了早期 V8 存在的一些 bug
|
PS: 这也是 编写可维护的JavaScript 一书中推荐的判断函数类型的方式(没有 if 条件句判断),直接用 typeof 操作符判断
1.4 _.isArray(obj)
判断 obj 是否是数组类型
PS: 判断一个数据是不是数组还有一种老式方法,既检查该数据是否有 sort 方法,因为只有数组才有这个方法:
1.5 _.isObject(obj)
判断 obj 是否是对象类型
从代码中我们可以看出来,underscore 将函数也视为对象,但是像将 null 排除在外,即使 typeof null = "object"
1.6 _.isBoolean(obj)
判断 obj 是否是布尔类型
1.7 _.isNull(obj)
判断 obj 是否是 null
1.8 _.isUndefined(obj)
判断 obj 是否是 undefined
这里将 obj 与 void 0 全等对比是一种 hack 的方法。
- 为什么要这样做呢,为什么不能像判断 null 那样直接全等比较呢。这是因为 undefined 在 ES3 中可以被用作全局和函数变量,在 ES5 中可以被用作函数变量,也就是说它在一些情况下是可以被赋值操作,被改写的,所以直接全等比较是不靠谱的。
- 既然 undefined 并不能真正靠谱地表示 “未定义”,那么我们就需要找替代品,而 void 运算符可以完美胜任。该运算符后面跟一个表达式,而无论这个表达式是什么,运算结果都是 “undefined”。那干脆在后面加个 0,运算开销最小。
PS: 当函数没有返回结果的时候,默认返回的就是 “undefined”,所以也可以利用一个匿名空函数。
jQuery 采用了另外一种方式,为整体的匿名函数定义两个形参,但是只传入一个实参,那么第二个形参就会被赋值 “undefined”
1.9 _.isNaN(obj)
判断 obj 是否是 NaN
underscore 只希望判断 obj 是不是等于 NaN 这个特殊的值(NaN 本质上是一个数字类型的值,像 1,2,3 一样它也代表了一个数值),而不是希望判断 obj 到底是不是个数字。原生的 isNaN(obj) 方法,只要 obj 不是数字类型的值就会返回 true:
2. 其他判断
2.1 _.isElement(obj)
判断 obj 是否是一个 DOM 元素节点
元素节点的 nodeType 值为 1
之所以会加上两个非 “!” 运算符,是因为如果 obj 是 undefined,null, NaN, 0, 这几个可以转成 false 的值是,返回的是这几个值本身,而不是 false,所以要将它们强制转成 false
2.2 _.isFinite(obj)
判断 obj 是不是有穷数字
这里依赖了原生的 isFinite 方法,原生的方法在进行判断的时候会先将 obj 转成数字,然后再判断该数字是否有限,但是,true 和 null 会被转成1 和 0,就导致将 true 和 null 判断成有限,而它们根本就不是数字,!isNaN(parseFloat(obj))
可以将排除这种情况
2.3 _.isEmpty(obj)
判断 obj 是不是为空