4.1 값 변환
: 어떤 값을 다른 타입의 값으로 바꾸는 과정
→ 명시적 : 타입캐스팅 - 코드만 봐도 의도적으로 타입변환을 일으킨다는 사실이 명확(명백한)
→ 암시적 : 강제변환 - 다른 작업 도중 불분명한 부수효과로부터 발생하는 타입변환()
- 강제변환시 문자열, 숫자, 불리언 같은 원시값 중 하나가 됨!
- 객체, 함수 같은 합성 값 타입을 변환될 일 ❌ → ‘박싱'은 값을 감싸는 것, 강제변환 ❌
var a = 123;
var b = a + ""; //암시적 강제변환
var c = String(a); // 명시적 강제변환
4.2 추상연산
: 값이 특정 타입으로 변환되는 기본규칙
- ToString
- ToNumber
- ToBoolean
- ToPrimitive
1) ToString
: ‘문자열이 아닌 값'→’문자열'의 변환 작업
(1)내작 원시값은 본연의 문자열화 방법이 정해져 있음
- null → “null”
- undefined→ “undefined”
- true→”true”
(2)숫자 : 그냥 문자열로 바뀜 (너무 작거나 큰값은 지수 형태로 바뀜)
var a = 12.31*10000000000000000 *100000000000000;
console.log(a.toString()); //1.231e+31
(3)기본적으로는 Object.prototype.toString()의 toString()메서드가 내부 Class를 반환
(4)자신이 toString() 메서드를 가진 객체는 자신의 toString()메서드를 호출
- 배열 → 원소값이 ‘,’로 분리된 형태로 출력
var arr = [1,2,3,1];
console.log(arr.toString()); //1,2,3,1
- JSON 문자열화
- JSON.stiringify() : toString()과 기본적인 로직은 같음
- 안전값 : JSON 표현형 으로 나태낼 수 있는 값은 모두 문자열 변환 가능
- 안전한 값이 아닌 경우 : 다른 언어로 인식하여 JSON값으로 쓸 수 없는, 표준 JSON규격을 벗어난 값
- ex) undefined, 함수, 심볼, 환형 참조객체
- 환형참조객체 : 마지막 객체가 첫번째 객체를 참조하는 등 순환 참조가 발생하여 메모리 누수를 유발하는 객체, HTML DOM객체와 자바스크립트 간 연동 시 실수로 환형참조 하는 경우 종종 발생 → JSON.stringify : 에러
- 이런 값이 배열에 포함되어 있으면 null로 바뀜
- 객체 프로퍼티에 있으면 지움
- ex) undefined, 함수, 심볼, 환형 참조객체
console.log(JSON.stringify("12")); //"12" console.log(JSON.stringify(12)); //12 console.log(JSON.stringify(null)); //null console.log(JSON.stringify(undefined)); //undefined console.log(JSON.stringify(true)); //true console.log(JSON.stringify(function (){})); //undefined console.log(JSON.stringify([1,12,undefined,31])); //[1,12,null,31] console.log(JSON.stringify({a:12, b:function (){}})); //{a:12}
- 부적절한 JSON값이나 직렬화하기 곤란한 객체값을 문자열화 하려면 toJSON()메서드를 따로 정의
- toJSON() : 문자열화하기 적당한 JSON 안전 값으로 바꾸는 것( JSON 문자열로 바꾸는 것 ❌ )
2) ToNumber
: ‘숫자가 아닌 값' →’수식 연산이 가능한 숫자’
- true →1, false → 0, undefined → NaN, null →0
- 숫자 리터럴 규칙과 비슷
- 변환이 실패하면 NaN
- 8진수는 10진수로 처리
- 객체 : 동등한 원시 값으로 바꾼 뒤 toNumber변환
3)ToBoolean
(1) falsy
: boolean 문맥에서 특정 값을 false 로 형변환
- false
- 0
- -0 : 음수 0
- 0n : Bigint
- “” : 빈 String
- null
- undefined
- NaN
→ 이 falsy가 아닌 값을 가지면 boolean에서는 모두 true
→ 빈 배열([])은 true로 변환❗️
if(false)
console.log("hello false");
if(0)
console.log("hello 0");
if(-0)
console.log("hello -0");
if(0n)
console.log("hello 0n");
if(null)
console.log("hello null");
if(undefined)
console.log("hello undefined");
if(NaN)
console.log("hello NaN");
if ("")
console.log("hello");
*️⃣ 참고 *️⃣
- 빈 배열의 false 반환 방법
→ length 프로퍼티 이용 : 빈 배열.length = 0을 반환
var arr = [];
if(arr)
console.log("arr is not empty");
// 빈 배열이지만 arr is not empty 출력
if(arr.length)
console.log("arr is not empty");
// arr.lenth = 0 이기에 if 안이 false, 출력 X
- falsy 객체 : 해당 객체가 boolean값에서 false로 판단되는 객체
- document.all : 웹 페이지의 요소르 자바 스크립트 프로그램에 가져올 수 있게 함. 그러나 “비표준”
- 그냥 없애려고 하였으나 의존하는 레거시 코드 베이스가 너무 많아서 falsy인 것처럼 돌아가게 함
- document.all : 웹 페이지의 요소르 자바 스크립트 프로그램에 가져올 수 있게 함. 그러나 “비표준”
(2) truthy
: falsy한 값이 아닌 값(falsy 목록에 없는 값)
4.3 명시적 강제변환
1) 문자열 ↔ 숫자
- String(), Number() : 원시 문자열/숫자로 강제변환(ToString과 ToNumber추상연산로직을 따름)
var a = 3;
String(a); // "3"
var b = "15.23";
Number(b); // 15.23
- 다른 방법
var a = 3;
a.toString(); // "3"
var b = "15.23";
+b; // 15.23
- a 를 변환할 때는 toString() 메서드를 사용했는데 당연히 a 에는 없기 때문에 객체 래퍼로 박싱해서 호출
- b 를 변환할 때는 단항연산자(Unary Operator) +,-를 사용하여 강제변환
- 단항연산자의 경우, 가독성이 떨어지고 실수할 수 있는 확률이 높기 때문에 쓰지 않는 것이 좋음.
2) 날짜 → 숫자
: Date객체 → 숫자로 강제 변환
- 현재 시각을 타임스탬프로 바꾸기 : Date 객체를 활용
var now = +(new Date());
다음 과정으로 변환
- new Date() : 현재 날짜/시각을 가리키는 객체
- 단항연산자 + 를 사용해서 객체를 숫자로 강제변환 : ToNumber 추상 연산이 적용
- valueOf() 메서드를 확인하니, 있고 원시 숫자값 반환
var now1 = (new Date()).getTime();var now2 = Date.now();
이외에도 2가지 방법이 더 있다.
3) 틸드(~) 연산자 활용
: 강제변환 연산자
- 자바스크립트의 비트 연산자는 32비트 연산만 가능하기 때문에 피연산자를 32비트로 바꾸는 ToInt32 추상 연산을 수행하고 NOT 연산을 수행. 이런 성질은 -1 과 같은 값에 활용될 수 있는데, 여기에 틸드연산을 적용하면 0 이 되기 때문이다. 문자열의 메서드인 indexOf() 는 대상을 못 찾으면 -1 을 반환하는데 보통 다음과 같이 사용한다.
if ("123".indexOf("3") !== -1) { console.log("찾았다!");}
하지만 -1 이 “실패” 라는 의미를 가졌다는 것을 노출시키기 때문에 그다지 좋지 않고 여기에 틸드 연산을 활용한다.
if (~("123".indexOf("3"))) { console.log("찾았다!");}
만약에 "4" 를 못찾으면 -1 을 반환하는데 여기다 틸드연산을 적용할 경우 0 이 되어서 false 가 된다. 따라서 찾았을 경우는 true 이므로 위와 같이 사용하게 되는 것이다. 만약, if문 안에 직접적으로 불리언 값을 적어주고 싶다면 !! 를 적용하면 된다.
if (!!~("123".indexOf("3"))) { console.log("찾았다!");}
틸드연산은 이외에도 비트 잘라내기에 유용, 소수의 소수점 이상을 잘라내기 위해서 사용되고는 한다.
~~123.14; // 123
이외의 방법으로는 Math.floor() 를 사용하지만 음수의 경우 2개의 작동방식이 다르다 는 점을 주의‼️
4) 숫자형태의 문자열 파싱
앞서배운 문자열 강제변환과는 차이점이 있는데, 비 숫자형 문자를 허용한다는 것이다.
Number("123a"); // NaNparseInt("123a"); // 123
단, parseInt() 의 경우에는 첫번째 인자로 문자열을 받는다. 비 문자열은 문자열로 강제변환되는데, 여기서 예측하기 힘들기 때문에 그냥 비 문자열은 인자로 전달하지 말자. 두번째 인자로는 기수(radix)를 전달하는데, 이 정보를 기반으로 첫번째 인자로 전달된 문자열을 파싱한다. 만약, 두번째 인자가 없다면 ES5 이후부턴 10진수로 처리하고 이전에는 첫번째 문자만 보고 추정한다.
5) 비 불리언 → 불리언
ToBoolean 추상연산과 동일하게 엄청 간단하다.
Boolean([]); // trueBoolean({}); // trueBoolean("false"); // trueBoolean("0"); // trueBoolean(null); // falseBoolean(undefined); // false
이외에도 단항연산자 ! 를 사용할 수 있다.
!!null; // false
4.4 암시적 강제변환
: 숨겨진 형태로 일어나는 타입변환으로 명백하게 보이지 않는 타입변환의 총칭
1) 문자열 ↔ 숫자
피연산자 한쪽이 문자열이라면 + 연산자는 항상 문자열 붙이기를 한다. 만약, 피연산자가 객체라면 다음 과정을 거친다.
- 객체를 ToPrimitive 추상연산으로 원시값으로 변환
- 원시값을 ToString 추상연산으로 문자열로 변환
- 문자열 붙이기를 한다.
따라서 아래와 같은 일이 일어난다.
var a = [1,2];
var b = [3,4];
a + b; // 1,23,4
보통 숫자를 문자열로 변환할 때의 가장 많이 쓰는 일반적인 방식은 다음과 같다.
var a = 4;
a + ""; // "4"
주의할 점은 String(a) 의 경우 toString() 을 바로 호출하지만 암시적 변환의 경우 valueOf() 를 먼저 호출한다는 사실이다. ES5 9.1 ToPrimitive 연산을 참고하면 보다 확실히 알 수 있다.
2) 비 불리언 → 불리언
불리언으로의 암시적 강제변환이 일어나는 경우는 다음과 같다.
- if 문의 조건 표현식
- for 문의 2번째 조건 표현식
- while 및 do~while문의 조건 표현식
- 삼항연산자의 첫번째 조건 표현식
- 논리연산자 및 좌측 피연산자
3. &&와 || 연산자
: 보통 다른 언어에선 &&와 ||의 반환값이 불리언 값이지만 자바스크립트에선 피연산자 중 하나로 귀결된다.
var a = 1;
var b = null;
a && b; // null
a || b; // 1
- && : boolean으로 변환시 앞쪽이 true일 경우에 뒤쪽을 반환
- || : boolean으로 변환시 앞쪽이 false일 경우에 뒤쪽을 반환
4.5 느슨한/엄격한 동등비교
: 흔히 == 와 === 의 차이점을 타입을 비교하냐의 여부로 따지고는 한다. 하지만 엄격히 말하자면, 강제변환을 허용하는가의 여부 이다. 즉, == 는 강제변환을 허용하고 === 는 강제변환을 허용하지 않는다.
결국 == 에서 암시적 강제변환이 발생하며 이는 여러가지 예측하기 힘든 결과들을 내놓는다.
[느슨한 동등 비교(==)의 규칙]
- 피연산자 중 하나가 문자열일 경우 : 문자열을 숫자로 강제변환
- 피연산자 중 하나가 불리언일 경우 : 불리언을 숫자로 강제변환
- 피연산자가 null 또는 undefined 일 경우 : 양쪽이 모두 null 이나 undefined 일 경우에만 참이다.
- 피연산자가 비객체와 객체인 경우 : 객체를 원시값으로 강제변환
- 따라서 다음이 성립
"0" == false; // true
-
- 피연산자 중 하나가 불리언이므로 숫자로 변환 "0" == 0
- 피연산자 중 하나가 문자열이므로 숫자로 변환 0 == 0
이런 결과들을 보고나면 무조건 엄격한 동등 비교인 === 를 쓰면 되는 것이 아닌가? 라고 생각할 수 있다. 저자의 의견은 무조건적으로 사용하지 말고 그 근본 원리를 이해하고 적절히 사용하는 것이 바람직하다고 한다.
예를 들어, 피연산자 중 하나가 null 이나 undefined 라면 == 를 쓰는 것이 훨씬 간단하다.
1) 추상 동등 비교
- NaN은 그 자신과도 동등❌
- +0과 -0은 동등❌
(1) 문자열 → 숫자 변환
var a = 42;
var b = "42";
a === b;// false
a == b;// true
💡 [비교 과정]
x == y 에서
1. Type(x)가 Number이고, Type(y)가 String이면, x == ToNumber(y) 비교 결과 반환
2. Type(x)가 String이고, Type(y)가 Number면, ToNumber(x) == y 비교결과 반환
→ 위의 예시에서는 Type(a) = Number, Type(b) = String 이므로, “42”가 42로 변환되어 비교
(2)비불리언 → 불리언
: boolean 타입과 비교할 때는, boolean을 ToNumber()값에 넣어서 비교하기 때문에 주의‼️
var a = "42";
var b = true;
a == b;// false
💡 [비교 과정]
x == y 에서
1. Type(x)가 불리언이면, ToNumber(x) == y 비교결과 반환
2. Type(y)가 불리언이면, x == ToNumber(y) 비교 결과 반환
→ 위의 예시는 2에 해당하므로 true → toNumber로 변환한 1 반환
→ 42 == 1 이므로 false (”42”도 42로 변환 )
(3)null과 undefined를 느슨한 동등비교(==)를 하면 서로에게 타입을 맞춤
💡 [비교 과정]
x == y 에서
1. x가 null이고, y가 undefined 면 true 반환
2. x가 undefined이고, y가 null이면, true 반환
var a = null;
var b; //undefined
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
(4)객체의 경우 ToPrimitive() 결과를 통해 비교
💡 [비교 과정]
x == y 에서
1. Type(x)가 String 또는 Number이고 Type(y)가 Object라면, x == ToPrimitive(y)비교결과 반환
2. Type(x)가 ObjectType(y)가 String 또는 Number이면, ToPrimitive(x) == y 비교 결과 반환
var a = 42;
var b = [ 42 ];
a == b;// true
→ 위의 예시에서는 toPrimitive([42]) →”42”이므로 42 == ”42”
한번 더 타입변환을 거쳐 42 == 42 이므로 true 반환
- null과 undefined의 경우는 객체 래퍼가 따로 없으므로 예외
2) 희귀사례
"0" == false; // true
false == 0; // true
false == ""; // true
false == []; // true
"" == 0; // true
"" == []; // true
0 == []; // true
암시적 강제변환을 안전하게 사용하려면 다음을 명심하자.
- 피연산자 중 하나가 true/false일 가능성이 있으면 절대 == 연산자를 쓰지 마라.
- 피연산자 중 하나가 [], "", 0이 될 가능성이 있으면 가급적 == 연산자를 쓰지 마라.
4.6 추상 관계 비교
: < 연산에 대한 것으로, ToPrimitive 연산을 한 후의 피연산자가 모두 문자열일 경우와 그렇지 않은 경우로 나뉨
- 피연산자가 모두 문자열 : 알파벳 순서로 비교
var a = ["abc"];
var b = ["zd"];
a < b; // true
- 피연산자가 모두 문자열 ❌ : ToNumber를 통해 숫자로 변환해서 비교
var a = [1];
var b = ["2"];
a < b; // true
단, <= 의 작동방식은 기존에 알고 있던 대로 동작하지 않는다. > 의 결과를 부정한 방식을 적용
var a = {};
var b = {};
a < b; // false
a >= b; // true
: 여기서 a 와 b 는 문자열 [object Object] 로 변환되기 때문에 a<b 의 결과는 false
→ 따라서, >= 의 결과는 그걸 부정한 참
질문이나 궁금하신점 또는 제가 잘못 알고있는 정보는 댓글 부탁드립니다😊
'FrontEnd 공부 > 자바스크립트 이론' 카테고리의 다른 글
You don't konw JS - 5장 문법 (0) | 2023.04.13 |
---|---|
You don't konw JS- 3장 네이티브 (0) | 2023.04.11 |
You don't konw JS- 2장 값 (1) | 2022.10.07 |
You Don't Know JS - 자바스크립트의 타입 (0) | 2022.09.01 |