개발/Javascript

[You don't know JS] Part1 - 4장. 강제변환

lanace 2020. 9. 6. 06:21

Ch4. 강제변환

4.1 값 변환


타입 케스팅

값이 다른 타입의 값으로 바뀔 때 명시적이면 타입 케스팅

코드에서 의도적으로 타입을 변화시킨 것

강제 변환

값이 다른 타입의 값으로 바뀔 때 암시적이면 강제 현

다른 작업 도중 불분명한 사이드 이펙트로 생긴것

var a = 42;
var b = a + "";    // 암시적
var c = String(a);  // 명시적

위 코드로 암시적과 명시적은 어느정도 이해가 되었을 것이고,

문제는 어떻게 동작하느냐 인데, 다음을 보자

동작에 대한 내용은 안나오는듯...? 모지;; 찾아야되나

Javascript에선 강제변환을 하면 Number, String, Boolean 같은 원시값중 하나가 되며, 함수나 객체는 변환될 일이 없다.
박싱은 원시값을 해당 객체로 감싼건데, 이것은 강제변환이 아니다.

위에 예제에서 b = a + "" 는 자바스크립트 개발자가 보았을때 충분히 명시적일 수 있다.

하지만 다른 개발자가 보았을때 그렇지 않을 수 있기 때문에 암묵적으로 본다.

4.2 추상 연산


4.2.1 ToString

String 이 아닌값 → String

내장 원시값들은 문자열화 방법이 이미 정해져있다.

null -> "null"
undefined -> "undefined"
true -> "true"
314 -> "314"

{...simething} -> "[object Object]"
[1, 2, 3] -> "1,2,3"

Object → String 을 보면 일반적인 객체는 Object.prototype.toString 메서드가 내부 [[Class]]를 반환한다.

엄밀하게 객체 → 문자열 강제변환시 ToPrimitive 연산 과정을 거친다.
이후에 나오니 확인해보자.

Array → String 은 ',' 로 join된 값이 기본 toString 로직이다.

Json → String

ToString 은 Json.stringify() 로 Json 문자열화 하는데 연관있다.

JSON.stringfiy(42);  // "42"
JSON.stringfiy("42");  // ""42""
JSON.stringfiy(null);  // "null"
JSON.stringfiy(true);  // "true"

문자열은 ""로 한번 더 감싸진다.

JSON 형태로 표현하지 못하는 것들이 있는데, 이는 아래와 같이 처리된다.

  • undefined → 필드 누락 (배열이면 null로 변경)
  • function → 필드 누락 (배열이면 null로 변경)
  • Symbol → 필드 누락 (배열이면 null로 변경)
  • 환형 참조 → 에러 발생
JSON.stringfiy(undefined);  // undefined
JSON.stringfiy(function() {});  // undefined

JSON.stringfiy(
    [1, undefined, function() {}, 4]
);  // "[1, null, null, 4]"

JSON.stringfiy({
    a: 2,
    b: function() {}
});  // "{"a": 2}"

객체에 toJSON이 정의되어 있으면 이 메서드를 먼저 호출하여 직렬화 한다.

환형 참조가 예상되거나 함수등 처리할 수 없으면 toJSON을 정의해두면 좋다.
(하지만 toJSON을 일반적으로 쓰는지는 잘 모르겠네...)

toJSON은 적절히 평범한 실제 값을 반환하고, 문자열 처리는 JSON.stringify()가 담당한다.

즉, toJSON → JSON.stringfy() 순서로 처리된다고 보면 될듯 하다.

  • JSON.stringfiy()

결론적으로 JSON.stringify()와 ToString의 연관은 다음 두가지이다.

  1. String, Boolean, Number가 문자열로 형변환 될때 ToString을 사용한다.
  2. toJSON을 가지고 있으면 문자열화 전에 toJSON이 호출되어 JSON 안전값으로 강제변환 된다.

4.2.2 ToNumber

숫자가 아닌 값 → 숫자

true -> 1
false -> 0

undefined -> NaN
null -> 0

8진수를 의미하는 앞에 0 을 붙인 수는 정상적으로 변환되지 않음.
일반적인 10진수처럼 변경됨 (ex. "012" → 12)

객체 → Number

동등한 원시값으로 변경한 뒤 toNumber의 로직에 따라 동작한다.

원시값을 구하기 위해 valueOf() 메서드를 구현 했는지 확인한다. 만약 구현되어 있지 않으면 toString()을 사용하며, 그것도 안되면 TypeError를 던진다.

ES5부터는 [[Prototype]]이 null인 경우 대부분 Obect.create(null)을 이용하여 강제변환이 불가능한 객체를 생성할 수 있다.

var a = {
    valueOf: function() {
        return "28";
    }
}

var b = {
    toString: functuin() {
        return "28"
    }
}

var c = [2, 8];
c.toString = function() {
    return this.join('');
}

Number(a);
Number(b);
Number(c);

4.2.3 ToBoolean

  • Falsy vs Thrusy

falsy객체

과거 비표준 스펙인 document.all 과 같은 유사배열을 사용하여 IE를 구분하고 있었는데, 이는 document.all이 trusy로 동작했다.

하지만 이후 falsy 하게 변경하기 위해 브라우저단에서 변경하여 falsy 처럼 동작되게 랩핑 되었다.

그래서 남은 흔적이다...

falsy 객체란 겉보기엔 평범한 객체지만 boolean으로 강제변환 하면 false가 되는 객체를 말한다.

truthy 값

falsy를 제외한 나머지값

4.3 명시적 강제변환


명시적 변환은 분명하고 확실한 타입 변환이다.

대게 이러한 방식을 선호한다.

4.3.1 명시적 강제변환: 문자열 ↔ 숫자

Number와 String을 사용하는데, 앞에 new 키워드는 붙지 않는다.

그럼 붙이면 어떻게 되는가?
new 키워드는 진짜 필요 없는거 아닌가??

4.4 암시적 강제변환


4.4.1 '암시적' 이란?

4.4.2 암시적 강제변환: 문자열 ↔ 숫자

질문!

console.log({} + []);
console.log([] + {});

이거 결과가 둘다 [Object ] 인데, 아래 코드는 각각 [Object ~] , 0 이다.

[] + {}
{} + []
var a = {
    valueOf: function() {return 42},
    toString: function() {return 2};
}

4.4.3 암시적 강제변환: boolean → number

4.4.4 암시적 강제변환: * → boolean

강제 변환이 일어나는 표현식

  • if () 문의 조건 표현식
  • for 에서 조건 표현식
  • while 에서 조건 표현식
  • ? : 삼항 연산자의 조건 표현식
  • ||, && 의 좌측 피연산자

4.4.5 &&와 || 연산자

일반적인 논리 연산자와는 조금 다름

오히려 선택 연산자로 보는게 맞을듯 함

다른 언어와 달리 실제로 반환값이 boolean타입이 아니기 때문

두 피연산자의 값중 하나를 선택하게 된다.

var a = 42;
var b = "abc";
var c = null;

a || b;
a && b;

c || b;
c && b;

4.4.6 심벌의 강제변환

var s1 = Symbol('');
String(s1);

var s2 = Symbol("");
s2 + "";  // error

4.5 느슨한 / 엄격한 동등 비교


4.5.1 비교 성능

4.5.2 추상 동등 비교

0 === -0은 true

http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero 참고

4.5.3

4.5.4

4.5.5

4.6 추상 관계 비교


추상 관계 비교는 두 피연산자 모두 string 일때와 그 외인 경우로 나뉜다.

a < b

  • a, b 모두 string 인 경우
  • 그 외
var a = [42];
var b = ["43"];

a < b;  // true
b < a;  // false

4.7 정리하기