Skip to main content

YOU DON'T KNOW JS - 1

2023-02-10#

Chapter1 타입 & Chapter2 값#

타입#

우선 자바스크립트 같은 동적 언어는 타입 개념이 없다고 생각하는 개발자가 많다고 한다. 하지만, 자바스크립트에도 어떤 값을 다른 값과 분별할 수 있는, 고유한 내부 특성의 집합(사전적 의미)이라고 할 수 있는 타입이 존재한다.

특히, 자바스크립트는 강제로 타입변환이 일어나는 사례가 많은데 타입별로 내재된 특성을 제대로 알고 있으면 값을 다른 타입으로 변환되는 상황이나 자바스크립트를 이해하는데 도움이 될 것이다.

내장 타입#

자바스크립트에는 7가지 내장 타입이 있다.

  • null
  • undefined
  • boolean
  • number
  • string
  • object
  • symbol(ES6부터 추가)

여기서 object를 제외한 타입을 원시 타입이라고 한다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures

값의 타입은 typeof 연산자로 알 수 있다. 하지만 해당 연산자를 사용해도 7가지 내장 타입과 1:1로 정확하게 떨어지지 않는다.

typeof

null#

여기서 nullfalsy 한 유일한 원시 값이지만, typeof 연산자를 사용해 보면 object인 특별한 존재이다.

그래서 typeof 연산자로 null 값을 정확히 확인하려면 아래처럼 조건이 붙어야 한다.

var a = null;
(!a && typeof a === "object"); // true

fuctnion & array#

함수도 typeof 연산자로 확인해보면 function을 반환하는데 마치 function이 최상위 레벨의 내장 타입처럼 보일 수 있다. 하지만 실제 명세를 읽어보면 object의 "하위 타입"이다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function

MDN의 문서를 참고해보면 아래처럼 함수에 대한 정의를 살펴볼 수 있다.

note

JavaScript의 Function은 다른 모든 객체처럼 속성과 메서드를 가질 수 있으므로 일급(first-class) 객체이다

다른 객체와 함수를 구별하는 것은, 함수는 호출할 수 있다는 점입니다. 간단히 말해, 함수는 Function 객체이다.

배열의 경우는 Key를 문자열로 가지는 object와는 반대로 숫자 인덱스와 length property가 자동으로 관리되는 등의 추가 특성을 지닌 객체의 "하위 타입"이라 할 수 있다.

#

배열#

자바스크립트의 배열은 어떤 타입의 값이라도 담을 수 있는 그릇으로 볼 수 있다.

array

책의 내용중에 배열 값에 delete 연산자를 적용하면 슬롯을 제거할 수 있다고 하지만, 마지막 원소까지 제거해도 length 속성 값까지 바뀌지 않기 때문에 주의할 필요성이 있다고 한다.

하지만, 실제 업무에서 배열을 다룰 때 생각을 해보면 배열 내장타입에서 제공하는 메서드를 기본적으로 많이 사용하고, 원본 배열을 복사해서 기존 데이터에 영향이 없도록 다양한 처리를 하고 있으며 delete 연산자를 사용할 일은 많지는 않다.

tip

배열에서 실제 원소를 없애고 싶으면 Array.prototype.splice() 내장 메서드를 사용해보자.

array-remove

유사 배열#

숫자 인덱스가 가리키는 값들의 집합을 유사 배열이라고 한다.

대표적으로 자바스크립트로 HTML DOM의 쿼리 작업을 실행하다 보면 유사배열 형태로 가져오는 것을 알 수 있다.

간단한 예시로 HTMLCollection 등을 예로 들 수 있겠다.

info

유사 배열은 내장 메서드를 사용할 수 없으므로 직접 반복문을 작성해서 순회하거나 Array.from() 혹은 Array.slice() 함수등을 사용해서 직접 배열로 만들어서 처리하는 방법도 있다.

문자열#

문자열을 문자의 배열이라고 생각할 수 있는데, 문자열은 배열과 겉모습이 닮은 유사 배열이다. 그리고 배열과의 큰 차이점으로 문자열은 불변 값이고 배열은 가변 값이라는 차이점이 있다.

문자열이 불변 값이라는 것은 문자열 메서드는 그 내용을 바로 변경하지 않고 항상 새로운 문자열을 생성 후 반환한다는 특징을 가지고 있다.

반면에, 배열의 메서드는 그 자리에서 곧바로 원소를 수정하는 메서드가 대부분이다.

  • Array.push() - 배열 끝에 요소 추가
  • Array.pop() - 배열의 마지막 요소 삭제
  • Array.unshift() 배열의 맨 앞에 하나 이상의 요소 추가
  • Array.shift() 배열의 맨 앞 요소 삭제
  • Array.splice() 배열의 기존 요소를 삭제 또는 교체 추가

사실상 문자열을 다룰 때 유용한 대부분의 배열 메서드는 문자열에 쓸 수 없지만 불변 배열 메서드는 빌려 쓸 수 있다는 특징이 있다.

tip

예시로, 문자열을 뒤집는 코드가 필요하다면 배열에는 reverse()라는 가변 메서드가 준비되어 있지만 문자열은 그렇지 않다.

문자열을 뒤집으려면 아래와 같은 코드가 필요하다.

string-reverse

숫자#

자바스크립트에서 숫자 타입은 number가 유일하며 "정수", "부동 소수점 숫자"를 모두 아우른다. 사실 자바스크립트의 정수는 부동 소수점 값이 없는 값이다. (42.0 = 42와 같음)

자바스크립트 숫자 리터럴은 10진수로 표시하며 소수점 생략, 지수표현, 등등 외에도 여러 가지 표현 방법들이 있다.

만약, 숫자의 자리수를 고정해서 나타내고 싶다면 소수점 이하자릿수를 고정해 주는 Number.toFixed() 메서드를 사용하거나 유효 숫자 자릿수를 지정하는 Number.toPrecision() 메서드를 사용할 수 있다.

number-fix

작은 소수 값

자바스크립트에는 널리 알려진 이진 부동 소수점 숫자의 부작용 문제가 있다. (IEEE 754 표준을 따르는 모든 언어에서 공통적인 문제라고 한다.)

number-epsilon

저런 부동 소수점을 처리하는 일반적인 방법으로는 미세한 반올림 오차를 허용 공차로 처리하는 방법이 있다.

저런 미세한 오차를 "머신 입실론(Machine Epsilon)" 이라고 하는데 자바스크립트의 숫자 머신 입실론은 2^-52이다. ES6부터는 Number.EPSILON으로 미리 정의되어 있다.

number-close-func

NaN#

NaN은 글자 그대로 "숫자 아님(Not A Number)"이다. 주의할 점은 NaN은 어떤 NaN과도 동등하지 않은 반사성이 없는 유일무이한 값으로 취급된다.

내장함수 isNaN()을 활용하면 숫자인지 여부를 평가할 수 있는데 이것으로 숫자여부를 판단할 때는 주의해야 한다. (문자열도 NaN으로 보기 때문)

ES6부터는 Number.isNaN() 메서드를 지원하니 안전하게 NaN 여부를 체크하자.

number-nan

값 vs 레퍼런스#

자바스크립트에서는 포인터라는 개념 자체가 없고 참조하는 방법도 조금 다르다. (c++ 언어를 예시로 든다면)

우선적으로 자바스크립트는 어떤 변수가 다른 변수를 참조할 수 없다.

자바스크립트에서 레퍼런스는 값을 가리키며 10개의 레퍼런스가 있다면 이들은 저마다 항상 공유된 단일 값을 개별적으로 참고하고 있는 상태이다.

자바스크립트에는 값 또는 레퍼런스의 할당 및 전달을 제어하는 구문이 없고 값의 타입만으로 값-복사, 레퍼런스-복사를 선택해서 실행된다.

note
  • 값-복사 방식 타입
    • string
    • number
    • boolean
    • null
    • undefined
    • symbol
  • 레퍼런스-복사 방식 타입
    • object
    • function
    • 합성 값(예시 -> 배열안에 여러 타입의 값들)

레퍼런스 복사

reference

실제 데이터를 다루는 업무에서는 특히 원본 데이터를 다룰 때 배열, 객체등에 담아서 쓰는 경우가 많다.

특히 자바스크립트에서 기존 데이터를 합성 값(배열 같은)에 담아서 사용하다 보면 수정되어야 하지 않을 데이터가 수정되거나 파악하기 어려운 버그가 발생할 수 있다.

단편적인 예로 레퍼런스 값을 값-복사에 의해 효과적으로 전달하기 위해서는 직접 값의 사본을 만들거나, 깊은 복사를 지원하는 라이브러리를 활용해서 값을 사용하는 것이 좋은 방안이 될 수 있다.