
타입을 배우기 전에 원시타입 원시타입 하길래 이게 뭘까 한 적이 있었다. 엄청 옛날부터 써온 건가.. 생각도 했었다. 기본적으로 원시타입은 call stack이라는 저장공간에 저장된다고 한다. 학생 시절 쓰던 개인 사물함을 떠올리면 된다. call stack은 개인 사물함이고 각자의 사물함에 우리는 이름표를 붙였었다. 그 이름표는 변수가 된다. 그리고 그 사물함에 국사책을 넣어놓으면 변수에 할당된 값은 '국사'가 되는 것이다. 하지만 call stack이라는 사물함은 공간이 너무 작아서 책을 하나밖에 넣지 못한다. 그래서 책을 여러 권 넣어 놓을 수 있는 사물함을 만들었는데 그게 heap이라는 저장공간이다. 근데 heap은 값을 직접 저장하는 대신 call stack에 주소를 주고 heap에 데이터를 저장하는 방식이다. 박물관이나 미술관에 들어가기 전에 내 옷과 가방을 공용사물함에 맡기고 번호표 같은걸 받아오는 방식을 생각하면 편할 듯 하다. 미술관의 공용사물함이 heap이고 맡긴 짐이 데이터가 되고 번호표가 주소가 된다. 조금 더 전문적으로 말하자면 '동적으로 변하는 데이터를 다루기 위해 특별한 저장공간을 만들었다.'라고 할 수 있다.
한 가지 헷갈리는 부분이 있었는데
원시타입은 데이터 값을 복사하기 때문에 기존 데이터에 영향을 주지 않는다
참조타입은 주소를 복사하기 때문에 해당 원소를 바꿔주면 기존의 데이터에 영향을 준다
let a = 1;
a = b;
b = 2;
a = ?
이렇게 생각해보니 이해가 쉽게 갔다. 반대로 참조타입은
let arr = [1, 2];
let newArr = arr;
console.log(newArr) // [1, 2]
newArr[0] = 3;
console.log(arr) // [3, 2]
이렇게 된다!
배틀그라운드에서 4배율 스코프를 먹고 싶어서 '한 집만 더..한 집만 더..'하던 기억이 떠오른다. 그냥 배그나 하러갈까?
흠흠..어쨌든 배틀그라운드에서 배율의 쓰임새는 내가 한정된 범위를 더 정확하게 보고 원하는 타겟을 맞추기 위해 사용된다. JS에선 '변수를 정의할 수 있는 유효 범위', '변수의 유효 범위' 이런식으로 정의된다. 결국 제한된 범위를 설정하여 땅따먹기를 한다고 보면 된다.
let greeting = 'Hello';
function greetSomeone() **{
let firstName = 'Jay';
return greeting + ' ' + firstName;
}**
console.log(greetSomeone())
console.log(firstName)
이 코드에서 **로 묶여있는 함수는 새로운 범위 즉 스코프를 만들었다. 이 스코프를 Local Scope라고 한다. 함수 안을 보면 firstName이라는 변수를 찾을 수 있다. 이 변수는 함수 내에서만 정의된다. 그렇기 때문에 firstName은 지역변수(Local Variable)가 된다. 그럼 함수를 벗어난 범위는 어떻게 될까? 바로 Global Scope가 된다. 그리고 Global Scope에 있는 변수는 전역변수(Global Variable)이 된다. 그러므로 greeting이라는 변수는 전역변수이다. Global을 부모 Local을 자식으로 보면 편하다. 그 이유는 마지막에 설명하겠다.
여기서 중요한 사실은 Local Scope안에서 선언된 지역변수는 그 상위 Scope에서 사용 할 수 없다. 사용 할 수 없다는 표현이 정확한진 모르겠지만 위의 코드에서 console.log(firstName)은 Global Scope에서 실행되기 때문에 Local Scope에서 선언된 지역변수 firstName은 ReferenceError가 뜨게 된다.
(솔직히 정확한지 잘 모르겠다..)
그렇다면 console.log(greetSomeone())은 어떻게 될까?
함수 greetSomeone은 Global Scope에 있기 때문에 실행이 되고 greeting + ' ' + firstName을 return하라는 함수이기 때문에 'Hello Jay'가 출력된다. 변수 firstName은 같은 scope내에서 선언되고 return했기 때문에 아무 문제 없이 작동된다!
두 줄로 정리하자면
Local Scope에서는 Global Scope의 변수/함수에 접근 가능하다
(하위 스코프에서 상위 스코프로 접근 가능)
Global Scope에서는 Local Scope의 변수/함수에 접근 불가능하다
(상위 스코프에서 하위 스코프로 접근 불가능)
한 줄로 정리하자면
자식 이기는 부모 없다.
앞에 썼던 코드를 보면
let greeting = 'Hello';
function greetSomeone() **{
let firstName = 'Jay';
return greeting + ' ' + firstName;
}**
**로 묶인 스코프가 Function Scope이다. 말그대로 함수로 묶인 스코프를 의미한다.
이 코드에서 Function Scope와 Block Scope의 범위는 같다. 하지만
let greeting = 'Hello';
function greetSomeone() **{
if (Jay is Handsome) *{
let firstName = 'Jay';
return greeting + ' ' + firstName;
}* else *{
return 'Impossible';
}*
}**
이렇게 함수 안에 퀄리브라켓이 여러개 존재한다면 Function Scope와 Blcok Scope의 범위는 달라지게 된다. Block Scope는 *로 설정된 퀄리브라켓이 시작하고 끝나는 범위를 말한다.
선언 키워드 차이
var: function단위 스코프 (재할당 o, 재선언 o)
let: blcok단위 스코프 (예측하기 쉽다, 변수 정의, 재할당 o, 재선언 x)
const: blcok단위 스코프 (상수 정의, 재할당 x, 재선언 x)
가능한 재선언을 안 하는 것이 버그를 줄이는데 도움이 되기 때문에 let을 사용 하는걸 추천!
window는 부모님의 부모님의 부모님 즉 모두의 조상이다. Global Scope에서 선언된 함수와 var로 선언된 변수는 window 객체와 연결되는데 이 때문에 Global Scope에 너무 많은 변수를 선언하는 것은 바람직하지 않다. (var은 앞서 말했듯이 let을 사용하자)
'use strict': 엄근진 모드
※절대로 선언 키워드 없이 변수를 초기화하지 말자
..why? Local Scope에서 선언 키워드 없이 변수를 선언하면 전역변수 취급
...so? 변수를 예측하기 어렵고 버그 발생 가능성이 높아진다
- 지역스코프/전역스코프 나누기
- 선언된 변수 찾고 전역변수/지역변수인지 명시
- 선언되지 않은 변수가 가르키는 대상을 찾기(안에서 밖으로)
이걸 보자마자 프로게이머 Clozer가 생각났다. 아는 사람이 많지 않을텐데 나도 참 게임을 많이 좋아하는 것 같다. 근데 JS에서 Closure는 정말..싫다..그냥 싫다. 이해하기가 어렵다기보단 알 것 같으면서도 모를 것 같아서 더 약오른다.
JS에서 Closure는 '외부 함수의 변수에 접근 할 수 있는 내부 함수'가 작동 원리이다. 예를 들어 클로저 함수에서는 외부함수, 지역, 전역 범위의 변수에 모두 접근 가능하다. 함수를 실행하기 전에 해당 함수에 변수를 넣고 싶을 때 사용한다는대 솔직히 잘 모르겠다. 계속 Closure를 경험해보고 써보면서 익혀봐야겠다.
...args
let arr = [1, 2, 3]
let newArr = [...arr, 4, 5, ...[6, 7]]
console.log(newArr) // [1, 2, 3, 4, 5, 6, 7]
const arr = [1, 2, 3, 4, 5];
console.log(arr); // [ 1, 2, 3, 4, 5 ]
console.log(...arr); // 1, 2, 3, 4, 5
console.log(1, 2, 3, 4, 5); // 1, 2, 3, 4, 5
function func(...args) {
console.log(args)
}
func(1, 2, 3); // [1, 2, 3]