函數(shù)isEqual接收兩個(gè)對(duì)象, 判斷它們的相等性, 返回boolean
根據(jù)相等判斷的實(shí)用性, 做出如下的相等性說(shuō)明:
原始類型
Number,String,Boolean,Undefined,Null: 直接通過(guò)===判斷, 經(jīng)過(guò)對(duì)象包裝(eg: new Number(1))的這類對(duì)象轉(zhuǎn)換為原始類型再判斷(即_.isEqual(new Number(1), 1)===true)引用類型
Object: 引用相同, 必然相等; 引用不同, 進(jìn)行深入比較(其中type為function的類型, 先判斷構(gòu)造函數(shù)是是否相等, 再判斷其自有的屬性和方法是否也一致)
原生js存在的不完善地方,這里列舉一些常用到的特例:
原生會(huì)返回true的情況:
null == undefined(通過(guò)null === undefined返回false修正)-0 === +0(通過(guò)-Infinity === Infinity返回false修正)NaN !== NaN(通過(guò)if(a!==a) return (b!==b)修正)原生會(huì)返回false
經(jīng)過(guò)對(duì)象包裝的原始類型的比較:
new Number(1) === 1(原生的這樣判斷其實(shí)是無(wú)可厚非的, 實(shí)際運(yùn)用中我們統(tǒng)一都?xì)w為相等)
用到的工具:
- typeof
- Object.prototype.toString
- 運(yùn)算符
+可以把字符串轉(zhuǎn)為數(shù)字 - lodash的keys,has方法
使用例子:
isEqual({name: 'rose'}, {name: 'rose'}) // 返回true
isEqual([{name: 'rose'}, 2], [{name: 'rose'}, 2]) // 返回true
var A = function(){}
var B = new A
var C = new A
isEqual(B, C) //返回true
代碼:
import _ from 'lodash'
var toString = Object.prototype.toString
var isEqual = function (a, b, aStack, bStack) {
//原始類型Number, String, Boolean
// 0 === -0 使用 1 / 0 !== 1/ -0 排除
if (a === b) return a !== 0 || 1 / a === 1 / b
// null == undefined 返回true
// null === undefined 返回false
if (a == null || b == null) return a === b
// isNaN !== isNaN 返回true
if (a !== a) return b !== b
var className = toString.call(a)
if (className !== toString.call(b)) return false
switch (className) {
case '[object RegExp]':
case '[object String]':
return '' + a === '' + b
case '[object Number]':
if (+a !== +a) return +b !== +b
return +a === 0 ? 1 / +a === 1 / b : +a === +b
case '[object Date]':
case '[object Boolean]':
return +a === +b
// todo case '[object Symbol]'
}
// 判斷a 是否為數(shù)組
var areArrays = className === '[object Array]'
if (!areArrays) {
if (typeof a !== 'object' || typeof b !== 'object') return false
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
// from different frames are.
// 先判斷構(gòu)造函數(shù)是否相同
var aCtor = a.constructor, bCtor = b.constructor
if (aCtor !== bCtor) return false
}
aStack = aStack || []
bStack = bStack || []
var length = aStack.length
while (length--) {
// 避免循環(huán)引用導(dǎo)致的死循環(huán)
if (aStack[length] === a) return bStack[length] === b
}
aStack.push(a)
bStack.push(b)
if (areArrays) {
length = a.length
if (length !== b.length) return false
while (length--) {
if (!isEqual(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--) {
// Deep compare each member
// 遞歸比較
key = keys[length];
if (!(_.has(b, key) && isEqual(a[key], b[key], aStack, bStack))) return false;
}
}
aStack.pop()
bStack.pop()
return true
}
export {
isEqual
}
todo:
- 去掉lodash的依賴, 寫一個(gè)從零開始的對(duì)象判斷
- 加入es6新類型數(shù)據(jù)結(jié)構(gòu)的判斷
參考鏈接: