Hoisting
JS 엔진이 코드를 실행하기 전에 모든 변수선언과 함수선언을 메모리에 미리 등록해 두는 동작.
그래서 선언이 코드 아래에 있어도 마치 코드의 최상단으로 끌어올려진 것처럼 동작.
TDZ(Temporal Dead Zone)
let/const 변수가 선언되기 전까지 해당 변수를 기록해 두는 공간으로 해당영역에 표시된 변수는 아직 선언시점이 되지 않은 변수로 구분한다.
호이스팅 영역 : name1, name2, name3
TDZ 영역 : name2, name3
const, let은 var와 다르게 tdz를 이용해서 마치 호이스팅이 일어나지 않은 것처럼 동작하게 해 준다.
console.log("선언 전 : ", name1);
var name1 = "jiwon";
console.log("선언 후 : ", name1);
// console.log(name2);
//일반변수
let name2 = "sumin";
//일반상수
const name3 = 'jisu';
함수의 선언문은 전체 함수가 메모리에 먼저 등록(호이스팅)되기 때문에 코드 어디서든 호출이 가능함.
hello();
function hello(){
console.log("안녕하세요.")
}
Scope
스코프는 변수와 함수가 접근할 수 있는 유효범위이며, 이 변수/함수가 어디까지 보이고, 어디까지 쓸 수 있는지를 결정
전역스코프 : 코드 어디서든 접근 가능한 영역(전역에서 선언된 변수/함수)
함수스코프 : 함수 내부에서만 접근 가능한 영역(var 키워드로 선언한 값)
블록스코프 : {} 블록 내부에서만 접근이 가능한 영역(let, const 키워드로 선언한 값)
렉시컬스코프 : 선언된 위치 기준으로 스코프를 결정(js는 렉시컬 스코프를 기반으로 함) 전역스코프
var num1 = 20;
function test1(){
console.log(num1);
}
function test2(){
var num1 = 40;
console.log(num1);
}
test1()은 전역 변수 참조 20을, test2()는 함수 내부 변수 참조로 40이 출력하는데,
함수 내부에 값이 있으면 그 값을 사용, 없다면 전역에서 값을 찾아 사용한다.
var num1 = 20;
var num2 = 10;
function test3(){
var num1 = 40;
let num2 = 20;
test4();
console.log("num1 in test3 : " + num1);
}
function test4(){
var num2 = 11;
console.log("num1 in test4 : " + num1);
console.log("num2 in test4 : " + num2);
}
test3();
console.log("전역 num1 : " + num1);
test4()는 test3안에서 호출되었지만, test3()의 num1/num2를 가져오지 않고 자신이 선언된 위치에서의 전역 스코프 변수를 가져온다. -> 렉시컬 스코프
렉시컬 스코프의 반대 의미는 동적스코프이다.
동적스코프는 함수가 실행된 위치기준으로 스코프가 결정된다.
var i = 1000;
for(var i=0; i<10; i++){
console.log(i);
}
console.log("i = " + i);
let j=1000;
for(let j=0;j<10;j++){
console.log(j);
}
console.log("j = " + j);
var는 함수 스코프이기 때문에 for문내에 새로운 i가 생기지않고 전역에 i를 저장하며, for문이 종료되어도 전역 i는 그대로 유지된다.
let, const는 블록 스코프이기때문에 for문내에 새로운 스코프를 만들고 j를 해당 스코프에 저장해 사용한 뒤 for문이 종료되면 제거된다.
별개로 전역에 있는 j는 그대로 전역변수로 사용된다.
따라서 i는 10이 출력되고 j는 1000이 출력된다.
Closure
클로저는 함수와, 그 함수가 선언된 시점의 렉시컬 환경의 조합.
즉, 내부함수의 선언시점에 외부함수의 변수를 함께 저장해서 사용하는 것을 클로저라고 함.
콜백/이벤트 핸들러/모듈패턴에서 핵심적인 역할을 함.
function getCounter(){
//count는 getCounter영역에 선언된 변수
let count = 0;
function increase(){
count++;
return count;
}
return increase; //내부함수를 반환 -> 외부에서도 count에 접근 가능
}
const run = getCounter();
console.log(run());
count는 getCounter 변수이지만, run이 살아있을 경우 count는 increase에서 상태유지된다.
따라서 한번 더 run()을 콘솔창에 실행시키면, 한 번 더 증가된 값인 2가 출력된다.
let run1 = getCounter();
let run2 = getCounter();
let run3 = getCounter();
console.log(run1());
console.log(run1());
console.log(run1());
console.log(run2());
console.log(run2());
console.log(run2());
console.log(run3());
console.log(run3());
console.log(run3());
또한 각 변수에 getCouner를 실행할 경우 새로운 렉시컬 환경이 만들어지며 서로 독립적은 상태값을 가진다.
function createStore(initial = 0){
let value = initial;
return {
get: function(){return value;},
set: function(v){value = v;},
increase: function(){value++; return value;}
};
}
const store = createStore(10);
console.log(store.get());
store.increase();
store.set(100);
console.log(store.get());
initail의 초기값을 0으로 설정(안 할 경우 undefined가 출력됨.)
return을 통해 자바의 getter, setter처럼 가능하다.
즉 외부에서 value를 직접 접근이 불가하며 메서드로만 조작이 가능하다.
예)
버튼을 여러 번 눌러도 처음 한 번만 출력이 되도록 할 수 있다.
function attachOnce(el, msg){
let clicked = false; //지역상태(클로저 생성)
el.addEventListener("click", function(){ //이벤트리스너 등록
if(clicked) return; //클릭한적이 있냐?
clicked = true; //클로저에있는 clicked -> true
console.log(msg); //클릭시 하고자하는 동작을 수행
})
}
ArrayFunction
배열에 값을 추가하려면 push("값");
members.push("신지원");
배열에서 인덱스 기준 n개만큼 잘라내려면 splice(인덱스, n);
members.splice(1, 3);
배열에서 인덱스 기준 마지막 인덱스 -1개를 추출하려면 (원본에 지정 없음) slice(인덱스, 마지막 인덱스 -1);
members.slice(0, 3);
배열이나 객체의 요소를 개별적으로 펼 처서 복사하거나 전달하려면... 배열이나... 객체 사용(spread연산자)
let members2 = [
...members,
"신지원"
]
수정하거나 데이터 추가할 때도 편리하다.
let choi = {
name: "jiwon",
age: 24,
gender: "남"
};
//값을 수정할 때
choi = {
...choi,
gender: "여"
};
console.log(choi);
//데이터를 추가할 때
choi = {
...choi,
address: "경기도 광명시"
}
console.log(choi);
비구조할당
배열이나 객체에서 값을 추출할 때 개별변수에 할당해서 추출하는 문법
const [cho, lee, kim] = members;
console.log(cho, lee, kim);
choi = {
name: "jiwon",
age: 24,
gender: "남"
};
const {name, age} = choi;
console.log(name);
console.log(age);
const {name:userName, age:userAge} = choi;
console.log(userName);
console.log(userAge);
sql의 서브쿼리가 복수인 다중열처럼 가능하다.
JS반복문
1. 기본 반복문
for(let i=0; i<stdList.length; i++){
console.log(stdList[i]);
}
2. for of
배열 값 중심으로 순회한다. 가장 깔끔하며 인덱스가 필요 없을 때 사용.
for(const std of stdList){
console.log(std);
}
3.for in
인덱스 중심으로 순회한다.
for(const i in stdList){
console.log(i + "번째 : ", stdList[i]);
}
객체의 key를 전부 열거하는 법
const std = {name: "최지원", java: 75, db: 80, front: 90};
for(const key in std){
console.log(key);
}
배열의 고차함수
읽기 전용 + 새로운 배열/값을 반환 -> 불변경 유지를 위해
배열.forEach(function(v: 순차적으로 요소하나, i: 인덱스번호, a: 전체배열){}
stdList.forEach(function(v, i, a){
console.log(v, " ", i, " ", a);
});