자바스크립트의 데이터 타입
자바스크립트의 모든 값은 데이터 타입을 갖고 있으며, 이 데이터 타입은 크게 원시 타입과 객체 타입으로 나눌 수 있다.
- 원시 타입 : boolean, null, undefined, number, string, symbol, bigint
- 객체 타입 : object
위 타입들 중 boolean의 true와 false 처럼 평가되는 값들이 존재하는데, 이를 truthy, falsy한 값이라 한다.
falsy 값은 false, 0, -0, 0n, 0x0n, NaN, "", '', ``, null, undefined 이며, truthy는 falsy를 제외한 모든 값이다.
Number
자바스크립트의 Number는 다른 언어와 다르게 모든 숫자를 하나의 타입에 저장한다. 그 수의 범위는 -(2^53 -1) 부터 2^53-1까지이다. 이 범위를 외의 값들은 bigint로 다룬다.
또한 자바스크립트는 2진수, 8진수, 16진수 등의 별도 데이터 타입을 제공하지 않기에 각 진수별로 값을 표현해도 모두 10진수로 해석되어 동일한 값을 표시한다.
8 == (8).toString(8) //true로 해석
BingInt
number가 다룰 수 있는 숫자 크기의 제한을 극복하기 위해 도입되었으며, 최대 2^55-1을 저장할 수 있다.
주의해야할 점은 일반 Number 타입의 값과 BigInt 타입의 값 끼리의 계산은 안되기에 Number 타입의 값을 BigInt 타입으로 변환하거나, BigInt 값을 Number 타입으로 변환하여 계산해야 한다.
객체 타입
앞서 기술한 7가지의 원시 타입을 제외한 모든 타입인 함수, 배열, 정규식, 클래스 등은 객체 타입이다.
객체 타입은 참조를 전달하며 작성한 코드와 실제 값이 다르게 동작할 수 있다.
const hello1 = function () {}
const hello2 = function () {}
hello1 === hello2 //false
값을 저장하는 방식의 차이
원시 타입은 불변 형태의 값이 저장되며 변수 할당 시점에 메모리 영역을 차지하고 저장된다.
하지만 객체는 프로퍼티를 삭제, 추가, 수정이 가능하기에 원시 값과 다르게 변경 가능한 형태로 저장되며 값을 복사할 때도 값이 아닌 참조를 전달하게 된다.
즉, 값은 같더라도 참조하는 곳이 다르기에 원시값에서 기대했던 결과를 얻을 수 없다.
Object.is
Object.is는 두 개의 인수를 받으며, 인수 두 개가 동일한지 확인하고 반환하는 메서드다.
Object.is를 활용하면 ==, ===를 사용하는 것 보다 개발자들이 생각하는데로 연산할 수 있다.
-0 === +0 // true
Object.is(-0,0)//false
Number.NaN === NaN //false
Object.is(Number.NaN,NaN) //true
NaN===0/0 //false
Object.is(NaN,0/0) //true
리액트에서의 동등 비교
Object.is는 리액트에서 폴리필을 이용한 구현을 통해 사용된다.
/**
* 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
*/
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
const objectIs: (x: any, y: any) => boolean =
// $FlowFixMe[method-unbinding]
typeof Object.is === 'function' ? Object.is : is;
export default objectIs;
리액트에서는 이 objectIs를 기반으로 동등 비교를 하는 shallowEqual이라는 함수를 만들어 사용한다.
/**
* 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({hello:'world'},{hello:'world'}) // false
shallowEqueal({hello:'world'} , {hello:'world'}) // true
리액트에서 객체의 얕은 비교까지 구현한 이유는 리액트에서 사용하는 JSX props는 객체이며, props만 일차적으로 비교하면 되기 때문이다.
type Props = {
hello : string
}
function HelloComponet(props : Props){
return <h1>{hello}</h1>
}
위 코드의 props는 객체이며 props에서 꺼내온 값을 기준으로 렌더링을 수행하기에 일반적인 케이스에서는 얕은 비교로 충분하다. 이러한 특성을 안다면 props에 도 다른 객체를 넘겨준다면 리액트 렌더링이 예상치 못하게 작동한다는 것을 알 수 있다.
props가 깊어지는 경우 객체간 비교가 완벽하지 않아 React.memo가 정상적으로 동작하지 않는 경우가 있다. 이런 경우가 있음에도 재귀적으로 완전히 객체를 비교하지 않는 이유는 성능에 악영향을 미칠 수 있기 때문이다.
이러한 특징을 잘 숙지한다면 훅의 의존성 배열의 비교, 렌더링 방지를 넘어 useMemo와 useCallback의 필요성, 렌더링 최적화를 위한 React.memo를 올바르게 작동시킬 수 있을 것이다.
'React > 모던 리액트 Deep Dive' 카테고리의 다른 글
리액트의 렌더링과 메모이제이션 (1) | 2024.11.08 |
---|---|
클래스 컴포넌트와 함수 컴포넌트 (0) | 2024.11.05 |
가상 DOM과 리액트 파이버 (2) | 2024.10.30 |
[React Deep Dive] JSX(Javascript XML) (0) | 2024.10.26 |
[React Deep Dive] 타입스크립트 (1) | 2024.10.17 |