원시 자료형은 값(value)을 저장하고 참조 자료형은 주소(reference)를 저장한다.
- 원시 자료형 : number, string, undefined, boolean, bigInt, symbol, (null)
- 참조 자료형 : 배열, 객체
let num1 = 1; let num2 = num1; num1 = 3; console.log(num2); // 1 console.log(num1); // 3
원시 자료형에는 값 자체가 할당되기 때문에 num1에 3을 할당해도 num2의 값은 바뀌지 않는다.
let arr1 = [1, 2, 3]; let arr2 = arr1; arr2[2] = 6; console.log(arr1[2]); // 6
참조 자료형에는 주소 값이 할당되기 때문에
arr1
과arr2
는 같은 주소를 공유하게 된다. 따라서 둘 중 하나의 값이 바뀌면 나머지 하나의 값도 바뀐다.
c언어의
malloc
과 비슷한 개념이다.
스코프(Scope)는 변수의 접근 규칙에 따른 유효성 범위를 뜻한다.
let username = 'kimbob'; if (username) { let message = `Hello, ${username}!`; console.log(message); // Hello kimbob! } console.log(message); // ReferenceError
여기서 중괄호
{ }
로 둘러쌓인 부분을 안쪽 스코프, 바깥을 바깥쪽 스코프라고 한다. 바깥쪽 스코프에서 선언된 변수는 안쪽 스코프에서 사용할 수 있지만, 안쪽 스코프에서 선언된 변수는 바깥에서 사용할 수 없다. 이것이 바로 유효성 범위이다. 자세한 내용은 아래를 살펴보자.
스코프는 중첩이 가능하다 !
스코프에는 두 가지 종류가 있다. 블록 스코프(block scope)는 중괄호
{ }
로 둘러싼 범위이고, 함수 스코프(function scope)는 함수로 둘러싼 범위이다. (❗ 화살표 함수는 블록 스코프에 포함된다.)
스코프 중 가장 바깥쪽의 스코프를 우리는 특별히 전역 스코프(global scope)라고 부른다. 그 외의 스코프는 지역 스코프(local scope)라고 부른다.
지역 변수는 전역 변수보다 더 높은 순위를 갖고있다.
let greeting = 'Hello'; let greets = () => { let greeting = 'Bonjour!'; console.log(greeting); // Bonjour! }
여기서 function 속의 greeting은 지역 변수이다. 따라서
console.log(greeting)
에 출력되는 내용은 지역 변수 greeting의 값인'Bonjour!'
가 된다.
변수를 선언할 수 있는 키워드에는 세 가지가 있다.
var
: 블록 스코프를 무시하고 함수 스코프만 따른다.(화살표 함수 포함)let
: 블록 스코프, 함수 스코프 모두 따른다. 재선언을 방지하기 때문에 블록 단위 예측이 가능하다.const
: 상수(constant) 역할을 한다. 값의 변경이 불가능하다. 값을 재할당 할 경우 TypeError 가 발생한다. 안전한 프로그램을 만들기 위해서는, 변수에 값을 재할당할 핗요가 없다면const
키워드를 사용하여 선언 해주는 것이 좋다.
브라우저에만 존재하는, 브라우저 창을 의미한다. 하지만 전역 영역을 담고 있기도 하다.
var
로 선언한 변수나 함수가 포함된다.var vegetable = 'carrot'; console.log(window.vetgetable); // carrot
때문에 전역 변수를
var
로 선언할 경우 window가 망가질 수도 있다 !
클로저(closure)란, 함수와 함수가 선언된 어휘적 환경(변수 및 함수 선언의 형태)의 조합이다. 클로저 함수는 외부 함수의 변수에 접근이 가능한 내부 함수를 일컫는다.
const adder = function(x) { return function(y) { return x + y; } }
여기서 외부 함수는 리턴 값이 함수 형태이며, 내부 함수는 외부 함수의
x
값에 접근할 수 있다.
리턴하는 함수(내부 함수)의 스코프는 외부 함수에 접근할 수 없다. 이를 '스코프를 이용하여 변수의 접근 범위를 닫는다' 고 이야기한다.
1) 데이터를 보존하는 함수
위에서 이야기 했듯이, 내부 함수는 외부 함수의 변수에 접근이 불가능하다. 때문에 이를 이요하면, 외부 함수의 변수 값을 보존하며 함수를 사용할 수 있다. 위에서 쓴 adder 함수를 예로 들어보자.const add5 = adder(5); add5(7); // 12 add5(18); // 23
외부 함수
adder
의 실행이 끝나더라도, 변수x
의 값은 변하지 않았기 때문에 계속 사용이 가능하다 ! 이는 태그를 만들 때 유용하게 쓰인다.2) 정보의 접근 제한
function makeCounter() { let privCounter = 0; return { increment: function(){ privCounter++; }, decrement: function(){ privCounter--; }, getValue: function(){ return privCounter; } } }
위 함수는 은 클로저의 모듈 패턴이다. 외부 함수는 함수 여러 개를 포함한 '객체'를 반환한다. 이 경우
makeCounter
함수를 바꾸지 않고는 value 값을 조정할 수 없다. 이를 정보의 접근 제한(캡슐화)이라고 이야기한다.3) 모듈화
함수의 재사용을 극대화하여 함수 자체를 독립적인 부품 형태로 분리한다. 데이터와 메소드를 같이 묶어서 다룰 수 있다.
배열을 풀어서 인자로 전달하거나, 배열을 풀어 각각의 요소로 넣을 때 사용한다.
function sum(x, y, z) { return x + y + z; } const numbers = [1, 3, 5]; sum(...numbers); // 9
파라미터를 배열의 형태로 받아서 사용한다. 파라미터 개수가 가변적일 때 유용하다.
function sum(...theArgs) { return theArgs.reduce((previous, current) => { return previous + current; }); } sum(1, 2, 3) // 6 sum(1, 5, 6, 8) // 20
이를 활용하여 배열과 객체를 합치고 복사하는 것이 가능하다. 함수에서 나머지 파라미터를 받아올 때도 유용하게 사용된다.
구조 분해 (Destructing) : Spread 문법으로 값을 해체할 후 개별 값을 변수에 새로 할당 해주는 것을 이야기한다. (분해 후 새 변수에 할당)
const [a, b, ...rest] = [1, 2, 3, 4, 5, 6, 7]; a; // 1 b; // 2 rest; // [3, 4, 5, 6, 7]
얕은 복사
: 참조(주소)값의 복사 ➡ 한 데이터를 공유하는 것
깊은 복사
: 값 자체의 복사 ➡ 독립적인 메모리에 값 자체를 할당하여 생성하는 것ex)
- 원시 자료형 : 깊은 복사
- 참조 자료형 : 얕은 복사
- Object.assign : 깊은 복사(객체 제외)
const obj = { john: 'apple'; charlie: 'banana'; else : {jane: 'peach', tom: 'kiwi', sarah: 'pineapple'} } const copiedObj = Object.assign({}, obj);
위 함수에서
john
과charlie
는 깊은 복사가 되지만,else
는 얕은 복사가 되어obj
와copiedObj
는 같은 주소를 공유하게 된다.
- 구조 분해 병합으로 생선된 객체 : 깊은 복사(객체 제외)
오늘 너무 많은 정보를 한 번에 머리에 쑤셔 넣어서 뇌가 아주 꾸깃 꾸깃 해졌다. 그래도 TIL 작성하면서 다시 좀 예쁘게 펴진 것 같다. 내일 일어나서 한번 더 보면 더 예쁘게 펴지겠지 ! 오늘도 고생했다 나 자신 🧚♀️