원시 자료형과 참조 자료형의 차이를 이해하고, 각자 맞는 상황에서 사용할 수 있다.
원시 자료형이 할당될 때에는 변수에 값(value) 자체가 담기고, 참조 자료형이 할당될 때는 보관함의 주소(reference)가 담긴다는 개념을 코드로 설명할 수 있다.
참조 자료형은 기존에 고정된 크기의 보관함이 아니라, 동적으로 크기가 변하는 특별한 보관함을 사용한다는 것을 이해할 수 있다.
string, number, bigint, boolean, undefined, symbol, (null)
let value = 'hokiki'
value = 'nhpunch' // 재할당
const pi = 3.14 // 원시 자료형에 값 할당
const arr = ['hello', 'world'] // 참조 자료형의 주소가 할당
자바스크립참조 자료형의 데이터는 동적으로 변한다
const arr = [1, 2, 3];
arr.push(4, 5, 6);
arr // [1, 2, 3, 4, 5, 6]
참조 자료형을 변수에 할당할 경우, 데이터의 주소가 저장된다
const ages = [21, 25, 27]
ageCopy = ages;
ageCopy === ages // true
const num1 = [1, 2, 3]
const num2 = [1, 2, 3]
num === num2 // false: 같은 데이터가 heap에 저장 되지만 주소가 다르기 때문,,
스코프의 의미와 적용 범위를 이해할 수 있다
스코프의 주요 규칙을 이해할 수 있다중첩 규칙
block scope와 function scope
전역 스코프와 지역 스코프
전역 변수와 지역 변수간의 우선 순위
let, const, var 의 차이
전역 객체(window)의 이해
let message = 'Outer'; // 전역 변수: 내부에서 접근 가능
function getMessage() {
return message; /// 'Outer'
}
function shadowGlobal() {
let message = 'Inner'; // 지역 변수: 바깥에서 접근 못함
return message; // 'Inner'
}
function shadowGlobal2(message) {
return message;
}
shadowGlobal2('Parameter') // 'Parameter'
function shadowParameter(message) {
message = 'Do not use parameters like this!';
return message;
}
shadowParameter('Parameter') // 'Do not use parameters like this!'
var
블록 스코프를 무시함, 함수 스코프만 따름. but 화살표 함수의 블록 스코프는 무시하지 않음
함수 스코프는 함수의 실행부터 종료까지이고, var 선언은 최상단에 선언됨
var 키워드는 재선언해도 에러를 내지 않음, let 키워드는 재선언을 방지
const
값이 변하지 않는 상수를 정의할 때 쓰는 변수 선언 키워드
let 키워드와 동일하게 블록 스코프를 따름
값의 변경을 최소화하여 보다 안전한 프로그램을 만들 수 있음
값을 새롭게 할당할 일이 없다면, const 키워드의 사용이 권장
값을 재할당하는 경우, TypeError!!
let
재할당이 필요한 경우에 한정해 사용하는것이 좋고 기본적으로는 const 사용
let | const | var | |
---|---|---|---|
유효 범위 | 블록, 함수 스코프 | 블록, 함수 스코프 | 함수 스코프 |
값 재할당 | 가능 | 불가능 | 가능 |
재선언 | 불가능 | 불가능 | 가능 |
변수 선언에서 주의할 점
전역 변수는 최소화
편리한 대신, 다른 함수 혹은 로직에 의해 의도되지 않은 변경이 발생할 수 있음(side effect)
선언 없는 변수 할당 금지
선언 없이 변수를 할당하면, 해당 변수는 var로 선언한 전역 변수처럼 취급 -> 'use strict'
window 객체(브라우저only)
var로 선언된 전역 변수 및 전역 함수는 window 객체에 속함
var myName = 'hokiki';
console.log(window.myName); // hokiki
let message = 'Outer'; // 전역 변수: 내부에서 접근 가능
클로저 함수의 정의와 특징에 대해서 이해할 수 있다
클로저가 갖는 스코프 범위를 이해할 수 있다
클로저를 이용해 유용하게 쓰이는 몇 가지 코딩 패턴을 이해할 수 있다
// 리턴하는 함수에 의해 스코프(변수 접근 범위)가 구분됨
// 클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는데에 있음
// 따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요!!
// 외부 함수 adder의 실행이 끝나더라도, 외부 함수 내 변수 x를 사용할 수 있음(장점)
const adder = function (x) { // 변수 x가 선언된 함수는 바깥에 있으니 '외부 함수'
return function (y) { // 변수 y가 선언된 함수는 보다 안쪽에 있으니 '내부 함수'
return x + y
} // 외부 함수는 y에 접근 X
} // 내부 함수는 x에 접근 O
const add5 = adder(5) // 변수 add5 에는 클로저를 통해 리턴한 함수가 담겨 있다
// adder 함수에서 인자로 넘긴 5라는 값을 x 변수에 계속 담은 채로 남아있다
add5(7) // 12
add5(10) // 15
const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
// 클로저는 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 해줌!
const divMaker = tagMaker('div');
divMaker('hi') // '<div>hi</div>'
divMaker('hokiki') // '<div>hokiki</div>'
const anchorMaker = tagMaker('a');
anchorMaker('pizza') // '<a>pizza</a>'
anchorMaker('beer') // '<a>beer</a>'
// 모듈화
// 재활용 가능한 makeCounter 함수
// 여러개의 counter를 만드는 것이 가능
const makeCounter = () => {
let value = 0; // counter1과 counter2의 value는 서로에게 영향을 끼치지 않음.
return {
increase: () => {
value += 1;
},
decrease: () => {
value -= 1;
},
getValue: () => value
}
}
// makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 됨
// 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고,
// 각각의 값을 보존할 수 있음
const counter1 = makeCounter();
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue(); // 1
const counter2 = makeCounter();
counter2.decrease();
counter2.decrease();
counter2.decrease();
counter2.getValue(); // -3
// 이와 같이 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화
// 클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있다. 즉, 클로저는 모듈화에 유리하다!!
Spread/Rest 문법, 구조 분해 할당을 사용할 수 있다.
// array
let parts = ['shoulders', 'knees'];
let lyric = ['head', ...parts, 'and', 'toes'];
console.log(lyric); // ['head', 'shoulders', 'knees', 'and', 'toes']
// object
let obj1 = { ho: 'kiki', won: 100 };
let obj2 = { ho: 'bbang', price: 200 };
let clonedObj = { ...obj1 };
let mergedObj = { ...obj1, ...obj2 };
console.log(clonedObj); // { ho: 'kiki', price: 100 }
console.log(mergedObj); // { ho: 'bbang', won: 100, price: 200 }
//function
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a); // a one
console.log("b", b); // b two
console.log("manyMoreArgs", manyMoreArgs); // manyMoreArgs [ 'three', 'four', 'five', 'six']
}
myFun("one", "two", "three", "four", "five", "six");
console.log(myFun);
구조 분해 (Destructing)
구조 분해 할당은 spread 문법을 이용하여 값을 해제한 후, 개별 값을 변수에 새로 할당하는 과정
배열의 구조 분해
const [a, b, ...rest] = [10, 20, 30, 40, 50];
a // [10]
b // [20]
...rest // [30, 40, 50]
const {a, b, ...rest} = { a: 10, b: 20, c: 30, d: 40}
a // {a: 10}
b // {b: 20}
...rest // {c: 30, d: 40}
객체에서 구조 분해 할당을 사용하는 경우, 선언(const, let, var)과 함께 사용하지 않으면 에러!
선언없이 할당하는 경우, {} 블록 스코프로 인식함
{title, width, height} = {title: "Menu", width: 200, height: 100};
({title, width, height} = {title: "Menu", width: 200, height: 100});
() 로 감싸주어 코드 블록이 아닌 표현식으로 해석하면 에러 해결
function whois({displayName: displayName, fullName: {firstName: name}}) {
console.log(displayName + " is " + name); // hokiki is namhyun
}
let user = {
id: 28,
displayName: "hokiki",
fullName: {
firstName: "namhyun",
lastName: "kim"
}
};
whois(user) // 함수에 객체 전달