[javascript] 타입과 문법

·

5 min read

타입

내장 타입

  • null

  • undefined

  • boolean

  • number

  • string

  • object

  • symbol (ES6 ~)

null과 typeof

typeof null === "null" // false
typeof null === "object" // true

20년간 유지된 버그.

null값 확실히 확인

let isNull = (n) => !n && typeof n === "object";

undefined vs is not defined

동의어로 여겨지기도 하지만 다른 개념. 둘을 구분할수는 없지만 typeof는 변수가 정의되지 않아도 오류를 뿜지 않아 대처는 가능.

undefVal; // error
typeof undefVal // 'undefined'

전역변수 체크방식

if(window.Global) // X

위의 방식은 node 등의 다중 자바스크립트 환경을 고려하면 좋은 방법이 아니다. node만 해도 전역 객체가 window가 아닌 global이기 때문. 아래의 방식이 쓸만하다.

if(typeof Global !== "undefined") // O

배열

typeof [] // object

배열은 객체이기도 하기 때문에 Key / Property 추가 가능 (length는 늘어나지 않음). 하지만 키 프로퍼티를 배열에서 쓰는건 추천하지 않음.

let arr = []
arr["100"] = 1;
a.length // 101

이 사단이 날 수 있기 때문

문자열

문자열은 여러모로 배열과 닮음. length 있고 indexOf, concat, push 등 배열처럼 가능.

하지만 문자열은 불변이라는 차이가 있음.

숫자

1E3 // 1 * 10^3
15E10 // 15 * 10^10

정수 체크

Number.isInteger(1); // true
Number.isInteger(1.1); // false

작은 소수값

0.1 + 0.2 === 0.3 // false

부동 소수점 방식이라 안 맞음. 이런 미세한 두 값 간의 차이를 머신 입실론이라고 함.

Number.EPSILON == Math.pow(2, -52) // 2^-52 -> EPSILON

이걸로 차이를 구할 수 있음.

const isEqualbothDecimal = (a, b) => Math.abs(a - b) < Number.EPSILON;

그래서 정수는 안전의 값이 정해져 있는데, 이 값의 범위 안에서는 안전하게 표현할 수 있는 것.

Number.MIN_SAFE_INTEGER < num < Number.MAX_SAFE_INTEGER

특수한 값

null은 예전에는 값이 있었거나 지금은 빈 값.
undefined는 값이 아직 없거나 undefined로 할당된 상태

void 연산자

어떤 값이든 무효로 만들어 undefined로 만듬.

let a = 2
console.log(void a, a) // undefined, 2

관례적으로 void 0으로 많이 쓰는데, 자주 쓰이진 않지만 어떤 식의 결과값이 없다는 것을 확실히 밝힐 때 요긴하다.

NaN (Not a Number)

글자 그대로 숫자가 아닌 것. 하지만 NaN의 타입은 숫자다.

typeof NaN // "number"

게다가 NaN끼리는 늘 틀림.

NaN === NaN // false

그래서 NaN끼리 비교하는 함수가 있지만 문제적인 함수임.

window.isNaN(2 * 'hi') // true
window.isNaN('hi') // true

그저 숫자인지만 확인하기 때문에, Number.isNaN을 써야한다.

Object.is()

-0 == 0 // true
-0 === 0 // true
0 > -0 // false

0과 -0은 값이 0이 되는 순간 그 직전까지의 이동 방향을 알아야 하기 때문에 중요하지만 단순 비교로는 난해한 결과를 내기 때문에 Object.is 함수를 사용한다.

Object.is(0, -0) // false

Native (내장 함수)

let str = new String("hi")
typeof str // "object" => string 아님

String 함수는 문자열을 생성하지만 문자열 래퍼를 생성하여 원시값 문자와는 다르다. 일례로,

if(false) // false
if(new Boolean(false)) // true

상단의 예는 Boolean으로 객체 래퍼가 생성되었기 때문에 객체로 인식되어 falsy가 아니기 때문에 true.

내장 함수로 래퍼 없이 변환하려면 new 키워드를 빼버리면 된다.

String('hi') // "hi"
Number(1000) // 1000

원시 값

원시 값은 valueOf 함수로 구할 수 있다.

(new String("hi")).valueOf() // "hi"
(new Boolean(false)).valueOf() // false

Array

배열에는 크기를 미리 정하는 기능이 있다.

let a = [];
a.length = 100;
a              // (100) [empty × 100]
new Array(100) // (100) [empty × 100]

그런데 저 빈 슬롯들은 구멍 난 배열 (sparse array) 이라고 하는 문제아들이라는 것. 즉 슬롯 자체가 존재하지 않는다. 그러니 진짜 undefined 값으로 채워진 배열을 생성해야 한다.

Array.apply(null, { length: 100 }) // undefined x 100

강제변환

추상 연산

  • ToString

  • ToNumber

  • ToBoolean

  • ToPrimitive

암시적 변환

장황함, 보일러 플레이트, 불필요한 상세적 구현을 방지하는 장점이 있다.

&&와 ||

이는 다른 언어와는 많이 다르다. 첫번째 피연산자의 불리언 값을 평가한다는 공통점이 있다.

||는 결과가 true면 첫번째 피연산자, false면 두번째 피연산자 값을 반환한다.

웬만하면 삼항연산자로 더 명시적인 식을 구성하는 것이 권장됨.

&& 는 반대로 true면 두번째 피연산자, false면 첫번째 피연산자 값을 반환함.

첫번째 피연산자의 결과가 truthy일때만 두번째 연산자를 선택하는 것, 이를 가드 연산자라고 하는데 첫번째 표현식이 두번째의 가드 역할을 하기 때문.

let isFlag = false;
!isFlag && flag()

위의 코드는 플래그가 없으면 flag함수를 호출한다.

*&&는 ||보다, ||는 삼항연산자보다 우선순위가 높다.

비교

2 == "2" = 2 == Number("2")
"2" == 2 = Number("2") == 2
42 == [42] // true

위의 결과는 [42]가 ToPrimitive 추상 연산을 거친다. 그런 다음 42 == 42 는 true.

비교 순서

a == 1 && a == 2

위의 둘은 동시에 비교되는것이 아니다. 엄밀히 a == 1 가 먼저 평가됨.

이로서 ==는 꽤나 머리아프니, === 를 권장한다.

24 == "24" // X
24 === +"24" // O

즉 둘의 비교는 비교 시 강제변환을 허용하느냐의 문제.

하지만 You Don't Know JS 저자의 주장대로 == 역시 개발자가 조심할 능력이 된다면 사용해도 무리없다는 논리도 납득할 수 있다.

추상적 관계 비교

[42] < ["43"] // true
[42] > ["43"] // false

어느 한쪽이라도 문자열이 아니면 양쪽 모두 ToNumber로 강제변환한다.

["42"] < ["043"] // false

하지만 위처럼 모두 문자열이라면 문자를 단순 어휘 (ASCII ??) 로서 비교함.

부수효과

모든 문은 완료값을 가지며, 하단의 코드 역시 마찬가지다.

let a = b; // undefined
var a;
(a = 24) // 24

equal문은 반환값이 존재한다.

switch문

switch문은 case문에 === 를 사용한다. == 를 사용하고 싶다면 아래의 코드를 참고하면 된다.