underscore.js 源码解析( 十五 )
clone tap has matcher property propertyOf isEqualclone
_.clone(object) 创建一个浅拷贝的 object 使用 _.clone 的源码如下:1 | _.clone = function (obj) { |
tap
_.tap(object, interceptor)
interceptor 拦截器
使用 interceptor 的作用是用于对于传入的对象 object 进行 interceptor 操作, 并且操作完成之后返回 object 本身。用于链式调用: 1 | _.tap = function (obj, interceptor) { |
has
_.has(object, key)用于判断对象是否包含有特定的属性 key , 在源码中有这样一句话, 等同于源码分析如下:object.hasOwnProperty(key),但是使用hasOwnProperty函数的一个安全引用,以防意外覆盖。什么是意外覆盖?
1 | _.has = function(obj, key) { |
matcher
matcher(attrs)使用 matcher 函数是一个断言函数, 返回一个 true or false 来判断给定的对象中是否含有 attrs 中指定的键值对儿示例:1 | let list = { |
源码分析
源码如下:1 | _.matcher = _.matches = function(attrs) { |
_.isMatch 源码如下:1 | _.isMatch = function (object, attrs) { |
property
property(key) 该方法返回一个函数,返回传入该函数的任何对象的 key 属性。 源码
1 | let property = function (key) { |
propertyOf
使用 propertyOf 与使用 property 相反。 propertyOf(object) 传入一个对象, 返回一个函数,这个函数接收一个属性,返回对象对应属性的值。使用 propertyOf 1 | _.propertyOf = function (obj) { |
isEqual
isEqual(object, other) 使用 isEqual 用来判断两个对象是否相等。 因为不同的对象被放在了不同的内存空间中, 因此, 即使是属性和值均相等的对象也是不相同的, 如果对象的属性和值都是相同的,使用 isEqual 返回的是 true1 | {} == {} // false |
equal 进行判断:1 | _.equal = function (a, b) { |
eq 进行判断的源码如下:1 | var eq = function(a, b, aStack, bStack) { |
eq 函数
使用 eq 函数进行比较的时候,进行比较的值有下面几种情况:- 处理传递进比较的数值出现
0 === -0的情况。这种情况用于单个数值的比较
a === -0 但是他们是不相同的。 1 | // 当 a !== 0 || 1/a === 1 / b |
- 处理
null == undefined的情况
1 | // 当 a == null 或者 b == null 的时候, 返回 a === b |
- 使用
Object.toString进行判断属于Object的哪一种类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15let className = toString.call(a);
// 类型不同, 返回 false
if (className !== toString.call(b)) return false;
switch (className) {
case '[object RegExp]':
case '[object String]':
return '' + a === '' +b;
case '[object Number]':
// 使用 NaN 进行比较
if (+a !== +a) return +b !== +b;
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
return +a === +b;
} - 对于数组和对象的之间进行比较,需要深度比较当不是数组, 对象的情况:进行比较对象或者数组:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var areArrays = className === '[object Array]';
if (!areArray) {
if (typeof a !== 'object' || typeof b !== 'object') return false;
var aCtor = a.constructor, bCtor = b.constructor;
// 如果 aCtor !== bCtor
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor isntanceof bCtor) && ('constructor' in a && 'constructor' in b)) {
return false;
}
}
let length = aStack.length;
while(length--) {
if (aStack[length] === a) return bStack[length] === b;
}
// 将 a 压入到 aStack 数组中
// 将 b 压入到 bStack 数组中
// aStack bStack 主要用于多重数组的情况
aStack.push(a);
bStack.push(b);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 比较数组
if (areArrays) {
length = a.length;
if (length !== b.length) return false;
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
} else {
// 比较对象
let keys = _.keys(a), key;
length = keys.length;
// 如果两个对象的属性数目不相同 返回 false 不用进行深度遍历
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;