항해99 React 입문주차 SA
js의 모든것은 아니지만 알면 좋은 것
js는 다음과 같은 8개의 기본 자료형을 지원한다.
자료형이 비교적 적은 부분에서 눈치를 챌 수 있겠지만, js는 loosely typed language고, dynamic typing을 사용한다. 입력된 값에 따라 인터프리터가 타입을 지정해준다.
String으로 된 숫자를 Number로 바꾸기 위해 다음과 같은 방식을 사용한 코드들을 종종 볼 수 있다.
let foo = "12345" * 1
"12345"는 분명 문자열이지만 1을 곱하니까 12345를 String이 아니라 Number로서 다룰 수 있다. 이런 일이 가능한 이유가 바로 js가 동적 타이핑을 사용하기 때문이다.
또한 js에는 "==" 비교 연산자가 2종류 존재한다.
"3" == 3 //true
"3" === 3 //false
정적 타이핑 언어를 먼저 공부한 사람이라면 char(혹은 string) 3과 int 3이 어떻게 같을 수 있냐는 반응을 할지도 모른다. 내가 그랬다. loosely typed된 js는 이 둘을 같다고 여길 수도 있다.
loosely typed는 일단은 빠르게 코드를 작성할 때 편하다. string인지 int인지 알 것 없이 집어넣으면 알아서 판단해서 사용하니까 코딩 테스트 등에서 굳이 타입이 중요하지 않을 경우에는 이러한 특성이 빠른 코드 작성에 아주 도움이 된다.
하지만 정적 타이핑을 경험해 본 적이 있는 사람이라면 이렇게 동적으로 지정해주는 타입에만 의지하는 것이 얼마나 위험한 일인지 알고 있을 것이다. 당장 js에만 국한하더라도 String 타입이 지원하는 메소드와, Number 타입이 지원하는 메소드가 다르고, 각 메소드에 parameter로도 특정 타입을 요구하는 경우가 많다. 이럴 때 타입을 고려하지 않고 작업을 했을 경우 운 좋으면 syntax error, 운 나쁘면 logical error과 끝없는 싸움을 할 수 있다.
느슨한 타입, 동적 타이핑에서 느껴지는 js의 "제가 일단 알아서 할게요" 마인드가 또 다른 부분에서 빛을 발하는데, 바로 undefined다. 대부분의 다른 언어들은 뭐든 없으면 syntax error를 바로 뱉어내곤 빨간 줄로 없는데요? 하고 개발자가 해결할 때까지 그저 바라본다.. 그런데 js는 없으면 "없음" 이라는 값을 알아서 집어넣어준다. 이렇게 선언되지 않은 값에 대해서 일단 출력해주는 값이 undefined이다. undefined는 "무엇이 있는지 없는지도 모르는"상태에 대한 임의의 값에 불과하기 때문에, 개발자가 선언적으로 "이 변수에는 그 무엇도 할당되지 않았음"을 나타내는 말로는 모두에게 익숙한 null을 사용한다.
굉장히 유명한 meme. 간단한 예를 들어보자면
console.log(typeof tissue) // undefined
휴지 변수가 정의조차 되지 않았기 때문에 js는 "뭐라는지 모르겠다" 한다.
let tissue = null
console.log(tissue) // null
tissue = 0
console.log(tissue) // 0
휴지를 정의해주고 "휴지가 없다"는 의미로 직접 null을 할당해주니 "휴지가 없다" 고 한다. 휴지에 0이라는 값을 할당해주니 "휴지는 0만큼 있다" 한다.
졸업 이후로 C언어와 완전히 거리를 둔 지 벌써 2년째.. 전공자라고 말하기 부끄러울 정도로 "참조"라는 이름에 거리감이 생겼다. 이 이름을 다시 보게 된 계기는 항해 기간 중, 알고리즘 주차에서 발생했다.
배열을 변수에 할당했는데, 변수를 바꾸니까 원본 배열이 바뀌는 것이었다.
지금까지 이런 일이 한 번도 없었나.. 싶을 정도로 황당한 개념 미숙지 오류였다.
Object.freeze(obj)
를 사용하면 된다. object가 아닌 경우에는 보통 const, 혹은 스코프를 이용한 getter를 사용하는 것 같다. 참고로 js에는 타입 앞에 static 선언이 불가능하다.
호이스팅은 js의 특성 중 하나로, 선언하는 코드가 어느 줄에 있더라도, 말 그대로 끌어올려서(hoisting) scope 내부의 모든 코드에 앞서 먼저 선언하는 현상을 말한다.
console.log(tissue) // undefined
var tissue = 0
console.log(tissue) // 0
이래도 ReferenceError가 나지 않는다. var변수의 경우 hoisting이 선언 및 undefined 초기화까지 이루어지기 때문에 처음엔 undefined가 출력된다.
참고: 호이스팅 - 용어 사전 | MDN
js의 이런 특성 때문에 선언 전인 변수 등을 사용하려는 시도가 충분히 있을 수 있으나, let, const 그리고 class 와 같은 경우에는 var 변수와 달리 초기화 단계까지는 가지 않기 때문에 ReferenceError가 발생한다. 따라서 hoisting 직후와 초기화 단계 사이에는 변수를 사용하지 않는것이 바람직하다. 어떻게 될 지 알 수 없기 때문이다. 이 구간적 틈을 Temporal Dead Zone, TDZ라 한다. 이 TDZ를 피하기 위해 애초에 선언-사용 순으로 작성하는 것이 바람직하다. 참고 링크에 있는 Lifecycle 그림이 훌륭하기 때문에 꼭 눌러보는 것을 추천한다.
참고: TDZ(Temporal Dead Zone)이란?
변수를 예시로 많이 들었는데, 함수도 거의 동일하다.
function foo() {} // 함수 선언문
let bar = () => {} // 함수 표현식
함수 선언문의 경우 선언, 초기화, 할당이 모두 hoisting 되기 때문에 scope 내라면 선언 전이라도 사용이 가능하다.
execution context에 대해 설명하려면, call stack에 대해 먼저 설명 하는 것이 좋을 것 같다.
js는 단일 스레드 언어이다. 이 말은 즉 일을 처리할 사람이 1명 뿐이라는 것이다. 따라서 주어진 일(task)는 동시다발적이 아니라 순차적으로 수행할 수밖에 없다. call stack은 이 일을 순차적으로 수행하기 위해 존재하는 짧은 일정 그 자체라고 볼 수 있다. 사탕을 만드는 일을 예를 들겠다(실제로 어떻게 작업하는지는 잘 모른다).
여기서
이 5가지 작업이 사탕을 만드는 과정에서 생성되는 실행 컨텍스트(execution context 혹은 stack frame)라고 보면 된다.
은닉화는 원하지 않는 범위에 대해 특정 데이터를 숨기는 개념을 의미한다. 개념적인 부분이기 때문에, 그 방법에도 여러 가지가 있다.
closure를 사용한 은닉화가 가장 대표적이다.
function foo() {
let bar = "bar"
return bar
}
const roo = foo() // "bar"
이렇게 하면 foo 밖에서는 bar 를 건드릴 수 없다.
let b = 1;
function hi () {
const a = 1;
let b = 100;
b++;
console.log(a,b); // 1, 101 두 번째로 실행된다. 여기서 사용된 b는 함수 hi() scope 및 context에서 선언되었기 때문에, 맨 윗줄의 전역 b와는 무관하게 해당 함수 내에서만 적용된다.
}
//console.log(a); // 주석을 풀 경우 가장 먼저 실행되지만, a가 선언되지 않았기 때문에 reference error가 난다.
console.log(b); // 1 가장 먼저 실행된다.
hi();
console.log(b); // 1 마지막으로 실행된다.
깔끔하게 정리하셔서 읽기 편했어요! 잘 읽고 갑니다~😎