
JavaScript 코드가 실행되고 있는 컨텍스트(환경)
실행컨텍스트는 실행할 코드에 제공할 환경 정보들을(변수 정보들) 모아놓는다.
함수를 실행할 때마다, 그 함수에 대한 새로운 실행 컨텍스트를 생성하여 자신만의 고유한 컨텍스트에서 실행된다.
어떤 정보냐면..
→ 코드 실행하기 위한 여러가지 정보
→ ex) 어떤 변수가 있는지, 어떤 변수를 hoisting 할지, scope는 어떻고 scope chain은 어떻고, this는 어디에 어떻게 binding 되고 등등..
변수의 선언을 끌어올리는 것을 말한다.왜 y 변수에
Uncaught ReferenceError: y is not defined에러가 아닌,undefined가 찍힐까?
var x = 1;
console.log(x + " " + y); // ✅ '1 undefined'
var y = 2;
var x = 1;
var y; // ✅ Declare y : 호이스팅이 발생하였으므로, undefined가 된다
console.log(x + " " + y); // '1 undefined'
y = 2; // Initialize y
var는 함수 레벨 스코프
let, const는 블록 레벨 스코프
var로 선언한 변수는 선언 전에 사용해도 에러가 나지 않지만,
let, const는 에러가 발생한다.
var는 이미 선언되어있는 이름과 같은 이름으로 변수를 또 선언해도 에러가 나지 않지만,
let, const는 이미 존재하는 변수와 같은 이름의 변수를 또 선언하면 에러가 발생한다.
var, let은 변수 선언시 초기 값을 주지 않아도 되지만,
const는 반드시 초기값을 할당해야 한다.
var, let은 값을 다시 할당할 수 있지만,
const는 한 번 할당한 값은 변경할 수 없다.
(단, 객체 안에 프로퍼티가 변경되는 것까지 막지는 못합니다)
✨ 2. 내용을 보면, 그럼 let과 const는 호이스팅이 되지 않는걸까?
할 수 있지만 사실 var, let, const는 다 호이스팅 된다.
다만 var는 호이스팅 되면서 초기 값이 없으면 자동으로 undefined를 할당하고,
let, const는 자동으로 초기값이 할당되지 않는다.
let, const는 변수가 선언되고 변수에 값이 할당 되기 전 TDZ(Temporal Dead Zone)에 있게 된다.
Q1. TDZ 문제 > 출력될 결과 값은?
let letValue = 'out Scope';
function hello() {
console.log('letValue', letValue);
let letValue = 'inner scope';
};
hello();
✅ 답 : Uncaught ReferenceError: Cannot access 'letValue' before initialization 에러가 발생한다.
letValue변수와 hello 함수를 메모리에 등록한다.GlobalContext의 Lexical Enviroment에 등록)letValue를 'out Scope'로 초기화한다.hello() 함수를 호출한다.hello() 함수를 호출하면 실행 컨텍스트가 만들어지고 letValue를 메모리에 등록helloContext의 Lexical Enviroment에 등록)letValue는 메모리에 초기화 되지 않은 채로 등록되어 있기 때문에 TDZ 상태에 있다function a(){
//로직
}
➡ function 정의만 존재하고 별도의 할당 명령이 없다.
➡ 함수 자체가 호이스팅이 된다.
catName("Chloe"); // "My cat's name is Chloe"
function catName(name) {
console.log("My cat's name is " + name);
}
const a = function(){
//로직
}
➡ function 키워드로 정의한 함수를 변수에 할당하는 것을 말한다.
➡ 변수 const notHoisted; 선언만 호이스팅 되었고, 함수 할당 부분이 없으므로 에러 발생!
console.log(notHoisted) // undefined
notHoisted(); // TypeError: notHoisted is not a function
const notHoisted = function() {
console.log('bar');
};
window, Node.js에서는 global if (true) {
var x = 5;
}
console.log(x); // 5 → if 문 안에 있더라도, 일반 블록이므로 x는 전역변수가 된다.
Q1. Scope Chain
function sum() {
var a = 3;
var b = 5;
function inner() {
var b = 7;
var c = 11;
a = a + b + c; // ✅ a = 3, b = 7, c = 11
console.log(a); // 21
};
console.log(a); // 3
inner();
console.log(a); // 21
};
sum();
console.log(a); // ✅ Uncaught ReferenceError: a is not defined
Q2. Scope Chain
var a = 1;
var outer = function () {
var inner = function () {
console.log(a);
var a = 3; // ✅ 호이스팅으로 undefined 출력됨.
}
inner();
console.log(a); // ✅ a의 값이 outer에 없으면 **바깥으로만** 차례로 검색해 나간다 ➡ 1 출력
}
outer();
console.log(a); // ✅ 1 출력
scope가 끝난 외부 함수의 변수를 참조할 수 있다.
var A = function() {
var a = 1;
var B = function() {
return ++a;
};
return B;
};
var outer = A();
console.log(outer()); // 2
console.log(outer()); // 3
var outer = A() 에서 함수 B자체를 반환A() 실행했으므로 A의 실행컨텍스트는 종료됨outer 변수는 이제 B함수를 바라보고 있음outer() outer를 호출하면, 즉! B를 호출. a값은 계속 증가함this는 실행컨텍스트가 생성될 때 결정된다.
실행컨텍스트는 함수를 호출할 때 생성되므로, this는 함수를 호출할 때 결정된다.
this가 무엇이냐고 한다면 → this가 바라보고 있는 객체인데, 상황에 따라 대상이 달라진다.
windowglobalQ1. 암시적 binding
var name = 'lee';
var user = {
name: 'kim',
getName: function() {
console.log(this.name);
},
age: 50,
child: {
age: 15,
underTwenty: function() {
console.log(this.age);
return this.age < 20
}
}
}
user.getName(); // ✅ this는 user, "kim"
user.child.underTwenty(); // ✅ this는 user.child, "15"
user.parentUnderTwenty = user.child.underTwenty;
user.parentUnderTwenty(); // ✅ this는 user, "50"
Q2. 암시적 binding
var name = 'lee';
var user = {
name: 'kim',
getName: function() {
console.log(this.name); // ✅ "kim"
var inner = function() { // inner 함수에서 this는? -> 전역 !!!
console.log(this.name); // ✅ "lee"
}
inner();
}
}
user.getName(); //✅ this는 user
➡ 함수를 호출할 때, 원하는 대상의 객체를 인자로 넘겨준다.
➡ call 메서드와 완전히 같은 기능이나, 호출할 함수에 인자를 배열로 넘김
➡ call과 비슷하지만, 바로 호출하는 것이 아니라 대상을 묶어놓기(binding)만 하는 것
var user = {
name: 'kim',
getName: function() {
console.log(this.name);
},
age: 50,
child: {
age: 15,
underTwenty: function() {
console.log(this.age); // ✅ user의 age : 50
return this.age < 20
}
}
}
user.child.underTwenty.call(user); //✅ this는 user로 명시적 binding
let user = {
name: 'kim',
underTwenty: function(age) {
return age < 20;
}
}
user.underTwenty(30); // ✅ "메서드"!
const under20 = user.underTwenty;
under20(15); // ✅ 객체 안에 정의된 함수라도, 이것은 메서드가 아닌 "함수"!