You don't know js Part I
#
챕터 1, 값#
typeof와 null- JS는 typeof라는 연산자로 값의 타입명을 알아낼 수 있다. 근데 null은 object로 출력되는 버그가 있다
#
object의 하위타입- function과 array도 object의 하위타입이다. 그래서 얘들도 속성값을 가질 수 있다.
#
undeclared vs undefined- undeclared와 undefined는 절대 같지 않다. undefined는 선언된 변수에 할당할 수 있는 값인 반면,
undeclared는 선언되지 않았다는 상태를 나타내는 표현이지, 할당할 수 있는 값이 아니다.
근데 정작 JS부터 이 두 용어를 대충 섞어 써버린다. 선언되지 않은 경우var is not defined
라고 표현하기 때문이다. - typeof를 사용해서 에러를 발생시키지 않고 변수가 선언되었는지를 체크할 수 있다.
#
챕터 2, 타입#
배열과 유사배열- 배열도 객체라서 문자열을 키값으로 사용할 수 있긴 하다. 다만 이 경우 length값이 증가하지 않는다.
- 유사배열은 배열처럼 정수를 키로 가지고 있을 뿐, Array의 객체는 아니다. Array의 메서드를 사용하지 못한다.
#
문자열- JS의 문자열은 문자배열이 아니다. 그러므로 배열의 메서드를 사용할 수 없다.
- JS의 문자열은 불변값이므로 특정 위치의 문자만 바꿀 수 없다. 변경하고자 한다면, 새 문자열을 만들어야 한다.
#
숫자- JS는 하나의 숫자 타입인 number를 사용한다. 이걸로 정수부터 실수까지 표현한다
- 이진 부동소수점으로 나타낸 소수는 원래 수와 매우 유사할 뿐, 일치하지 않는다.
따라서 JS에서 소수끼리 연산을 하면, 오차가 발생하는데, 이를 머신 엡실론이라고 부른다. - 다만 정수는 원래 수와 일치하게 표현할 수 있다.
JS의 정수 최소, 최대값은 Number.MAX_SAFE_INTEGER, MIN_SAFE_INTEGER을 사용하면 구할 수 있다. - 0으로 나눈 결과는 Infinity이다.
#
NaN- 숫자가 아님을 나타내는 경계값, 숫자에서 발생한 특별한 종류의 에러상황을 나타낸다.
- NaN은 자기 자신과 비교해도 다르다고 나오는 특성이 있다.
- window.isNaN은 문자열도 NaN으로 취급하는 반면, Number.isNaN은 그렇지 않다.
#
객체 & 레퍼런스- 프리미티브 타입은 값이 복사되는 반면, 객체는 레퍼런스 값이 복사된다. 같은 레퍼런스값을 가지니, 같은 객체를 참조하게 된다
- 객체 복사는 두가지 방법이 있다. 얕은 복사와 깊은 복사가 있는데, 얕은 복사는 속성을 그대로 복사하다보니, 객체인 경우 레퍼런스를 복사한다. 반면 깊은 복사는 속성이 가진 객체도 완전히 복사한다. 즉 새로운 객체를 생성한다.
#
Number 객체- Number객체로 프리미티브 타입을 감싸는 경우, 타입은 object인데, 언제 언박싱이 발생할지 모른다.
- 객체이긴 하지만 내부의 프리미티브 값을 변경할 수 없다.
#
챕터 3, 네이티브- 네이티브라고 불리는 여러가지 내장타입이 있다.
- String, Number같은것들이 있다.
#
네이티브는 사실 내장함수이다.- 생성자처럼 사용할 수 있는데, 원시값을 감싼 객체가 반환된다. 그러므로 typeof의 결과도 object이다.
[[Class]]
#
내부 - typeof가 object 인 경우,
[[Class]]
라는 내부 프로퍼티가 추가로 붙는다. - 보통 해당 값과 관련된 내장 네이티브 생성자를 가리키는데, 그렇지 않을때도 있다.
- 단순 원시값은 해당하는 객체 래퍼로 자동 박싱된다.
#
래퍼 박싱- 원시값은 속성 및 메서드가 없는데, JS는 원시값을 알아서 박싱해주다보니, 아래와 같이 사용할 수 있다.
- 필요에 따라 JS엔진이 알아서 암시적으로 박싱하므로, 개발자가 따로 최적화해줄 필요 없다고 한다.
#
래퍼 객체 주의사항- boolean값 같은걸 래퍼로 감싸고, 그걸 가지고 조건문에서 참 거짓 여부를 따진다면,
값이 존재하니 truthy하다고 나올 것이다. 내부 값이 false여도 말이다.
이런 상황을 주의하자. - 값을 꺼낼 땐 valueOf를 사용하자
#
Wrapper객체는 주의해서 사용 할 것- 정말 필요해서 쓰는게 아니라면, 그냥 원시 타입을 사용하는게 낫다.
위에서 언급한 Boolean문제처럼, 굳이 valueOf()를 써야 한다거나 하는 문제를 사전에 피하도록 하자. - Boolean말고도 Array도 특이한데, 인자로 3을 넣으면 빈 슬롯 배열이 만들어진다. 미리 값이 채워진 배열을 원한다면, Array.apply 함수를 사용하자.
#
Object(), Function(), RegExp()- Function의 경우 동적으로 함수를 만들어내야 하는 상황이라도 있는게 아니라면, 쓸 일이 없다.
- 나머지도 마찬가지이다. 그냥 리터럴로 표현하는게 훨씬 나으니, 그렇게 사용하자.
- Object의 경우, 리터럴로 한번에 여러 속성정의가 가능한데, 한번에 하나씩 프로퍼티를 지정할 이유가 없다.
- RegExp의 경우, 읽기 편한건 물론, JS엔진이 실행 전에 정규표현식을 미리 컴파일하고 캐시하기 때문에,
더더욱 RegExp()를 사용할 일이 없다. - 다만, Function과 마찬가지로 동적으로 정규표현식을 만든다면 필요할 수 있다.
#
Date(), Error()- date와 error는 리터럴 형식이 없어서 사용해야한다.
#
네이티브 프로토타입- 내장 네이티브 생성자는 각자 .prototype을 가진다.
- 프로토타입을 통해 객체에 정의된 메서드에 접근가능하다.
#
프로토타입을 디폴트값을 사용하는 경우- 함수의 프로토타입은 빈 함수, 정규표현식, 배열 등 등, 각자의 프로토타입은 각 타입의 디폴트값이다.
- 이 점을 활용하여, 각 타입의 디폴트값으로 활용할 수 있다.
#
챕터 4 강제변환- 변환은 암시적으로도, 명시적으로도 할 수 있다.
#
boolean값- JS의 경우, boolean으로 강제변환 시 모든 가능한 수가 정의되어있다.
- 그래서 아래의 경우 false로 강제변환된다. | 강제변환 시 false가 되는 값들 | | - | | undefined | | null | | false | | 0 | | NaN | | "" |
#
falsy 객체- 객체는 truthy하다. 근데 falsy객체가 따로 있다.
- 레거시 코드때문에 그렇다. 대표적으로 document.all이 있다.
- 웹페이지의 모든 요소를 가져오는 기능을 제공하는데, 비표준이다.
하지만. 여기에 의존하는 레거시 코드가 많다보니 함부로 제거할 수 없었다. - 근데 이걸 가지고 비표준 IE를 감지하는 수단으로도 줄곧 써왔다.
- document.all을 없애버릴 수 도 없는데, 강제변환시 false가 나와야 한다. 반환값이 객체인데도!
- 그래서 document.all은 falsy하게 되었다...
#
명시적 강제변환- Number(), String()등을 사용하여 명시적으로 변환한다. new 붙이지 않는다.
- 연산자를 사용하는 방법도 있다.
- 날짜를 숫자로 변환할 수 있다. 반면 숫자를 날짜로 변환하지는 못한다.
#
틸드- indexOf의 결과, 찾지 못한 경우 -1을 반환하는 점을 이용하여, 틸드연산자와 조합하여 boolean으로 강제변환하기도 한다.
- 틸드를 두번 써서 비트를 두번 뒤집어서 내림을 구현하기도 하는데, 음수일때 올림을 해버리는 결과가 나오니 주의해야 한다.
#
문자열 파싱- 파싱은 좌에서 우로 읽으면서, 숫자가 아닌 값을 만나면 멈추는 반면, 강제 변환은 NaN을 던진다.
#
JS에서의 논리 and, or 연산자- 결과값이 boolean이 아닌 피연산자 중 한쪽의 값이다.
- 대충 아래와 같이 동작한다
a || b
는a ? a : b
a && b
는a ? b : a
- 근데 그냥 삼항연산자 쓰는게 낫지 않은지...?0
#
느슨한 비교와 엄격한 동등 비교- 느슨한 비교는 강제변환을 허용하지만, 엄격한 비교는 허용 하지 않는다.