자바스크립트에서는 값의 일치여부를 체크하다보면 예상치못한 결과를 마주할때가 있다.
예를 들어 0과 -0을 비교해보았을 때 false가 나와야 정상(?)이지만 일치 연산자로 비교했을때 true가 나온다. 또한, NaN은 자기자신과 일치 연산자로 비교했을때 false가 나오는 값중에 하나이다.
배열과 객체에서도 이러한 현상이 발생한다.
const a = { a: 1, b: { c: 2, d: { e: 3 } } }
const b = { b: { d: { e: 3, f: 4 }, c: 2 }, a: 1 }
이 예시를 본다면 객체 안에 객체, 그리고 그 안에 또 객체가 존재한다. 이런 내부 객체까지 확인해야하지만, 일치연산자만으로는 부족하다.
이 문제를 해결하고자 isEqual이라는 함수를 구현해보았다.
{}
인지 확인할 수 있는 함수 리터럴을 만들었다. 이렇게 두 개의 함수 리터럴을 만든 이유는 나중에 조건 비교할 때 가독성이 더 좋아지게 하기 위해서다. 이렇게 식별자 이름을 알기 쉽게 설정함으로써 보는 사람들이 이 함수가 어떤 역할을 하는지 알 수 있기 때문이다. const isPrimitive = obj => obj !== Object(obj);
const isObject = obj => obj.constructor === Object;
[]
와{}
일때와 아닐 경우 세분화하기.{}
인 경우는 for문과 length를 비교해주어야하는 반면, 빌트인 함수는 단순 일치 연산자로 비교해도 값이 나온다.// []와 {}가 아닐때
if (!isObject(obj1) && !isObject(obj2) &&
!Array.isArray(obj1) && !Array.isArray(obj2)){
if (obj1 !== obj2) return false;
}
[]
와 {}
일때, 두 비교하는 객체가 같은 배열이 아니거나 같은 타입이 아닐 경우도 세분화해서 구현했다.// [] 또는 {}일때
if (
(Array.isArray(obj1) && !Array.isArray(obj2)) ||
(!Array.isArray(obj1) && Array.isArray(obj2)))
return false;
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
for (const key in obj1) {
if (!(key in obj2)) return false;
}
for (const key in obj1) {
if (!isEqual(obj1[key], obj2[key])) return false;
}
}
return true;
}
if (arguments.length < 2) {
throw new Error(
`isEqual requires at least 2 argument, but only ${arguments.length} were passed`
);
}
매개변수명도 obj에서 comparison으로 바꾸어줬다.
이유 1) 객체 타입만 받아오는것이아니라 원시 타입도 받아오기때문에 obj는 옳지 않다고 판단하였다.
이유 2) input을 변수명도 고려했었지만, 이 변수명은 두 개를 비교대상으로 받아온다는 의미를 내포하지못해 사용하지않았다.
1) 에러
if (arguments.length < 2) {
throw new Error(
`isEqual requires at least 2 argument, but only ${arguments.length} were passed`
);
}
2) 타입 체크
// 원시 타입 체크
if (isPrimitive(comparison1) && isPrimitive(comparison2))
return Object.is(comparison1, comparison2);
// 객체 타입 체크 -> 불필요
if ( typeof obj1 === 'object' && obj1 !== null &&
typeof obj2 === 'object' && obj2 !== null) {...}
3) if문
if (!isObject(comparison1) && !isObject(comparison2) && !Array.isArray(comparison1) && !Array.isArray(comparison2))
return Object.is(comparison1, comparison2);
4) for...in 변경
for (const key of Object.keys(comparison1)) {
if (!Object.prototype.hasOwnProperty.call(comparison2, key)) return false;
}
for (const key of Object.keys(comparison1)) {
if (!isEqual(comparison1[key], comparison2[key])) return false;
}