首先看一些在object块定义的常用的类型判断函数。
_.isElement(object)
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
nodeType相关知识点:
节点类型 | 描述 | 名称 | |
---|---|---|---|
1 | Element | 元素 | ELEMENT_NODE |
2 | Attr | 属性 | ATTRIBUTE_NODE |
3 | Text | 文本内容 | TEXT_NODE |
8 | Comment | 注释 | COMMENT_NODE |
9 | Document | 整个文档(DOM)树的根节点 | DOCUMENT_NODE |
11 | DocumentFragment | 某个文档片段 | DOCUMENT_FRAGMENT_NODE |
其实一般来说是会隐式转换的...可能这里是在强制转换避免出错吧。
_.isArray(object)
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
呐呐...underscore的开头就引用了js原生的一些方法:
var ArrayProto = Array.prototype,
ObjProto = Object.prototype,
FuncProto = Function.prototype;
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind,
nativeCreate = Object.create;
而关于[object Array]
...
当判断一个对象是否为数组时,不能用typeof
运算符进行操作,因为会返回object
:
typeof([]) // "object"
那这个时候怎么判断数组是不是数组呢...虽然现在有原生的isArray
来判断:
Array.isArray([]) // true
但是依然存在一种调用object的toString()
方法来判断的办法:
Object.prototype.toString.call([]) // "[object Array]"
这个办法还可以用来判断别的各种类型:
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
_.each
方法之后会细说,当做原生的forEach
来看呐。
_.isObject(value)
_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !obj;
};
嗯...这里要注意的是运算符的优先级,&&优先级高于||,所以这里把函数也作为object来看待了...
_.isObject(function f() {}) // true
_.isArguments(object)
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return _.has(obj, 'callee');
};
}
因为IE<9下对arguments调用Object.prototype.toString.call()
,返回的是[object Object]
,而非[object Arguments]
,所以遇到这种情况需要判断一下是否还有callee属性。
_.isFunction(object)
if (typeof /./ != 'function' && typeof Int8Array != 'object') {
_.isFunction = function(obj) {
return typeof obj === 'function' || false;
};
}
Safari5及之前版本、Chrome7及之前版本在对正则表达式调用typeof
操作符时会返回function
,所以就是为了功能兼容吧。
_.isFinite(object)
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
用到了JavaScript的全局函数isFinite
,会检查参数是否为无穷大。
isFinite(function f() {}) // false,因为值为NaN
isFinite(123) // true
_.isNaN(object)
_.isNaN = function(obj) {
return _.isNumber(obj) && obj !== +obj;
};
首先判断是否为数字,其次判断其本身是否与自己相等。
嗯,这是只针对数字的判断。
还有这样一个bug...???
_.isBoolean(object)
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
其实我感觉可能直接用toString.call(obj) === '[object Boolean]'
就可以...
_.isNull(object)和_.isUndefined(value)
_.isNull = function(obj) {
return obj === null;
};
_.isUndefined = function(obj) {
return obj === void 0;
};
这里就涉及到了为什么用void 0
而不是undefined
,
在非严格模式下,我们可以为全局标识符
undefined
赋值--《你不知道的js》
但是我没运行出来。而且严格模式下也没有报错...
function foo() {
undefined = 2
}
console.log(undefined) // undefined
可以声明一个名为undefined
的局部变量倒是真的。
function foo() {
var undefined = 2
console.log(undefined) // 2
}
foo()
所以说明使用undefined
很多时候是不靠谱的。
那void 0
呢,表达式void
没有返回值,因此返回结果是undefined
,void
并不改变表达式的结果,只是让表达式不返回值。在这个层面上讲,void 0
,void a
都是一样的。
var obj = {a: 3}
void obj // undefined
_.isEmpty(object)
_.isEmpty = function(obj) {
if (obj === null) return true;
if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)))
return obj.length === 0;
return _.keys(obj).length === 0;
};
判断是否为空的方法:
- 如果是null,则为空;
- 如果是有长度的结构(数组,类数组,字符串等),则判断其长度;
- 如果是object,则判断由
key
组成的数组的长度~
_.has(obj, key)
_.has = function(obj, key) {
return obj != null && hasOwnProperty.call(obj, key);
};
首先要确定传入的对象不能为空,然后用hasOwnProperty
来检查是否为对象的属性。