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가 바라보고 있는 객체인데, 상황에 따라 대상이 달라진다.
window
global
Q1. 암시적 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); // ✅ 객체 안에 정의된 함수라도, 이것은 메서드가 아닌 "함수"!