this
, function
등의 동작 원리가 포함되어 있으며, 이런 원리들은 자바스크립트의 핵심 원리로 간주된다.undefined
로 초기화된다. (var
기준)console.log(a); // undefined
var a = 10;
function foo() { return "Hello"; }
function outer() {
let a = 1;
function inner() {
console.log(a); // outer의 스코프에 접근 가능
}
inner();
}
outer();
this
바인딩this
키워드의 값을 결정한다.window
또는 global
)undefined
(strict 모드) 또는 전역 객체this
를 결정const obj = {
a: 10,
method() {
console.log(this.a); // obj의 a
}
};
obj.method();
function first() {
console.log("First");
}
function second() {
console.log("Second");
}
first();
second(); // 호출 순서대로 실행: first() => second() 실행 순서가 보장된다.
function makeCounter() {
let count = 0;
return function() {
return ++count; // 클로저를 통해 count 유지
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
function a() {
b();
}
function b() {
console.log("b");
}
a(); // 호출 스택: global → a → b
this
값이미지 출처 : heycoding tistory
let
, const
변수와 함수 선언 뿐 아니라 코드의 스코프와 관련된 정보를 관리한다. 현재 컨텍스트 내의 var
로 선언된 변수와 함수 선언의 정보를 관리한다.
실행 컨텍스트 생성 시, Variable Environment는 var
변수와 함수 선언만 초기화하고, let
과 const
는 Lexical Environment에서 관리된다.
Lexical Environment
의 스냅샷이 아니며, 각자 독립적으로 생성되고 관리된다.this
가 참조하는 객체를 나타낸다.window
또는 global
객체를 참조한다.{ a: 10, b:function(){} }
window
객체(브라우저)나 global
객체(Node.js)가 환경 레코드 역할을 한다.let
, const
를 사용하는 블록 스코프({}
)도 독립적인 렉시컬 환경을 생성한다.{
let a = 10;
const b = 20;
}
function outer() {
let a = 10; // outer 렉시컬 환경의 변수
function inner() {
console.log(a); // 상위 렉시컬 환경(outer)을 참조
}
inner();
}
outer();
outer()
실행 시:
outer
함수의 렉시컬 환경 생성{ a: 10 }
inner()
실행 시:
inner
함수의 렉시컬 환경 생성{}
(함수 내부에서 선언된 변수 없음)outer
함수의 렉시컬 환경ReferenceError
)가 발생한다.let x = 10;
function foo() {
let y = 20;
function bar() {
let z = 30;
console.log(x + y + z); // 10 + 20 + 30
}
bar();
}
foo();
bar
함수의 렉시컬 환경:{ z: 30 }
foo
함수의 렉시컬 환경foo
함수의 렉시컬 환경:{ y: 20 }
{ x: 10 }
function makeCounter() {
let count = 0; // 클로저로 참조됨
return function () {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
let
, const
로 선언된 변수를 블록 스코프로 관리한다.var
키워드와 관련된 변수의 호이스팅과 스코프를 처리한다.var
키워드로 선언된 변수는 환경 레코드의 변수 환경(Variable Environment)에 저장된다.※ 외부 렉시컬 환경 참조 X
- 변수 환경은 외부 렉시컬 환경 참조를 가지지 않으며, 단지 현재 스코프 내의 변수를 관리한다.
변수 환경 (Variable Environment)
변수 환경은 변수의 선언과 값을 관리한다. 하지만, 실행 중에 값이 변경되더라도, 변수 환경 자체는 변경된 값을 추적하지 않는다.
초기화 단계에서 설정되며, 실행 중에 변경된 값은 변수 환경이 아닌, 실행 스택이나 스코프 체인을 통해 처리된다.
변수 환경은 렉시컬 환경과 다르게 외부 렉시컬 환경을 참조하지 않는다. 변수 환경은 현재 함수의 변수들만 관리하며, 외부 함수의 변수나 스코프에는 접근하지 않는다.
렉시컬 환경 (Lexical Environment)
function outer() {
var x = 10; // 'x' 변수 선언
function inner() {
console.log(x); // 'x'를 참조
}
x = 20; // x 값을 변경
inner(); // x 값을 출력
}
outer();
변수 환경 (Variable Environment)
변수 환경은 변수 선언과 초기 값을 관리한다. outer
함수 내에서 x
는 변수 환경에 선언되고, 초기 값은 10
으로 설정된다.
실행 중에 값이 변경되면, 변수 환경은 변경된 값을 추적하지 않는다.
x
의 값이 20
으로 변경되었지만, 변수 환경 자체는 초기값인 10
만을 관리하고 변경된 값을 추적하지 않는다.x
는 변수 환경에 10
으로 설정된 후, 실행 중에 20
으로 변경된다. 변수 환경은 여전히 10
을 관리하지만, x
의 값은 실행 스택에서 20
으로 변경된다.x
값의 변경 사항을 추적하지 않기 때문에, inner
함수가 호출될 때 초기 값 10을 참조할 수 있다.outer()
함수 실행:var x = 10;
이 실행되며, x
는 outer
함수의 변수 환경에 10으로 저장된다.x = 20;
로 값 변경:x
의 값을 20
으로 변경하지만, 변수 환경은 여전히 10을 저장하고 있다.var
로 선언된 변수는 함수의 변수 환경에서 초기값을 관리하고, 변수의 값 변경은 변수 환경에서 추적되지 않기 때문이다.inner()
함수 호출:inner()
함수가 호출된다. 이때 inner()
함수의 렉시컬 환경은 outer()
함수의 변수 환경을 참조한다.inner()
함수는 자신의 변수환경에 x
가 없기 때문에 outer()
함수의 변수 환경을 참조하게 된다.inner()
함수는 x
를 참조할 때, 변수 환경에서 초기값인 10을 참조한다.inner()
가 실행될 때 변수 환경에는 x = 10
만 저장되어 있기 때문이다.inner()
함수에서 x
는 여전히 10
을 출력한다.inner()
함수에서 x
를 참조할 수 있는 이유는 렉시컬 환경 덕분인데, 렉시컬 환경이 상위 스코프의 변수를 참조할 수 있기 때문에, inner()
함수는 outer()
함수의 변수 환경을 참조하여 x = 10
을 출력하는 것이다.렉시컬 환경 (Lexical Environment)
inner
함수는 렉시컬 환경을 생성할 때, 상위 렉시컬 환경으로 outer
함수의 스코프를 참조한다.inner
함수는 렉시컬 환경을 통해 outer
함수의 x
를 참조할 수 있다.inner
함수는 렉시컬 환경을 통해 상위 함수인 outer
의 스코프 체인을 참조하여 x
를 찾을 수 있다.inner
함수는 렉시컬 환경을 통해 outer
함수의 변수 환경에 접근하고, x
의 변경된 값 20을 참조합니다. 즉, x
는 outer
에서 실행 중에 변경된 값인 20을 출력한다.inner
함수는 렉시컬 환경을 통해 outer
의 변수 환경에 접근하여 x
를 참조한다.inner
함수가 실행될 때 x
는 변경된 값인 20
으로 출력된다. inner
는 outer
의 스코프 체인을 참조하여 변경된 값인 20
을 출력한다.outer()
함수 실행:outer()
함수가 실행될 때, 실행 컨텍스트가 생성된다. 이 컨텍스트에는 변수 환경과 렉시컬 환경이 포함되어 있다.x = 10
이 저장되어 있다.inner()
함수는 outer()
함수의 렉시컬 환경 내에 존재한다.inner()
함수는 렉시컬 환경에서 outer()
함수의 변수 환경을 참조할 수 있다.x
값 변경:x
의 값이 10
에서 20
으로 변경된다. 변수 환경은 x
의 초기값만 저장하고, 값이 변경되면 해당 변경을 추적하지 않는다.x
의 값은 실행 스택에서 변경된 20
이 저장됩니다.inner()
함수 실행:inner()
함수가 실행될 때, inner()
의 실행 컨텍스트가 생성된다.inner()
함수가 outer()
함수의 렉시컬 환경을 참조하게 된다.inner()
는 outer()
함수의 변수 환경을 참조할 수 있다.outer()
함수)의 변수 환경을 참조하고, 이때 변경된 x
값인 20을 출력하게 된다.20
을 콜 스택에서 보고 찾는데, 변수 환경은 콜 스택을 확인하지 않고, 변수 환경에서 최초 초기화된 값만 기억한다는 것이다.console.log(x)
에서 x
는 변경된 값인 20
을 출력한.x
의 변경된 값 20
을 추적하지 않지만, 렉시컬 환경에서는 실행 시 값이 변경된 x
를 참조하여 20을 출력할 수 있다.this
바인딩은 this
식별자 키워드와 this
가 가리켜야 하는 객체를 연결하는 것을 의미한다.this
는 자바스크립트에서 함수가 호출될 때 그 함수가 속한 객체를 참조하는 키워드이다.. this
의 값은 함수가 호출되는 방식에 따라 달라질 수 있다.this
값이 어떻게 바인딩되는지 살펴보자.window
, Node.js에서는 global
console.log(this); // 브라우저에서는 window 객체를 가리킴
function highOrderFunction(callback) {
callback(); // 일반 함수 호출로 실행됨
}
function callbackFunction() {
console.log(this); // 전역 스코프에서 호출된 일반 함수
}
highOrderFunction(callbackFunction);
// 출력: window (브라우저 환경)
function highOrderFunction(callback) {
callback();
}
const obj = {
name: "example",
method: function () {
highOrderFunction(function () {
console.log(this); // 일반 함수 호출
});
}
};
obj.method();
// 출력: window (브라우저 환경)
use strict
사용 여부에 따라 this
가 다르게 출력될 수 있다.use strict
사용 : undefined
use strict
미사용 : window
(브라우저)function hello(){
console.log(this); // window/global
}
hello();
this
는 객체 자체를 참조한다.this
를 호출하면 해당 메서드를 포함하는 객체가 this
이다.window
/global
)가 this
이다. const obj = {
name: 'John',
greet: function() {
console.log(this.name);
}
};
obj.greet(); // 'John' 출력, this는 obj를 참조
this
는 새로 생성되는 객체의 인스턴스이다.function Person(name) {
this.name = name;
}
const person1 = new Person('Alice');
console.log(person1.name); // 'Alice'
this
가 자신의 문맥을 따르지 않는다. 대신, 외부 스코프의 this
를 상속받는다.const obj = {
name: 'John',
greet: () => {
console.log(this.name);
}
};
obj.greet(); // undefined, 화살표 함수는 외부 문맥의 this(전역 객체)를 참조
call, apply, bind
메소드를 사용하여, this
를 명시적으로 설정할 수 있다.const obj = { name: 'Alice' };
function greet() {
console.log(this.name);
}
greet.call(obj); // 'Alice', call을 사용하여 this를 obj로 설정
※ call(thisArg, arg1, arg2, ...)
this
객체를 1번째 인수로, 함수 내에서 필요로 하는 파라미터는 2번째 인수로 전달하면 된다.const obj = {name: 'tom'};
const say = function(city){
console.log(`hello ${this.name}, this is ${city}`);
};
say.call(obj, 'seoul'); // hello tom, this is seoul
// ------------------
function foo(a, b, c) {
console.log(a + b + c);
console.log(this);
};
foo.call(obj, 1, 2, 3); // 6, { name: 'tom' }
※ apply(thisArg, [arg1, arg2, ...]
this
객체를 1번째 인수로, 함수 내에서 필요로 하는 파라미터는 배열 형태로 인수를 전달할 수 있도록 하면 된다.const obj = {name: 'tom', age: 21};
const say = function(city, dongNm){
console.log(`hello ${this.name}, ${this.age}, this is ${city} ${dongNm}`);
};
say.apply(obj, ['seoul', '법정동']) // hello tom, 21, this is seoul 법정동
// -----------------------------
function foo(a, b, c) {
console.log(a + b + c);
console.log(this);
};
foo.apply(obj, [1, 2, 3]); // 6, { name: 'tom' }
※ const boundFunc = bind(thisArg, arg1, arg2, ...);
bind
함수는 새로운 함수를 생성하고, 해당 함수가 호출될 때 항상 특정한 this
값을 가질 수 있도록 한다.bind
함수로 별도 함수를 생성할 때 전달한 인수를 this
로 가지며, 이 함수를 가지고 있다가 나중에 실행할 수 있다는 것이 큰 특징이다. const obj = {name: 'tom'};
const say = function(city){
console.log(`hello ${this.name}, this is ${city}`);
};
const boundSay = say.bind(obj);
boundSay("busan"); // hello tom, this is busan
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
전역 실행 컨텍스트
전역 영역에 존재하는 코드를 관리하는 실행 컨텍스트로, 모든 스크립트 코드는 전역 실행 컨텍스트 안에서 실행된다.
자바스크립트 코드가 실행될 때 가장 처음 생성되고, 프로그램 전체를 감싸는 기본 환경을 관리하는 컨텍스트이다. (코드 실행을 위한 무대라고 생각)
아래는 전역 실행 컨텍스트의 역할이다.
코드의 시작점 : 자바스크립트 엔진이 코드를 실행하기 위해 가장 먼저 생성하는 컨텍스트
전역 변수와 함수 관리
window
객체, Node.js에서는 global
객체가 자바스크립트 실행 환경에서 가장 최상위 객체가 된다.window
, global
객체를 이용해 전역 코드의 변수와 함수를 저장하고 실행한다.프로그램 전체를 실행 : 함수 호출 전까지 실행되는 모든 전역 코드가 이 컨텍스트에서 처리된다.
항상 하나만 존재 : 프로그램이 실행되는 동안 단 하나의 전역 실행 컨텍스트만 존재하며, 실행되는 동안 절대 사라지지 않는다.
함수 실행 컨텍스트
콜 스택에서 전역 컨텍스트와 관련된 코드들을 자바스크립트 엔진이 순차적으로 실행시키다가 foo()
함수가 호출되면 자바스크립트 엔진은 foo()
함수에 대한 환경 정보를 수집해서 foo()
의 함수 실행 컨텍스트를 생성한 후 콜 스택에 담는다.
foo()
의 함수 실행 컨텍스트가 놓였으므로 전역 컨텍스트와 관련된 코드보다 foo()
의 함수 실행 컨텍스트와 관련된 코드가 우선순위가 더 높아지면서, foo()
함수 내부의 코드들이 순차로 실행되기 시작한다.foo()
함수 실행 도중, bar()
함수가 실행되면, bar()
함수에 대한 함수 실행 컨텍스트가 콜 스택 가장 위에 담기게 된다.
bar()
함수에 대한 함수 실행 컨텍스트가 콜 스택 가장 위에 쌓이게 되면, 콜 스택에서 bar()
함수 실행 컨텍스트가 제거되고, 제어권이 다시 이전 컨텍스트인 foo()
함수 실행 컨텍스트로 전환된다.
코드가 모두 진행되고 bar()
함수의 실행이 종료되면 bar()
함수 실행 컨텍스트가 콜 스택에서 제거되고, 다시 foo()
함수 실행 컨텍스트가 콜 스택의 맨 위에 존재하게 되므로, 코드 실행이 중단되었던 부분부터 이어서 실행하게 된다.
window
객체를 이용해 전역 코드의 변수와 함수를 저장하고 실행하는 방법 예시// 브라우저 환경
var a = 5;
function hello() {
console.log("Hello!");
}
console.log(window.a); // 5
window.hello(); // Hello!
a
와 hello
는 전역 실행 컨텍스트에 의해 window
객체의 속성으로 관리된다.window.a
와 window.hello()
로 접근이 가능하다.