JavaScript/DreamCoding

데이터타입, data types, let vs var, hoisting

느리지만 꾸준하게 2021. 8. 1. 18:52

ES6를 사용한다는 것은 JS를 사용한다고 보면된다.

 

let이라는 키워드를 이용해서 name이라는 변수를 선언한다.

선언함과 동시에 jay라는 값을 할당한다. 그러면 콘솔창에 jay라는값이 나타나진다. 다시 name이라는 변수에 hello를 할당하면 콘솔창에 hello로 변경되어져서 나온다.

어플리케이션을 실행하게 되면 어플리케이션마다 쓸 수 있는 메모리가 할당되어진다. 메모리는 텅텅 비어져 있는 박스들인데, 어플리케이션마다 쓸 수 있는 박스의 개수가 제한적이다. let이라는 keyword를 이용해서 name이라는 변수를 정의하게 되면 한가지의 box를 가리킬 수 있는 pointer가 생기게 된다. name이라는 변수가 가리키고 있는 메모리 어딘가에 jay라는 값을 저장할 수 있다. 그리고 나중에 name의 변수가 가리키고 있는 곳에다가 다른값을 넣어서 저장하게 된다.

 

이제 Block scope를 살펴보면 괄호를 이용해서 즉 블록을 이용해서 코드를 작성하면 블럭 밖에서는 더이상 블럭 안에있는 내용들을 볼 수 없게 된다. 즉, 콘솔로그를 이용해서 블록 밖에서 name이라는 변수에 접근하게 되면 아무값도 나오지 않는다. 반대로 블록을 안쓰고 파일안에다가 정의해서 쓰는 것들을 block scope라고 하는데 global한 것들은 어느 곳에서나 접근이 가능하다. 그래서 블럭 밖에서 global이름을 출력 할 수도 있고 블럭안에서도 바깥에 있는 것들이 보이기 때문에 global name이 출력된다.

blockscope

즉 global한 변수들은 어플리케이션이 실행되는 순간부터 끝날 때까지 항상 메모리에 탑재되어 있어서 최소한으로 쓰는것이 좋고 가능하면 class나 함수 if나 for loop 필요한 부분에서만 정의해서 쓰는것이 좋다. 자바스크립트에서 변수를 선언할 수 있는 키워드는 let인데 ES6에서 추가되었다.

 

그전에는 그럼 뭘 썼냐하면 var를 썼다. var는 그냥 좋지않다.

왜냐면 대부분의 프로그래밍 언어에서는 선언하고나서 값을 할당하는게 정상적인데, JS var에서는 조금 이상한 행동을 할 수 있다. 선언하기전에 값을 할당하기 때문이다.

선언하기전에 값을 할당..

심지어 할당하기 전에도 출력을 할 수 있는데 아래와 같이 undefined라고 나온다. 변수는 정의되어 있지만 값은 안들어가있다고 판단한다. 

변수를 선언하기도 전에 값이 안들어있다고 undefined가 출력
var의 한계 undefined출력

그리고 값을 할당한 다음에 출력하면 4로 이렇게 나오는 것을 확인할 수 있다.

값을 할당한 다음 출력하면
4로 출력이 된다.

이것을 let을 이용해서 똑같이 하면 에러가 발생한다.(값을 선언하기도 전에 넣었다고 에러가 나온다.) 이렇게 에러가 나오는게 정상적이다. 결론적으로 var는 값을 선언하기도 전에 쓸 수있어서 비정상적이다.

let에서 값을 넣고 변수를 선언하면 에러
값을 선언하기도 전에 넣었다고 에러

이것을 var hoisting이라고 하는데(move declaration from bottom to top) 즉, 어디에 선언했냐에 상관없이 항상 제일 위로 선언을 끌어올려 주는 것이다. 지금은 변수를 파일 제일 상위에 global scope를 이용해서 선언했기 때문에 파일 제일 위에 선언이 올라가게 된다. 그래서 아래와 같이 콘솔로그를 찍어보게 되면 undefined라고 나오게 된다. 

global scope를 이용해서 선언해서 파일 제일 위에 선언 올라감
그래서 undefined 출력

 

그리고 var를 쓰면 안되는 다른 이유는 block scope가 없기 때문이다.

(has no block scope) 얘네는 block를 철저히 무시하기 때문에 age라는 변수를 블록 안에다가 선언했음에도 불구하고 콘솔을 이용해서 age를 밖에다가 출력하게 되면 4로 정상적으로 나온다. 아무리 깊은곳에 블록을 선언해도 어디에서나 보이는 것이 var이다. 초창기에는 유연성을 위해서 프로그램을 금방 짜도 동작하는 어플리케이션을 만들었지만 어느정도 규모있는 프로젝트를 하다보면 선언하지도 않는 값들이 할당되는 이상한 현상이 발생하는데 바로 var 이거 때문이다. 이렇게 큰 단점이 있어서 var를 썼을 때의 위험부담을 알고 있어서 let이 나온것이다.

var는 block를 철저히 무시한다.
4로정상적으로 나온다.

즉 변수를 선언할 때는 let을 써야 한다! 이러한 최신기능을 쓸때는 compatibility, 호환성을 생각해봐야 하는데 ES6는 edge나 firefox, chrome, Safari, Opera과 같은 main browser에서 작동이 되기때문에 안심하고 써도 된다.(IE는 무시하자)

BABEL을 이용해서 ES6이상으로 개발하는거도 있다.(최종적으로 배포할때는 ES5나 4로 내려서 개발하면 된다.)

 

이번에는 constant에 대해서 보면 컨스턴스는 바로 할당하면 값이 절대 바뀌지 않는 것을 말하는데 변수를 이용하면 메모리 어딘가에 할당된 박스를 가리키고 있어서 포인터를 이용해서 값을 계속해서 바꿔나갈 수 있었다.

컨스턴스는 가리키고 있는 포인터가 잠겨 있기 때문에 값을 선언함과 동시에 할당한 뒤로는 다시는 값을 변경할 수 없는것이 바로 constants이다.

 

값이 계속 변경될 수 있는 것을 Mutable 데이터타입이라하는데 바이러스도 mutation을 통해서 유전자의 서열을 바꿔나가는데 그것처럼 데이터타입도 변경이 가능한 mutable 타입이 let키워드를 사용한 변수이다. 하지만 constants는 변경이 불가능한 Immutable 데이터 타입이라고 한다.

그래서 많은 개발자들이 주로 favor immutable data type always for a few reasons => 왠만하면은 값을 할당한 다음에 다시는 변경되지 않는 그런 데이터 타입을 사용해라라는 것이다.

 

 

좋은 이유중 하나가

보안상(security)의 이유이다. 

한번 작성한 값을 다른 해커들이 코드를 이상한걸 삽입해서 값을 계속 변경해 나가는 것이 가능한데 immutable 데이터타입은 그것을 방지할 수 있다.

 

thread safety도 이유가 되는데 

어플리케이션이 실행이 되면 한 가지의 프로세스가 할당이 되고 그 프로세스 안에서는 다양한 thread가 동시에 돌아가면서 어플리케이션을 좀 더 효율적으로 빠르게 돌아가도록 도와준다. 다양한 thread들이 동시에 변수에 접근해서 값을 변경할 수 있는데, 이 동시에 값을 변경하는 것은 좀 위험하다. 그래서 가능하면 값이 변하지 않는 것을 사용하는 것이 좋다. 그리고 값이 변경될 이유가 없다면 const를 써서 프로그램을 작성하는게 좋은데 이렇게 해야 코드를 변경하거나 다른 개발자가 코드를 바꿀 때도 그 실수를 방지할 수 있다.(reduce human mistakes)

 

정리를 하자면

JS에서는 Mutable type의 let과 Immutable type의 const 이렇게 두가지가 있다.

 

다음으로 볼 것은 Variabl types인데

JS 데이터타입에는 어떤것이 있는지 알아보면 어떤 프로그래밍 언어든 primitive type과 object type 두가지로 나눠져 있는데, primitive type은 더 이상 작은 단위로 나눠질 수 없는 한가지 아이템을 말한다. number과 string, boolean, null, undefied, symbol 이런것들이 있고 이러한 object는 sigle item들을 여러개 묶어서 한 단위로 한 박스로 관리 할 수 있게 하는 것이다.(box container)

 

JS에서는 function도 데이터타입중 하나인데 프로그래밍언어는 function도 다른 데이터타입처럼 변수에 할당이 가능하다.그렇기 때문에 함수의 파라미터 인자로도 전달되고 함수에서 리턴타입으로도 function을 리턴할 수 있는것이 가능하다.(first-class function) 그것을 first-class function이라고 부른다.

 

JS뿐 아니라

C언어 같은 경우는 low level언어라고 불리는데

개발자들이 프로그래밍을 하면서 조금 더 세세하게 메모리를 관리할 수 있기 때문이다. 즉, 이정도 사이즈 메모리를 할당해야지라는 것이 가능하다. 그래서 숫자에 관련된 변수만 보더라도 short, int, long float, double 다양한것들이 존재한다.

short나 int나 long같은 거는 정수를 할당할 때 쓰고 float나 double은 소수점인 아이들 실수들을 할당할 때 쓴다.

이것도 사용자가 얼마나 큰 사이즈의 데이터를 다루냐에 따라서 short를 쓸건지 int를 쓸건지 등등 사전에 생각하면서 변수를 할당해야 한다.

만약에 한반에 들어갈 수 있는 학생 수를 변수를 정의하는데 long을 쓴다는 것은 메모리를 낭비하는 것이다. 한반에 학생수가 그렇게 많이 들어가지 않으니까 short나 int를 쓰는것이 적합하다. 이렇게 미리 생각해서 생각하는 것이 C언어이다.

JAVA에도

숫자에만 관련된 데이터타입이 굉장히 많다. 그래서 작성하기전에 얼마나 큰 양의 데이터를 저장해야 되는지 생각한 다음에 선언해야 한다.

 

다행히도 JavaScript에서는!!!

number이거 하나면 숫자는 끝난다. 얼마나 큰 사이즈를 할당할 건지 생각할 필요가 전혀없다. 항상 number type만 이용해서 숫자를 쓰면 된다. 심지어 JS에서는 number이라는 데이터 타입을 선언하지 않아도 된다.(let a = 12;) JS에서는 Type이 dynamic하게 결정이 되기 때문에 let a, let b 이렇게 어떤 숫자든 할당해주면 끝이다.

 

TypeScript에서도

number 하나면 충분하다. 대신에 let a: number = 12;라고 number라고 타입을 명시해줘야 한다.

 

즉 JS에서는 정수나 소수점의 숫자들 상관없이 number type으로 할당이 되어진다.

값에 상관없이 type은 number로 나타내어진다.

하지만 number에서도 특별한 값이 미리 정해져 있는데 대부분의 프로그래밍 언어에서도 동일하게 적용이 된다.

숫자를 0으로 나누게 되면 무한대의 숫자값이 생기게 되는데 (infinity라고 부름) 1을 0으로 나누면 infinity

-1을 0로 나누면 -infinity

또 Nan(not a number)숫자가 아닌경우가 있는데 string을 숫자로 나누게 되면 NaN값이 출력이 된다. 그래서 이렇게 세가지의 값은 특별한 값이고

이게 중요한 이유는 나중에 DOM요소를 JavaScript이용해서 포지션을 바꾼다던지 다양한 계산을 해야할 때 나누고자 하는 값이 0인지 아닌지 숫잔지 아닌지 확인도 안하고 연산을 바로 해버리면 숫자가 아닌 이러한 Infinity나 NaN을 받을 수 있어서 사용자의 에러가 발생할 수 있다. 그래서 항상 연산할 때 그 값이 valid한 값인지 확인하고 연산을 하는 것이 중요하다. 

특별한 number들
각각의 output값들
positive infinity, negative infinity

 

새롭게 추가된게 있는데 JS에서의 number는 -2**53부터  2**53까지 이 정도의 범위의 숫자만 표현이 가능한데 최근에 bigint라는 타입이 추가되었다. 그래서 숫자의 마지막에 n만 붙이면 bigint로 간주된다. 사실 이건 최근에 추가된거여서 chrome랑 firefox에서만 지원이 된다. 동일한 것을 safari에서 하게되면 인식하지 못한다. 즉 다른브라우저에서는 지원이 안되고 프로그래밍하면서 이렇게 큰 숫자의 범위를 다루는 경우가 별로 없다.

bigint라고 값이 나온다.

 

이제 string을 알아보면

JS에서는 한가지의 글자든 여러개의 글자든 string로 판단한다. 그리고 일반 string과 다른 변수를 +기호를 이용해서 붙일 수도 있다.

string값이라고 나온다.

많이 쓰이는 것이 template literals라고 하는 template string라고 불리는 것인데 ``기호를 이용해서 원하는 string을 적고 $와 {}을 이용하면 변수의 값이 자동적으로 붙여진다. 

만약에 백틱(`)을 사용하지 않고 '을 사용해서 한다면 매우 복잡하다. 일일히 넣어서 +하고를 반복해야해서 번거롭기 때문에 백틱`을 이용한다.

''을 이용해서 string을 합칠 때

 

다음으로 boolean을 살펴보면

참과 거짓, true false인데 여기서 false값은 0 null undefined NaN ' '(비어져 있는 string)이 있다.

true는 어떤값 1이나 string이나 다 true로 간주된다.

3이 1보다 작다 이렇게 나타내면 거짓이니까 false로 나타난다

 

null과 undefined를 살펴보면

보기에는 비슷한거 같지만 조금 다르다. 이렇게 null이라고 할당하는 경우에는 텅텅 비어있는 값이다 아무것도 아니다라고 지정해주는것이라서 null로 값이 할당이 되어져 있는 거고

반대로 undefined는 선언은 되었지만 아무거도 값이 지정되어져 있지 않아서 텅텅비었는지 값이 들어가있는지 정해져 있지않다. let x = undefined;라고 할당해도 되고  let x;라고 아무런 값이 할당되지 않았다고 하는거도 undefined이다.

 

null과 undefined

symbol에서 살펴보면

symbol은 map이나 다른 자료구조에서 고유한 식별자가 필요하거나 동시다발적으로 일어날 수 있는 코드에서 우선순위를 주고싶을 때 고유한 식별자가 필요할 때 쓰여지는 것이다.

간혹 식별자를 string을 이용해서 string은 다른모듈이나 다른파일에서 동일한 string을 썼을 때 동일한 식별자로 간주된다. 하지만 반대로 symbol같은 경우는 동일한 id를 이용해서 symbol을 만들었지만 두가지의 symbol은 다른경우이다. 그래서 동일한지 아닌지 검사하면 false라고 나온다. 그래서 symbol은 동일한 string을 작성했어도 다른 symbol로 만들어져서 주어지는 string에 상관없이 고유한 식별자를 만들 때 사용되어진다.

만약에 string이 같다 동일한 symbol을 만들고 싶다하면 아래와 같이 하면된다. 그러면 주어진 string에 맞는 symbol을 만들어줘라고 이두가지가 똑같다고 나오는걸 확인할 수 있다.(.description을 이용해서 string으로 변환해서 출력을해야한다.)

이제 JS의 하이라이트인 Dynamic typing을 알아보면

(dynamically typed language) C나 JAVA언어는 Static type language라고 한다. 그 말은 변수를 선언할 때 어떤 타입인지 결정해서 타입을 같이 선언했던반면에 JS는 선언할 때 어떤 타입인지 선언하지 않고 runtime 프로그램이 동작할때 해당된 값에 따라서 type이 변경될 수 있다는 것을 말한다.

이러한 다이나믹 타이핑언어는 좋은 아이디어가 있을 때 빠르게 프로토타입을 하고 싶을 때는 유용하나 다수의 엔지니어들, 어느정도 규모가 있는 프로젝트 할 때 이런 다이나믹 타이핑 때문에 불편을 겪을 수 있다고 한다.

 

아래를 보면 hello라는 string을 할당하면 text는 바로 string이라고 된다. 다시 숫자 1을 하게되면 타입이 number이라고 된다.

그리고 만약 text에  string과 숫자 5을 더해버리면 JS 엔진이 문자열에 숫자5를 더하니까 5를 string으로 변환해서 string과 string을 합해주는 것들을 하게된다. 즉 string으로 타입이 변환되면서 75로 변환된다.

반대로 string와 string을 나누면 숫자와 숫자로 나누자고 판단하여 아래와 그림과 같이 나온다.

text라는 이름의 변수를 통해서 스트링의 타입을 예상하고 있는데

charAt(0)을 씀으로써 제일 첫번째에 있는 캐릭터를 받아오겠다고 하게된다.

프로그래밍 언어에서 인덱스는 배열을 쓸 때 항상 0부터 시작하고 0번째니까 첫번째가 출력이 된다. 그래서 아래와 같이 h가 출력이 된다. 

이렇게 string으로 이해하기 때문에 h를 받아오는게 맞는데 중간에 누가 타입을 숫자로 바꿔버리면 에러가 발생하게 된다. 그래서 JS는 runtime에서 type이 정해져 있기 때문에 이거 때문에 에러가 runtime으로 발생하는 경우가 빈번하다.

runtime error

그래서  JS의 dynamic typing error때문에 TypeScript이 나오게 된다. TS는 JS 문법위에 Type이 더 올려진 언어인데 

JS를 충분히 배우고 난다음에 TS로 꼭 넘어가자. 그리고 JS 이용해서 배우면 바로 Browser가 이해할 수 있어서 실시간으로 연동해서 볼 수 있지만 TS는 Browser가 이해할 수 있는 transcompiler(BABEL)을 이용해야 하기 때문에 실시간으로 보기가 어렵다.

 

마지막으로 Object를 간단하게 보면

오브젝트는 일상에서 보는 물건과 물체들을 대표하는 박스형태를 말한다. jay라는 오브젝트를 만들어서 이름은 jay고 나이는 27살이다라고 나타낼 수 있다.

 

jay는 const로 지정되어져 있어서 한 번 할당된 오브젝트는 다시는 다른 오브젝트르 변경이 불가능하다. jay는 const 키워드로 정의되어 있기 때문에 가리키고 있는 메모리의 포인터는 잠겨져 있고 다른 오브젝트로 할당이 불가능하다. jay 오브젝트 안에는 name과 age라는 변수들이 있다.

 

그래서 jay.name / jay.age 이런식으로 하면 각각의 포인터가 가리키고 있는 메모리에 다른값으로 할당이 가능하다.

그러므로 아래와 같이 jay의 나이는 20살이다라고 변경이 가능하다.

object구조

앞으로도 계속해서 이러한 Playground을 티스토리에 만들어서 console.log를 찍어보면서 type이 어떻게 변경되는지 직접 코드를 짜면서 확인해보자!

 

 

 

 

참고: https://www.youtube.com/watch?v=OCCpGh4ujb8&list=PLv2d7VI9OotTVOL4QmPfvJWPJvkmv6h-2&index=3