리액트는
가상 DOM과 실제 DOM의 비교
컴포넌트 렌더링 판단
변수, 함수의 메모이제이션
등이 자바스크립트의 동등 비교 기반입니다.
이번 글에서는 자바스크립트의 동등 비교에 대해 알아보고, 그것이 리액트에서 어떻게 활용되는지 알아봅시다.
자바스크립트의 동등 비교
데이터 타입
자바스크립트에서 모든 값은 타입을 가지고 있음. (변수는 동적 타입) 크게 원시 타입과 객체 타입으로 나눌 수 있습니다.
원시 타입: 객체가 아닌 모든 타입
boolean
: 참/거짓null
: 값이 없음(명시적)undefined
: 값이 할당되지 않음(암시적)number
: 숫자string
: 문자열Symbol
: 유일한 값Bigint
: 큰~ 숫자
객체 타입(참조 타입): 원시 타입이 아닌 타입 (ㅋㅋ)
object
(배열, 함수, 정규식, 클래스 기타 등등)
원시 타입과 객체 타입의 차이
원시 타입의 데이터는 크기가 정해져있고, 변수에 할당될 때 변수 저장 스택 메모리에 참조가 직접 저장됩니다.
반대로 객체 타입의 데이터는 크기가 가변적이고,예측할 수 없기 때문에 힙 메모리에 저장됩니다.
그리고 변수는 그 객체의 주소 참조를 스택 영역에 저장하여 그 주소를 통해 데이터에 접근하게 됩니다.
알고 넘어갈 점
스택 메모리는 블록 종료/함수 종료 시 즉시 정리됩니다.
반면에 힙 메모리는 생명주기가 긴 데이터를 가지는게 일반적이고, GC에 의해 관리됩니다.
원시 타입의 값이라 하더라도, 클로저에 의해 캡처된 값은 힙 메모리에 저장됩니다.
Object.is
메소드
자바스크립트의 기본 동등 비교법인 ===
와 비슷하지만 아래의 특정한 상황에 좀 더 정확히 비교하는 메소드입니다.
폴리필 코
function(x, y) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
}
return x !== x && y !== y;
}
예시
Object.is(+0, -0); // false
Object.is(Number.NaN, NaN); // true
Object.is(NaN, 0 / 0); // true
리액트에서의 동등 비교
아래는 리액트가 실제로 동등 비교를 할 때 사용되는 함수입니다.
ShallowEqual.js
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import is from './objectIs';
import hasOwnProperty from './hasOwnProperty';
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
if (is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];
if (
!hasOwnProperty.call(objB, currentKey) ||
// $FlowFixMe[incompatible-use] lost refinement of `objB`
!is(objA[currentKey], objB[currentKey])
) {
return false;
}
}
return true;
}
export default shallowEqual;
Object.is
로 비교한 후, 다를 시 두 객체 한 뎁스 안의 비교까지 수행하여 모든 속성이 같으면 또한 true
를 반환합니다.
알고 넘어갈 점: 이렇게 구현된 이유는 컴포넌트의 props가 객체이고, 리렌더링 판단 시 일차적으로 이전 props와 다음 props를 비교하기 위해서 입니다.