underscore.js 源码解析( 十五 )
clone
tap
has
matcher
property
propertyOf
isEqual
clone
_.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
返回的是 true
1 | {} == {} // 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;