자바스크립트 교과서 - 6. 타입 변환

Seoyong Lee·2023년 10월 2일
0

개발 공부

목록 보기
8/21
post-thumbnail

다음 책들과 MDN의 내용을 모아 자바스크립트 핵심 위주로 정리해 보았습니다. 여러 권의 책을 한 글로 훑어볼 수 있도록 최대한 간결하게 정리하겠습니다.

모던 자바스크립트 Deep Dive - 이웅모
You Don’t Know JS - Kyle Simson
자바스크립트는 왜 그 모양일까?(How JavaScript Works) - Douglas Crockford
자바스크립트 완벽 가이드(JavaScript: The Definitive Guide 7/E) - David Flanagan


명시적 타입 변환

  • 명시적 타입 변환(explicit coercion)은 타입 캐스팅(type casting)이라고도 부르며 개발자가 의도적으로 값의 타입을 변환하는 것을 말한다.
  • 대표적으로 String( )과 같은 네이티브 함수나 toString 메서드를 이용하는 방법이 있다.

암묵적 타입 변환

  • 암묵적 타입 변환(implicit coercion)은 타입 강제 변환(type coercion)이라고도 부르며 표현식을 평가하는 도중에 자바스크립트 엔진에 의해 암묵적으로 변환되는 것을 말한다.
  • 대표적인 경우는 + “” 를 이용해 숫자를 문자열로 강제 변환하는 방법으로 이 외에도 몇 가지 사례가 존재한다.
  • 암묵적 타입 변환은 타입을 변경하겠다는 개발자의 의도가 코드에 명백히 나타나지 않는다. 따라서 예측 가능성이 낮아지고 오류를 발생시킬 가능성이 높아질 수 있다.
  • 그러나 협업하는 동료가 코드를 이해하는 데 문제만 없다면 암묵적 타입 변환을 이용해 코드의 가독성을 개선할 수도 있다.
  • 흥미롭게도 타입 변환을 주제로 두 책의 저자 간 충돌이 발생한다. You Don’t Know JS의 저자 카일 심슨은 암묵적 타입 변환을 잘 이해하고 사용하면 나름 괜찮다고 주장하는 편으로 더글러스 크락포드의 초창기 자바스크립트 책에 대해 다음과 같이 비판하였다.

    더글라스 크락포드(Douglas Crockford)는 많은 연설과 기고문에서 자바스크립트 강제변환은 반드시 피해야 한다고 역설한 바 있다. 그는 아마도 암시적 강제변환을 나쁜 것이라 생각한 모양이다. 하지만 그 자신이 직접 작성한 예제 코드에도 명시적/암시적 강제변환을 혼용했다! 사실 그가 주로 염려했던 것은 동등 연산(==)이 아니었나 싶은데, 이 장을 계속 읽다 보면 여러분도 알겠지만, 동등 연산은 강제변환 메커니즘의 일부에 지나지 않는다.

    카일 심슨 , 『You Don’t Know JS - 타입과 문법, 스코프와 클로저』, 한빛미디어(2017), p113.


타입별 변환 방법

문자열 타입으로 변환

  • 다음 방법을 통해 문자열 타입으로 변환할 수 있다.
    • String 생성자 함수를 new 없이 호출 (명시적)
      • String(1); // “1”
    • Object.prototype.toString 메서드 사용 (명시적)
      • (1).toString(); // “1”
    • 문자열 연결 연산자 사용 (암묵적)
      • 1 + ''; // "1"

숫자 타입으로 변환

  • 다음 방법을 통해 숫자 타입으로 변환할 수 있다.
    • Number 생성자 함수를 new 없이 호출 (명시적)
      • Number("1"); // 1
    • parseInt, parseFloat 함수를 사용(문자열만 변환 가능) (명시적)
      • parseInt("-1"); // -1
      • 단항 산술 연산자 이용 (암묵적)
        • + '0'; // 0
      • 산술 연산자 이용 (암묵적)
        • '0' * 1; // 0

불린 타입으로 변환

  • 다음 방법을 통해 불린 타입으로 변환할 수 있다.
    • Boolean 생성자 함수를 new 없이 호출 (명시적)
      • Boolean(0); // false
    • ! 부정 논리 연산자를 두 번 사용 (명시적)
      • !!1 // true
  • 또한 다음 표현식에선 비 불리언 값의 조건 표현식 평가를 위해 불린 형태로 암시적 강제변환이 일어난다.
    • if ( ) 문의 조건 표현식
    • for ( ; ; ) 에서 두 번째 조건 표현식
    • while ( ) 및 do while ( ) 루프의 조건 표현식
    • ? : 삼항 연산 시 첫 번째 조건 표현식
    • || 및 &&의 좌측 피연산자

단축 평가

  • 논리합(||) 연산자와 논리곱(&&) 연산자는 논리 연산의 결과를 결정하는 피연산자를 타입 변환 없이 반환하며 이를 단축 평가(short-circuit evalution)라 한다.
  • 단축 평가는 표현식을 평가하는 도중 평가 결과가 확정된 경우 나머지 평가 과정을 생략한다.
  • 이러한 연산자들의 반환값은 평가 결과인 불리언 값이 아닌 피연산자의 값 중 하나인 것이 특징이다.
  • ES2020에선 다음 두 연산자가 추가되었다.
    • 옵셔널 체이닝 연산자 (optional chaining): 좌항의 피연산자가 null이나 undefined인 경우 undefined를 반환하고 그렇지 않다면 우항의 참조를 이어간다.
      const elem = null;
      const value = elem?.value;
      console.log(value); // undefined
    • null 병합 연산자 (nullish coalescing): 좌항의 피연산자가 null이나 undefined인 경우 우항의 피연산자를 반환하고 그렇지 않으면 좌항의 피연산자를 반환한다.
      const foo = null ?? "string";
      console.log(foo); // "string"

동등 비교

  • 동등 비교는 == 느슨한 동등 비교(Loose Equals)와 === 엄격한 동등 비교(Strict Equals)로 나뉜다.
  • 강제변환이 필요하다면 느슨한 동등 연산자(==)를, 필요하지 않다면 엄격한 동등 연산자(===)를 사용할 수 있다.
  • 그러나 느슨한 동등 비교는 다음과 같이 true/false와 직접 비교하려 하면 함정에 빠질 수 있다.
    const a = "42";
    const b = true;
    a == b; // false
  • “42”는 truthy 값이므로 결과는 true일 것 같지만 실제로 ES5 §11.9.3.6-7에 따르면 동등 연산자는 다음과 같이 작동한다.
    1. Type(x)이 불리언이면 ToNumber(x) == y 의 비교 결과를 반환한다.
    2. Type(y)이 불리언이면 x == ToNumber(y) 의 비교 결과를 반환한다.
  • 위 결과를 대입하면 2번이 적용되어 “42” == 1 이 되고, a의 “42”는 42로 바뀌어 42 == 1 → false가 된다.
  • 따라서 되도록이면 다음 룰을 따르는 것이 좋다.
    • 피연산자 중 하나가 true/false일 가능성이 있으면 절대로 쓰지 말자
    • 피연산자 중 하나가 [ ], “ “, 0 이 될 가능성이 있으면 가급적 쓰지 말자
  • 그냥 마음 편하게 엄격한 동등 비교만 사용하는 것도 방법이다.

객체 래퍼(참고)

  • 타입 변환의 경우에 사용하는 String, Number, Boolean 등의 함수는 new와 함께 사용하지 않으며, 만약new 키워드와 함께 사용하면 객체 래퍼를 생성하게 된다. 객체 래퍼에 대해서는 You Don’t Know JS 3장, ‘네이티브’에 관련 내용이 잘 정리되어 있다.
  • 다음과 같이 String 네이티브를 new를 이용해 생성자처럼 사용할 수 있지만 실제로는 object의 하위 타입으로 생성되며 원시 값과 다른 원하지 않는 결과물이 나오게 된다. 이것이 바로 객체 래퍼이다.
    const a = new String("a");
    typeof a; // "String"일 것 같지만 실제로는 "object" 다.
    console.log(a); // 브라우저마다 결과가 다름
  • 이러한 객체 래퍼의 존재 이유는 원시값에 자체적으로 프로퍼티나 메서드가 없기 때문으로 .toString( )과 같은 메서드를 사용할 때 자바스크립트가 자동으로 원시 값을 객체 래퍼로 박싱(래핑)한다.
  • 그렇다면 이러한 메서드를 처음부터 가지고 있도록 만들어지지 않은 이유는 무엇일까?

    빈번하게 문자열 값의 프로퍼티/메서드를 사용해야 한다면 자바스크립트 엔진이 암시적으로 객체를 생성할 필요가 없도록 처음부터 값을 객체로 갖고 있는 것이 이치에 맞는 것처럼 보인다. 하지만 좋은 생각이 아니다. 오래전부터 브라우저는 이런 흔한 경우를 스스로 최적화하기 때문이다. 즉, 개발자가 직접 객체 형태로 ‘선 최적화(Pre-Optimize)’하면 프로그램이 더 느려질 수 있다. 직접 객체 형태로 써야 할 이유는 거의 없다. 필요시 엔진이 알아서 암시적으로 박싱하게 하는 것이 낫다.

    카일 심슨 , 『You Don’t Know JS - 타입과 문법, 스코프와 클로저』, 한빛미디어(2017), p70.

  • 따라서 자바스크립트 내부적인 필요로 객체 래퍼가 존재하긴 하지만 개발자가 이를 직접 사용할 이유는 없다고 알아 두면 좋을 것 같다.

References

데이비드 플래너건 , 『자바스크립트 완벽 가이드』, 인사이트(2022)
더글러스 크락포드 , 『자바스크립트는 왜 그 모양일까?』, 인사이트(2020)
카일 심슨 , 『You Don’t Know JS - 타입과 문법, 스코프와 클로저』, 한빛미디어(2017)
이웅모 , 『모던 자바스크립트 Deep Dive』, 위키북스(2020)

0개의 댓글