Underscore 对象函数2
对象函数(Object Functions)
21. _.isMatch
_.isMatch(object, properties) 告诉你properties中的键和值是否包含在object中。
_.isMatch = function(object, attrs) {
var keys = _.keys(attrs), length = keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
};
1、 运行_.keys()函数,遍历对象attrs并把其属性名组成一个数组,把结果返回给keys.
2、 若object为空,则返回一个布尔值。
3、 定义空对象obj。
4、 运行for函数,得到每个key,并判断若attrs[key]的值不等于obj[key]的值,或者key不存在与obj,则返回false
5、 否则返回true
例:
var stooge = {name: 'moe', age: 32};
var test = _.isMatch(stooge, {age: 32});
console.log(test);
// true
var test2 = _.isMatch(null, {age: 32});
console.log(test2);
// false
var test3 = _.isMatch(null, {});
console.log(test3);
// true
22. _.isEqual
_.isEqual(object, other) 执行两个对象之间的优化深度比较,确定他们是否应被视为相等。
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
// instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// isEqual的内部递归比较函数
var eq, deepEq;
eq = function(a, b, aStack, bStack) {
// 有时两个对象是相等的,但是它们并不是完全相同的,例如'0 === -0'
if (a === b) return a !== 0 || 1 / a === 1 / b;
// 一个严格的比较是必要的,例如'null == undefined'
if (a == null || b == null) return a === b;
// NaN 与任何值(包括其自身)相比得到的结果均是 false,但是它们是等效的
if (a !== a) return b !== b;
// 使用原始的typeof方法检查类型。
var type = typeof a;
if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
return deepEq(a, b, aStack, bStack);
};
// isEqual的内部递归比较函数
deepEq = function(a, b, aStack, bStack) {
// 打开任何包装对象。
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// 比较className
var className = toString.call(a);
if (className !== toString.call(b)) return false;
switch (className) {
//通过className值来比较字符串,数字,正则表达式,日期,还有布尔值。
case '[object RegExp]':
//正则表达式强迫进行字符串比较,例如:('' + /a/i === '/a/i')
case '[object String]':
// 原字符串及其对应的对象包装器是等价的;因此'"5"'是等价于'new String("5")'。
return '' + a === '' + b;
case '[object Number]':
// NaN 与任何值(包括其自身)相比得到的结果均是 false
// 但是Object(NaN)是等价于NaN
if (+a !== +a) return +b !== +b;
// 对于其他的数字,需要执行一个公平的比较。
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
// 强制日期和布尔值运算数字原始值。
// 比较日期的毫秒表示。注意,无效的日期毫秒表示为“NaN”并不是等价的。
return +a === +b;
case '[object Symbol]':
// valueOf() 方法可返回 Boolean 对象的原始值。
return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
}
var areArrays = className === '[object Array]';
if (!areArrays) {
if (typeof a != 'object' || typeof b != 'object') return false;
// 对象与不同的构造函数并不等价,且对象和数组来自不同的框架。
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
_.isFunction(bCtor) && bCtor instanceof bCtor)
&& ('constructor' in a && 'constructor' in b)) {
return false;
}
}
// 运行平等的循环结构。检测循环结构的算法是改编自ES 5.1节15.12.3,文摘提供“JO”
// 遍历对象的初始化堆栈
// 这里我们只需要比较对象和数组。
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
while (length--) {
// 线性搜索。性能是成反比的独特的嵌套结构。
if (aStack[length] === a) return bStack[length] === b;
}
// 添加第一个object对象到遍历对象的栈中
aStack.push(a);
bStack.push(b);
// 递归比较对象和数组。
if (areArrays) {
// 比较数组长度来确定进一步的比较是必要的。
length = a.length;
if (length !== b.length) return false;
// 深入比较内容,忽视非数字属性。
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
} else {
// 深入比较对象
var keys = _.keys(a), key;
length = keys.length;
// 确保两个对象包含相同数量的属性之前进一步比较他们是否相等。
if (_.keys(b).length !== length) return false;
while (length--) {
// 深度比较每一个数字
key = keys[length];
if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
}
}
// 从堆栈中删除第一个对象遍历对象。
aStack.pop();
bStack.pop();
return true;
};
_.isEqual = function(a, b) {
return eq(a, b);
};
1、 运行eq()函数
1) 若a全等于b,则返回满足于表达式(a !== 0 | 1 / a === 1 / b)的布尔值。由于’0 === -0’返回的是true,但其实这两个数字时不相等的,故需要以上表达式来判断。 |
2) 若a为空或b为空,则返回满足于表达式(a === b)的布尔值。由于’null == undefined’返回的事true,故需要严格的使用全等于符号(三个等于符号组成)来比较。
3) 若a不全等于本身,则返回满足于表达式(b !== b)的布尔值。由于NaN是不等于自己本身的,可是NaN实际上是与自身等效的。
4) 使用typeof判断a和b的数据类型,若a不为函数类型,且不为对象类型,b不为对象类型,则返回false。
5) 若a和b都不满足以上条件,则运行deepEq()函数,并返回布尔值。
2、 deepEq()函数:
1) 若a是_对象的一个实例,那么a = a._wrapped
2) 若b是_对象的一个实例,那么b = b._wrapped
3) 把a对象转换为字符串,赋值给className,并判断className与b对象转换为字符串的值不相等,则返回false。
4) 通过运行switch…case…函数,来判断className的值,并分别运行不同的函数。
a) className为[object RegExp]正则表达式时,则强迫其a和b进行字符串比较,例:’’ + /a/i === ‘/a/i’,返回满足于表达式(‘’ + a === ‘’ + b)的布尔值。
b) className为[object String]字符串时,则返回满足于表达式(‘’ + a === ‘’ + b)的布尔值。
c) className为[object Number]数字时,首先判断对象a是否为NaN,若是则返回满足于表达式(+b !== +b)的布尔值。若不是则返回满足于表达式(+a === 0 ? 1 / +a === 1 / b : +a === +b)的布尔值。
d) className为[object Date]日期时,强制日期运算数字原始值,则返回满足于表达式(+a === +b)的布尔值。
e) className为[object Boolean]布尔值时,强制布尔值运算数字原始值,则返回满足于表达式(+a === +b)的布尔值。
f) className为[object Symbol]象征对象时,返回满足于表达式(SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b))的布尔值。由于每次运行Symbol()都是创建一个新的对象,故Symbol(“foo”) === Symbol(“foo”)为false。
5) 若className不是数组类型
a) 若a或b的数据类型不属于对象,则返回false。
b) 把a的构造函数赋值给aCtor,b的构造函数赋值给bCtor,并判断当aCtor !== bCtor且!(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor instanceof bCtor)且(‘constructor’ in a && ‘constructor’ in b),则返回false。
6) 对传入的第三第四个参数aStack,bStack进行操作。遍历对象的初始化堆栈,这里我们只需要比较对象和数组。进行while循环,若满足条件aStack[length] === a,则返回满足于表达式(bStack[length] === b)的布尔值。若不满足条件,则继续往下运行,添加第一个object对象到遍历对象的栈中。
7) 若className为数组类型,则
a) 先比较长度是否一样,若不一样,则返回false
b) 若长度一样,则运行while循环,从最后一个元素开始,运行eq()函数,若返回结果为false,则返回false。
8) 若className不是数组类型,则
a) 深入比较对象,同样先比较其长度是否一样,若不一样,则返回false。
b) 若一样则运行while循环函数,从最后一个元素开始,判断a中的key不存在与b中,则返回false,运行eq()返回结果为false,则同样返回false。
9) 从堆栈中删除第一个对象遍历对象。最后返回true。
例:
var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone = {name: 'moe', luckyNumbers: [13, 27, 34]};
console.log(stooge == clone);
// false
var test = _.isEqual(stooge, clone);
console.log(test);
// true
var test2 = _.isEqual(0, -0);
console.log(test2);
// false
var test3 = _.isEqual(3, 3);
console.log(test3);
// true
var test4 = _.isEqual(null, undefined);
console.log(test4);
// false
var test5 = _.isEqual(null, null);
console.log(test5);
// true
var test6 = _.isEqual('name', 'name');
console.log(test6);
// true
var test7 = _.isEqual(NaN, NaN);
console.log(test7);
// true
23. _.isEmpty
_.isEmpty(object) 如果object 不包含任何值(没有可枚举的属性),返回true。 对于字符串和类数组(array-like)对象,如果length属性为0,那么 _.isEmpty检查返回true。
_.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;
};
1、 若obj为null,则返回true
2、 若obj不是null,则判断是非为类数组对象,且判断obj为数组或字符串或参数对象时,返回满足条件(obj.length === 0)的布尔值。
3、 若obj为对象类型,则返回满足条件(_.keys(obj).length === 0)的布尔值。
例:
var test = _.isEmpty([1, 2, 3]);
console.log(test);
// false
var test2 = _.isEmpty([]);
console.log(test2);
// true
console.log('------------------');
var test3 = _.isEmpty({length:0});
console.log(test3);
// false
var test4 = _.isEmpty({});
console.log(test4);
// true
console.log('------------------');
var test5 = _.isEmpty('name');
console.log(test5);
// false
var test6 = _.isEmpty('');
console.log(test6);
// true
24. _.isElement
_.isElement(object) 如果object是一个DOM元素,返回true。
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
1、 当obj存在,且obj.nodeType的值为1时,通过两个感叹号!!强制转换为布尔类型的数据,并返回true
2、 由于nodeType的值为1时说明其DOM节点为元素element节点。
3、 双感叹号!!一般用来将后面的表达式强制转换为布尔类型的数据(boolean),也就是只能是true或者false; 因为javascript是弱类型的语言(变量没有固定的数据类型)所以有时需要强制转换为相应的类型。
例:
var test = _.isElement(jQuery('body')[0]);
console.log(test);
// true
25. _.isArray
_.isArray(object) 如果object是一个数组,返回true。
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
1、 先用toString把obj转换为字符串,然后判断得到的字符串值是否全等于[object Array],若等于则返回true,否则返回false。
例:
var test = _.isArray([1,2,3]);
console.log(test);
// true
var test2 = _.isArray({0:'a', 1:'b' ,length:2});
console.log(test2);
// false
26. _.isObject
_.isObject(value) 如果object是一个对象,返回true。需要注意的是JavaScript数组和函数是对象,字符串和数字不是。
_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};
1、 通过typeof函数判断obj的数据类型。
2、 若type的值为’function’或’object’,且存在obj,则返回true。
例:
var test = _.isObject({});
console.log(test);
// true
var test2 = _.isObject(function(){});
console.log(test2);
// true
var test3 = _.isObject([]);
console.log(test3);
// true
var test4 = _.isObject(1);
console.log(test4);
// false
var test5 = _.isObject('a');
console.log(test5);
// false
var test6 = _.isObject(null);
console.log(test6);
// false
27. _.isArguments
_.isArguments(object) 如果object是一个参数对象,返回true。
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
1、 通过_.each()函数,在 _对象中添加相应的对象,并运行函数。
2、 此处将生产函数
_.isArguments = function(obj) {
return toString.call(obj) === '[object Arguments]';
};
3、 使用toString把obj转换为字符串类型,若得到的值全等于’[object Arguments]’,则返回true,否则返回false。
4、 为了兼容IE9以下的浏览器,则运行以下代码
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return _.has(obj, 'callee');
};
}
若_.isArguments(arguments)为false,则运行 _.has(obj, ‘callee’)函数,判断字符串’callee’是否存在与obj中,若存在则返回true,否则返回false。
例:
var fun = function(){
return _.isArguments(arguments);
}
var test = fun(1,2,3)
console.log(test);
//true
var test2 = _.isArguments([1,2,3]);
console.log(test2)
//false
其中callee 属性是 arguments 对象的一个成员,该属性仅当相关函数正在执行时才可用。callee 属性的初始值是正被执行的 Function 对象。 这将允许匿名函数成为递归的。
例:
function factorial(n) {
if (n <= 0)
return 1;
else
return n * arguments.callee(n - 1)
}
print(factorial(4));
// Output: 24
28. _.isFunction
_.isFunction(object) 如果object是一个函数(Function),返回true。
_.isFunction = function(obj) {
return toString.call(obj) === '[object Function]';
};
1、 使用toString把obj转换为字符串类型,若得到的值全等于’[object Function]’,则返回true,否则返回false。
2、 为了兼容IE11,Safari8,PhantomJS浏览器,则运行以下代码
var nodelist = root.document && root.document.childNodes;
if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
_.isFunction = function(obj) {
return typeof obj == 'function' || false;
};
}
把DOM中的子节点复制给node list,并判断此类型不为函数类型。且判断/./不为函数类型,Int8Array不为对象类型,则返回满足条件(typeof obj == ‘function’ | false)的布尔值。 |
例:
var test = _.isFunction(alert);
console.log(test);
//true
var test2 = _.isFunction('alert');
console.log(test2)
//false
29. _.isString
_.isString(object) 如果object是一个字符串,返回true。
_.isString = function(obj) {
return toString.call(obj) === '[object String]';
};
1、 使用toString把obj转换为字符串类型,若得到的值全等于’[object String]’,则返回true,否则返回false。
例:
var test = _.isString("moe");
console.log(test);
//true
var test2 = _.isString(1);
console.log(test2)
//false
30. _.isNumber
_.isNumber(object) 如果object是一个数值,返回true (包括 NaN)。
_.isNumber = function(obj) {
return toString.call(obj) === '[object Number]';
};
1、 使用toString把obj转换为字符串类型,若得到的值全等于’[object Number]’,则返回true,否则返回false。
例:
var test = _.isNumber(8.4 * 5);
console.log(test);
//true
var test2 = _.isNumber(NaN);
console.log(test2)
//true
var test3 = _.isNumber('1');
console.log(test3)
//false
31. _.isDate
_.isDate(object) 如果object是一个日期,返回true。
_.isDate = function(obj) {
return toString.call(obj) === '[object Date]';
};
1、 使用toString把obj转换为字符串类型,若得到的值全等于’[object Date]’,则返回true,否则返回false。
例:
var test = _.isDate(new Date());
console.log(test);
//true
var test2 = _.isNumber('2016-03-13');
console.log(test2)
//false
32. _.isRegExp
_.isRegExp(object) 如果object是一个正则表达式,返回true。
_.isRegExp = function(obj) {
return toString.call(obj) === '[object RegExp]';
};
1、 使用toString把obj转换为字符串类型,若得到的值全等于’[object RegExp]’,则返回true,否则返回false。
例:
var test = _.isRegExp(/moe/);
console.log(test);
//true
33. _.isError
_.isError(object) 如果object继承至 Error 对象,那么返回 true。
_.isError = function(obj) {
return toString.call(obj) === '[object Error]';
};
1、 使用toString把obj转换为字符串类型,若得到的值全等于’[object Error]’,则返回true,否则返回false。
例:
try {
throw new TypeError("Example");
} catch (o_O) {
var test = _.isError(o_O);
console.log(test);
//true
}
34. _.isNaN
_.isNaN(object) 如果object是 NaN,返回true。 注意: 这和原生的isNaN 函数不一样,如果变量是undefined,原生的isNaN 函数也会返回 true 。
_.isNaN = function(obj) {
return _.isNumber(obj) && isNaN(obj);
};
1、 先运行_.isNumber()函数,若obj为数组类型,则运行isNaN(),若obj为NaN则返回true,否则返回false。
例:
var test = _.isNaN(NaN);
console.log(test);
//true
var test2 = isNaN(undefined);
console.log(test2);
//true
var test3 = _.isNaN(undefined);
console.log(test3);
//false
35. _.isBoolean
_.isBoolean(object) 如果object是一个布尔值,返回true,否则返回false。
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
1、 先判断obj是否存在值,若不存在,则判断obj是否完全等于false,若不相等,则运行toString把obj转换为字符串类型,若得到的值全等于’[object Boolean]’,则返回true,否则返回false。
例:
var test = _.isBoolean(null);
console.log(test);
//false
var test2 = _.isBoolean(undefined);
console.log(test2);
//false
36. _.isNull
_.isNull(object) 如果object的值是 null,返回true。
_.isNull = function(obj) {
return obj === null;
};
1、 当obj对象完全等于null时,返回true,反则返回false。
例:
var test = _.isNull(null);
console.log(test);
//true
var test2 = _.isNull(undefined);
console.log(test2);
//false
37. _.isUndefined
_.isUndefined(value) 如果value是undefined,返回true。
_.isUndefined = function(obj) {
return obj === void 0;
};
1、 当obj对象完全等于undefined时,返回true,反则返回false。
例:
var test = _.isUndefined(window.missingVariable);
console.log(test);
//true
var test2 = _.isUndefined(undefined);
console.log(test2);
//true
var test3 = _.isUndefined(null);
console.log(test3);
//false
38. _.isFinite
_.isFinite(number) 如果 number 是有限数字(或可转换为有限数字),那么返回 true。否则,如果 number 是 NaN(非数字),或者是正、负无穷大的数,则返回 false。
_.isFinite = function(obj) {
return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
};
1、 首先obj对象不为象征对象Symbol(obj),然后obj为有限数字(或可转换为有限数字),最后obj运行parseFloat() 函数解析为一个字符串,并返回一个浮点数。若此浮点数不为NaN,则返回true,若不满足上面的任意一个条件,则返回false。
例:
var test = _.isFinite(Infinity);
console.log(test);
//false
var test2 = _.isFinite(Math.PI);
console.log(test2);
//true
var test3 = _.isFinite(NaN);
console.log(test3);
//false