오늘은 그동안 관심 있었던 주제들이 많아 수업이 더 흥미로웠다. 특히 기억하고 싶은 내용들을 정리하고자 한다. 수업을 들으며 급하게 필기했기 때문에 아래 내용에 틀린 부분이 있을 수도 있다. 그래도 오늘 배운 내용을 잊지 않기 위해 일단 기록한다. 나중에 모던 자바스크립트 딥다이브 책과 유튜브 영상을 통해 틀린 내용을 수정하고, 내용을 더 확고히 할 것이다.
출처: https://haileychoi15.medium.com/실행-컨텍스트-execution-context-와-호이스팅-hoisting-3f407ad4820e
var
와 let
, const
의 경우 다름var
: 변수는 호이스팅되며 undefined
로 초기화됨let
과 const
: 변수는 호이스팅되지만 초기화되지 않으며, 실제 선언 위치까지 "일시적 사각지대"(TDZ)에 놓임const num = 10;
function printNum() {
const num = 30;
console.log(num);
}
printNum()
num
변수 선언 (TDZ)printNum
함수 선언 (호이스팅)num
변수 초기화 (10
)printNum
함수는 선언만 되어있어 별다른 액션이 없음printNum()
을 실행하면 새로운 실행 컨텍스트가 생성됨printNum
실행컨텍스트 - Record 객체num
변수 선언 (TDZ)
→ 이때 전역 실행컨텍스트에서 const
로 선언한 num
을 여기서 또 선언할 수 있는 이유는 두 num
이 서로 다른 컨텍스트에 있기 때문임. 즉, 각 실행 컨텍스트는 독립적인 스코프를 가지며, 다른 컨텍스트에서 선언된 변수와 충돌하지 않음
num
변수 초기화 (30
)console.log(num)
실행: 30
출력const num = 10;
function printNum() {
console.log(num);
}
printNum();
num
변수 선언 (TDZ)printNum
함수 선언 (호이스팅)num
변수 초기화 (10
)printNum
함수는 선언된 상태로 존재printNum()
호출 시 새로운 실행 컨텍스트가 생성됨printNum
실행컨텍스트 - Record 객체printNum
의 Outer 객체는 전역 실행 컨텍스트를 참조함console.log(num)
실행: 전역 스코프에서 num
을 찾아 10
출력const num = 10;
function floor2() {
const num = 20;
function floor3() {
const num = 30;
console.log(num);
}
floor3();
}
floor2();
console.log(num);
num
기록 초기화는 안함, floor2
함수 온전히 기록해 놔야지num
은 알고 보니 10
이었고 floor2
는 함수인 거 알았으니 업데이트 안 함floor2
함수 호출하고 실행하기 위한 실행 컨텍스트 만듦floor2
실행 컨텍스트 - Record 객체num
변수 선언 (TDZ)floor3
함수 선언 (호이스팅)num
변수 초기화 (20
)floor3
함수는 선언만 되어있어 별다른 액션이 없음floor3()
을 실행하면 새로운 실행 컨텍스트가 생성됨floor3
실행 컨텍스트 - Record 객체num
변수 선언 (TDZ)num
변수 초기화 (30
)console.log(num)
실행: 30
출력floor2
실행 컨텍스트로 돌아감floor2
실행 컨텍스트 완료 후 제거됨console.log(num)
실행: 10
출력실행 컨텍스트가 정상적으로 제거(종료)되지 못하는 현상
function outerFunc() {
let count = 0;
return function innerFunc() {
count++;
console.log(count);
};
}
let counter = outerFunc();
counter(); // 1
counter(); // 2
counter(); // 3
counter = null;
null
값으로 초기화 해줘야함객체를 생성하는 함수
new
키워드를 붙임new
키워드를 붙이면 암묵적으로 this = {}
와 return this
가 생략돼 있다고 생각해도 됨
function User(name, age, gender) {
// this = {}
this.name = name;
this.age = age;
this.gender = gender;
this.introduce = function () {
console.log('이름: ' + this.introducename + ', 나이: ' + this.age + ', 성별: ' + this.gender);
};
// return this;
}
const user1 = new User('John', 30, 'male');
user1.introduce();
const user2 = new User('Ann', 12, 'female');
user2.introduce();
객체의 상속과 재사용을 가능하게 하는 원리
생성자 함수의 prototype 객체를 가리키기(참조) 위한 속성
prototype
, constructor
공통적으로 사용할 수 있는 메서드나 속성값 같은 경우에는 prototype이라는 객체에다 넣어주자
function User(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
User.prototype.introduce = function () {
console.log('이름: ' + this.introducename + ', 나이: ' + this.age + ', 성별: ' + this.gender);
};
const user1 = new User('John', 30, 'male');
user1.introduce();
const user2 = new User('Ann', 12, 'female');
user2.introduce();
console.dir(user1.__proto__ === User.prototype); // true
위 예시에서 프로토타입 객체 내부에 있는
introduce
를user2.introduce();
로 호출할 수 있는 이유는 뭘까?
프로토타입 체인: 인스턴스 내부의 __proto__
속성으로 자신을 생성한 생성자 함수의 프로토타입 객체를 참조하는 현상
프로토타입 체이닝: 프로토타입 체인을 따라서 상위 프로토타입을 타고타고 올라가는 현상
자바스크립트에서 최상위에 Object
라는 생성자 함수가 있음
Object
로부터 파생됨function User(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
User.prototype.introduce = function () {
console.log('이름: ' + this.introducename + ', 나이: ' + this.age + ', 성별: ' + this.gender);
};
console.time();
const userList = [];
for (let i = 0; i < 9000000; i++) {
userList.push(new User('John', 30, 'male'));
}
console.timeEnd();
function User(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
this.introduce = function () {
console.log('이름: ' + this.introducename + ', 나이: ' + this.age + ', 성별: ' + this.gender);
};
}
console.time();
const userList = [];
for (let i = 0; i < 9000000; i++) {
userList.push(new User('John', 30, 'male'));
}
console.timeEnd();
따라서 성능 유지 차원에서 공통 메서드 및 속성들은 프로토 타입에 만들어 놓는다.