[JS]15. Array.prototype.includes() 객체도 비교 가능할까?




열코딩중 나의 멘토 Jerry 에게 퀴즈가 날라왔다.
console.log([{"name":"jerry", "name":"jorang"}].includes({"name":"jorang"}));
답이 뭐일거 같아?

나의 대답은 true 였지만,,, 바로 정답이었다면 내가 글을 올리지도 않았겠지!
정답은 false 였다.

아직 완벽한 정답은 모르지만 알아낸 곳 까지 차근차근 접근해보자!

Array.prototype.includes()

js 에 내장된 Array 전용 메소드로서, 배열이 특정 요소를 포함하는지 판별한다.
return 값을 true/false 값이다.
아래는 MDN 의 예시 코드이다.

const array1 = [1, 2, 3];

console.log(array1.includes(2));
// expected output: true

const pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

나도 평소 코테 준비할 때 Number, String을 파악하기 위해서는 자주 이용했던 메소드이다.


근데 왜 저건 안되지?

console.log([{"name":"jerry", "name":"jorang"}].includes({"name":"jorang"}));

우선 이 코드를 정리해보자.
includes 로 알아볼 배열에 name 프로퍼티가 2개가 있다.
(사실 각각의 객체로 2개 들어가있어야 했는데 출제자 jerry의 실수였다고 한다.)
한 객체에서 동일한 이름의 property는 존재할 수 없으므로 나중에 정의된 name:jorang 으로 덮어 씌어질 것이다.

console.log([{"name":"jorang"}].includes({"name":"jorang"}));

그럼 위의 코드로 정리할 수 있다.
앞의 배열에 name:jorang인 객체가 포함되어 있나요? 라는 물음인데 왜 때문에 false 가 나왔을까?

구글링을 해보기 전 나의 추축
(실제 슬랙 대화 내용)

음
includes는
하나라도 포함하면 true/false로 반환하는데
저 앞 객체가 프로퍼티가 name으로 같으니까
뒤에 jorang만 살아남고
근데 조랭있냐고 물었으니까
true
아닐까여?

오오
정답: false입니다

음. 아 객체에 담겨있어서 그런가?
아근데저렇게 콘솔에 바로 써도 메모리에 저장이되나?
내말이 말이 되려면 저장이 된다는 전제하인데..
저 인클루드 안에 애가 객체자나
그래서 값이 바로 나오지않고 참조값을 쓰는거지
즉시실행함수처럼
저렇게 객체 쓰면 바로 만들어지는거야 저 객체가
근데 객체는 메모리주소만 저장하고 빠져버리자나
그래서 메모리주소가 두개가 다르니까
각각 생성되서!
그래서 그치 않을까?

이런 대화 내용을 거쳤었는데…ㅋㅋㅋ
찾아보니 우리 뿐만 아닌 다른 사람들도 겪었던 문제인거 같다.
includes 메소드로 객체는 판단할 수 없다는 문제!

우선 내가 내놓은 결론은! (물론 정답은 아니다. 나의 짧은 지식안에서의 추측이므로 피드백 환영🤩)

  • includes() 안에 object 자료형을 담으면 데이터가 메모리 어느 곳에 저장이 된다 (Like 즉시실행함수)
  • object 의 비교는 데이터가 가진 값 자체가 아닌 실제 데이터가 저장된 주소를 가리키는 주소값을 비교하므로 비교하는 객체인 Array 와 비교당하는 includes 안에 객체가 각각 따로 생성되어 다른 메모리 주소값을 가지기 때문에 비교 연산시 false 를 반환한다.
  • 간단히 생각하면 원시 자료형과 참조 자료형의 가장 큰 차이였던 주솟값으로 저장되느냐 아니냐의 차이!
  • 그렇지만 아직 의문은 따로 변수를 만들어 저장하지 않고 저렇게 includes() 안에만 넣어도 메모리 어딘가에 저장이 되냐는 것이다. (알려주실 분 구합니다ㅠ😂)


그럼 배열 안 객체를 비교하고 싶을 때는 어떻게 해결해야 할까?
다른 사람들이 제시하는 해결방법은 Array.prototype.some()을 사용하라는 것이다.


Array.prototype.some()

배열에서 하나의 요소라도 주어진 판별 함수를 통과하면 true, 아니면 false 값 반환하는 메소드

이렇게 배열 내에 내가 원하는 객체로서의 데이터를 찾을 때는 아래와 같이 찾아야 한다.

let person = [{"name":"jerry", "name":"jorang"}]
let person_jorang = person.some(person => person.name === "jorang")
console.log(person_jorang) //true

짠! 기다리던 true 가 나온 것을 볼 수 있다.


includes()로 꾸역꾸역 하려면?

false 가 나오는 이유가 내 생각대로 각각이 따로 메모리에 저장되기 때문이라면,
미리 저장을 시키고 저장된 객체를 비교하는 방법으로 해결은 가능하다.

let person1 = {name: "jorang" };
let person2 = {name: "jerry" };
console.log([person1, person2].includes(person1)) //true

뭔가 마음에 쏙 들지는 않지만, 바라던 true가 나왔긴 했으니 여기서 만족해야하는 걸까…
이게 아니더라도 includes로 객체를 비교하는 방법이 또 있다고는 하는데 그건 좀 더 고민해봐야겠다…


나의 회고 🤫

코테 문제를 풀며 some, includes 를 사용하면서도 이 둘의 자세한 차이점을 알 지 못했다.
some 은 항상 every 와 비교했을 뿐…
그렇지만 오늘 이 시간을 통해 확실한 둘의 차이를 알 수 있었다.
원시 자료형의 비교는 includes
참조 자료형의 비교는 some
잊지 말자!🤩

아근데저렇게 콘솔에 바로 써도 메모리에 저장이되나?

이 부분 관련해서 알게 되서 공유드립니다!

  • 객체 리터럴은 평가될 때 마다 새 객체를 생성한다.
    • 따라서 변수에 식별자로 선언해주지 않고, console에 바로 찍어도, array.includes 내부에 있어도, 별도의 객체로 메모리에 생성이 된다.
  • 비교하는 앞 객체와 비교 당하는 뒤 객체가 각각 내용은 같지만 다른 메모리에 저장된 별개의 객체이다.
  • 객체는 참조값으로 비교를 하므로, 두 개의 실제 객체가 생성된 주소가 다르기 때문에 false 가 나온다.