들어가며
많은 기업들이 타입스크립트와 nest.js를 활용해서 서버 개발을 하곤 합니다. 취업을 하려면, 타입스크립트와 nest.js를 공부해서 실무를 익히는 것이 중요할 것입니다. 하지만 아직 자바스크립트의 기초도 없는 상태에서 타입스크립트와 nest.js를 공부하는 것이 맞을까 하는 생각이 들었습니다. 빠르게 기술변화를 적응하고, 러닝 커브를 줄이기 위해 빠르게 공부해야 하는 것도 맞겠지만, 그전에 언어의 기반이 되는 자바스크립트부터 제대로 알아야 하지 않을까 하는 생각이 들었습니다. 이 기회에 자바스크립트의 기본에 대해 정리해보고자 합니다.
VariableEnvironment
실행 컨텍스트 객체에는 다음과 같은 정보가 담깁니다.
VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. 선언
시점의 LexicalEnvironment의 스냅샷으로, 변경 사항은 반영되지 않음
LexicalEnvironment : 처음에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 반영됨.
ThisBinding : this 식별자가 바라봐야 할 대상 객체
VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다릅니다. 실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용하게 됩니다.
VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outer-EnvironmentReference로 구성돼 있습니다. 초기화 과정 중에는 사실상 완전히 동일하고 이후 코드 진행에 따라 서로 달라지게 될 것이므로 자세한 내용은 LexicalEnvironment를 통해 함께 살펴보겠습니다.
LexicalEnvironment
위에서 VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outer-EnvironmentReference로 구성돼 있다고 했습니다. 그럼 내부의 environmentRecord의 특징에 대해서 먼저 살펴보겠습니다.
1. environmentRecord와 호이스팅
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됩니다. 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등이 식별자에 해당합니다. 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집합니다.
전역 실행 컨테스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체,
즉 전역 객체를 활용합니다. 전역 객체에는 브라우저의 window, Node.js의 global 객체 등이 있습니다.
이들은 자바스크립트 내장 객체가 아닌 호스트 객체로 분류됩니다.
변수 정보를 수집하는 과정을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태입니다. 하지만 코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 컨텍스트의 코드 변수명들을 모두 알게 됩니다.
그렇다면 엔진의 실제 동작 방식 대신에 '자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다'라고 생각하더라도 코드를 해석하는 데는 문제 될 것이 전혀 없을 것입니다. 여기서 호이스팅이라는 개념이 등장합니다. 호이스팅이란 변수 정보를 수집하는 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념입니다. 자바스크립트 엔진이 실제로 끌어올리지는 않지만 편의상 끌어올린 것으로 간주하는 것입니다. 그렇다면, 호이스팅은 어떻게 이루어질까요? 이에 대해 알아보겠습니다.
1-1. 호이스팅 규칙
environmentRecord에는 매개변수의 이름, 함수 선언, 변수명 등이 담긴다고 했습니다. 몇 가지 예제를 통해 살펴보겠습니다.
function a(x){
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
우선 호이스팅을 생각하지 않는다면, 어떻게 값들이 출력될까요? 가정해본다면, 함수 호출 시 전달한 1이 출력되고, 두 번째엔 선언된 변수 x에 할당한 값이 없으므로 undefined가 출력되고, 마지막엔 2가 출력될 것 같습니다. 하지만 실제로 출력된 값은 이와 다릅니다.
그렇다면, 변수 정보를 수집하는 과정, 즉 호이스팅을 처리해 보겠습니다. environmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있고, 각 식별자에 어떤 값이 할당될 것인지는 관심이 없습니다. 따라서 변수를 호이스팅할 때 변수명만 끌어올리고 할당 과정은 원래 자리에 그대로 남겨둡니다. 매개변수의 경우도 마찬가지입니다. environmentRecord의 관심사에 맞춰 수집 대상을 순서대로 끌어올리고 나면 다음과 같은 형태로 바뀝니다.
function a(x){
var x;
var x;
var x;
x = 1;
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
a(1);
이제 호이스팅이 끝났으니 실제 코드를 실행할 차례입니다. 코드는 아래와 같이 실행됩니다.
- 2번째 줄 : 변수 x를 선언합니다. 이때 메모리에서는 저장할 공간을 미리 확보하고, 확보한 공간의 주솟값을 변수 x에 연결해둡니다.
- 3, 4번째 줄 : 다시 변수 x를 선언합니다. 이미 선언된 변수 x가 있으므로 무시합니다.
- 6번째 줄 : x에 1을 할당하라고 합니다. 우선 숫자 1을 별도의 메모리에 담고, x와 연결된 메모리 공간에 숫자 1을 가리키는 주솟값을 입력합니다.
- 7, 8번째 줄 : 각 x를 출력하라고 합니다. 모두 1이 출력됩니다.
- 9번째 줄 : x에 2를 할당하라고 합니다. 숫자 2를 별도의 메모리에 담고, 그 주솟값을 든 채로 x와 연결된 메모리 공간으로 갑니다. 여기에는 숫자 1을 가리키는 주솟값이 들어있었는데, 이걸 2의 주솟값으로 대치합니다. 이제 변수 x는 숫자 2를 가리키게 됩니다.
- 10번째 줄 : x를 출력하라고 하니 2차 출력됩니다. 이제 함수 내부의 모든 코드가 실행됐으므로 실행 컨텍스트가 콜 스택에서 제거됩니다.
호이스팅을 생각하지 않았다면, 처음에 1, undefined, 2로 출력되리라 예상했는데, 실제로는 1, 1, 2라는 결과가 나왔습니다. undefined가 아닌 1이 출력된다는 건 호이스팅 개념을 정확히 이해하지 못하면 예측하기 어려운 결과입니다. 그럼 함수 선언을 추가한 예제를 하나 더 살펴보겠습니다.
function a() {
console.log(b);
var b = 'bbb';
console.log(b);
function b() {}
console.log(b);
}
a();
마찬가지로 출력 결과를 미리 예상해 봅시다. b의 값이 없으니 에러가 나거나 undefined가 나올 것 같습니다. 두 번째 출력은 bbb, 세 번째 출력은 b 함수가 출력될 것 같습니다. 어떻게 출력될지 알아보겠습니다.
function a() {
var b;
function b() {}
console.log(b);
b = 'bbb';
console.log(b);
console.log(b);
}
a();
a 함수를 실행하는 순간 a 함수의 실행 컨텍스트가 생성됩니다. 이때 변수명과 함수 선언의 정보를 위로 끌어올립니다. 변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면 함수 선언은 함수 전체를 끌어올립니다. 순서대로 끌어올리고 나면 위와 같은 형태로 변환됩니다.
해석의 편의를 위해 한 가지만 더 바꿔보겠습니다. 호이스팅이 끝난 상태에서의 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것처럼 여길 수 있습니다. 그럼 아래와 같이 변경할 수 있습니다.
function a() {
var b;
var b = function b() {}
console.log(b);
b = 'bbb';
console.log(b);
console.log(b);
}
a();
그럼 실행 컨텍스트 내부의 코드를 차례대로 실행할 차례입니다.
- 2번째 줄 : 변수 b를 선언합니다. 이때 메모리에서는 저장할 공간을 미리 확보하고, 확보한 공간의 주솟값을 변수 b에 연결해둡니다.
- 3번째 줄 : 다시 변수 b를 선언하고, 함수 b를 선언된 변수 b에 할당하라고 하네요. 이미 선언된 변수 b가 있으므로 선언 과정은 무시합니다. 함수는 별도의 메모리에 담길 것이고, 그 함수가 저장된 주솟값을 b와 연결된 공간에 저장하겠죠. 이제 변수 b는 함수를 가리키게 됩니다.
- 4번째 줄 : 변수 b에 할당된 함수 b를 출력합니다.
- 6번째 줄 : 변수 b에 'bbb'를 할당하라고 합니다. b와 연결된 메모리 공간에는 함수가 저장된 주솟값이 담겨있었는데 이걸 문자 'bbb'가 담긴 주솟값으로 덮어씁니다. 이제 변수 b는 문자열 'bbb'를 가리키게 됩니다.
- 7, 8번째 줄 : 모두 'bbb'가 출력되고, 이제 함수 내부의 모든 코드가 실행됐으므로 실행 컨텍스트가 콜 스택에서 제거됩니다.
호이스팅을 고려하지 않은 상태에서 예상하기로는 에러 또는 undefined, 'bbb', b 함수가 순서대로 출력되리라 생각했지만, 실제로는 b 함수, 'bbb', 'bbb'라는 전혀 다른 결과가 나왔습니다. 이를 통해 가상의 개념인 호이스팅을 제대로 익힌다면, 자바스크립트의 구동방식에 대해 조금 더 잘 이해할 수 있을 것입니다.
1-2. 함수 선언문과 함수 표현식
호이스팅을 고려할 때, 함수 선언문에 대해서 호이스팅이 일어난다고 앞에서 배웠습니다. 그렇다면, 함수 표현식에서는 어떻게 호이스팅이 일어나는지에 대해 알아보겠습니다. 함수에는 두 가지 사용방법이 있습니다. 바로 함수 선언문과 함수 표현식입니다. 둘 모두 함수를 새롭게 정의할 때 쓰는 방식인데, 그중 함수 선언문은 function 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미하고, 반대로 함수 표현식은 정의한 function을 별도의 변수에 할당하는 것을 말합니다. 함수 선언문의 경우 반드시 함수명이 정의돼 있어야 하는 반면, 함수 표현식은 없어도 됩니다. 함수명을 정의한 함수 표현식을 '기명 함수 표현식', 정의하지 않은 것을 '익명 함수 표현식'이라고 부르기도 하는데, 일반적으로 함수 표현식은 익명 함수 표현식을 말합니다.
function a () { }
a(); // 실행 OK.
var b = function() { }
b(); // 실행 OK.
var c = function d() { }
c(); // 실행 OK.
d(); // 에러!
이때 기명 함수 표현식은 주의할 점이 있습니다. 이는 바로 외부에서는 함수명으로 함수를 호출할 수 없다는 점입니다. 함수명은 오직 함수 내부에서만 접근할 수 있습니다.
그럼 예제를 통해 함수 선언문과 함수 표현식의 실질적인 차이를 살펴봅시다.
console.log(sum(1, 2));
console.log(multiply(3, 4));
function sum(a, b) {
return a+b;
}
var multiply = function(a, b) {
return a+b;
}
함수 선언문과 함수 표현식에서 호이스팅된다면, 어떻게 될까요? 호이스팅은 다음과 같이 됩니다.
function sum(a, b) {
return a+b;
}
var multiply;
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = function(a, b) {
return a+b;
}
함수 선언문은 전체를 호이스팅한 반면 함수 표현식은 변수 선언부만 호이스팅했습니다. 여기서 함수 선언문과 함수 표현식의 극적인 차이가 발생합니다. 그렇다면 내부 코드를 차례대로 실행해보겠습니다.
- 1번째 줄 : 메모리 공간을 확보하고 확보된 공간의 주솟값을 변수 sum에 연결합니다.
- 4번째 줄 : 또 다른 메모리 공간을 확보하고, 그 공간의 주솟값을 변수 multiply에 연결합니다.
- 1번째 줄(다시) : sum 함수를 또 다른 메모리 공간에 저장하고, 그 주솟값을 앞서 선언한 변수 sum의 공간에 할당합니다.
- 5번째 줄 : sum을 실행합니다. 정상적으로 실행되어 3이 나올 것입니다.
- 6번째 줄 : 현재 multiply에는 값이 할당돼 있지 않습니다. 비어있는 대상을 함수로 여겨 실행하라고 명령한 것입니다. 따라서 'multiply is not a function'이라는 에러 메시지가 출력됩니다. 뒤의 8번째 줄은 6번째 줄의 에러로 인해 실행되지 않은 채 런타임이 종료됩니다.
sum 함수는 선언 전에 호출해도 아무 문제없이 실행됩니다. 지금까지 호이스팅에 대해 살펴봤습니다. 다음으로는 스코프, 스코프 체인에 대해 살펴보겠습니다.
2. 스코프, 스코프 체인, outerEnvironmentReference
스코프란 식별자에 대한 유효범위입니다. 어떤 경계 A의 외부에서 선언한 변수는 A의 외부뿐 아니라 A의 내부에서도 접근이 가능하지만, A의 내부에서 선언한 변수는 오직 A의 내부에서만 접근할 수 있습니다. 자바스크립트는 ES5까지만 하더라도 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성됐습니다. 하지만 ES6에서는 블록에 의해서도 스코프가 생성되게 수정됐습니다. 다만 블록은 var로 선언한 변수에 대해서는 작용하지 않고 let과 const, class, strict mode에서의 함수 선언 등에 대해서만 범위로서의 역할을 수행합니다. ES6에서는 둘을 구분하기 위해 함수 스코프, 블록 스코프라는 용어를 사용합니다.
이러한 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것을 스코프 체인이라고 합니다. 그리고 이를 가능케 하는 것이 바로 LexicalEnvironment의 두 번째 수집 자료인 outerEnvironmentReference입니다.
2-1. 스코프 체인
outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조합니다. 그럼 outerEnvironmentReference가 어떻게 작동되는지에 대해 예를 통해 살펴보겠습니다.
var a = function() {
var b = function() {
var c = function() {}
c();
};
b();
};
a();
만약 a, b, c 함수가 있다고 가정해보겠습니다. a 함수 내부에 b 함수를 선언하고 다시 b 함수 내부에 c 함수를 선언한 경우, 함수 c의 outerEnvironmentReference는 함수 b의 LexicalEnvironment를 참조합니다. 함수 b의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 b가 선언되던 때인 a의 LexicalEnvironment를 참조합니다. 이처럼 outerEnvironmentReference는 연결리스트 형태를 띱니다. '선언 시점의 LexicalEnvironment'를 계속 찾아 올라가면 마지막엔 전역 컨텍스트의 LexicalEnvironment가 있을 것입니다. 또한 각 outerEnvironmentReference는 오직 자신이 선언된 시점의 LexicalEnvironment만 참조하고 있으므로 가장 가까운 요소부터 차례대로만 접근할 수 있고 다른 순서로 접근하는 것은 불가능할 것입니다. 이런 구조적 특성 덕분에 여러 스코프에서 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하게 됩니다. 이런 스코프 체인을 좀 더 구체적으로 알아보겠습니다.
var a = 1;
var outer = function() {
var inner = function() {
console.log(a);
var a = 3;
};
inner();
console.log(a);
};
outer();
console.log(a);
- 시작 : 전역 컨텍스트가 활성화됩니다. 전역 컨텍스트의 environmentRecord에 {a , outer} 식별자를 저장합니다. 전역 컨텍스트는 선언 시점이 없으므로 전역 컨텍스트의 outerEnvironmentReference에는 아무것도 담기지 않습니다. (this: 전역 객체)
- 1번째 줄과 2번째 줄 : 전역 스코프에 있는 변수 a에 1을, outer에 함수를 할당합니다.
- 10번째 줄 : outer 함수를 호출합니다. 이에 따라 전역 컨텍스트의 코드는 10번째 줄에서 임시중단되고, outer 실행 컨텍스트가 활성화되어 2번째 줄로 이동합니다.
- 2번째 줄 : outer 실행 컨텍스트의 environmentRecord에 { inner } 식별자를 저장합니다. outer-Environment에는 outer 함수가 선언될 당시의 LexicalEnvironment를 참조복사합니다. 이를 [ GLOBAL, { a, outer } ] 라고 표기합시다. 첫 번째는 실행 컨텍스트의 이름, 두 번째는 environmentRecord 객체입니다. (this: 전역 객체)
- 3번째 줄 : outer 스코프에 있는 변수 inner에 함수를 할당합니다.
- 7번째 줄 : inner 함수를 호출합니다. 이에 따라 outer 실행 컨텍스트의 코드는 7번째 줄에서 임시중단되고, inner 실행 컨텍스트가 활성화되어 3번째 줄로 이동합니다.
- 3번째 줄 : inner 실행 컨텍스트의 environmentRecord에 { a } 식별자를 저장합니다. outerEnvironment에는 inner 함수가 선언될 당시의 LexicalEnvironment가 담깁니다. inner 함수는 outer 함수 내부에서 선언됐으므로 outer 함수의 LexicalEnvironment. 즉 [outer, { inner } ]를 참조 복사합니다. (this: 전역 객체)
- 4번째 줄 : 식별자 a에 접근하고자 합니다. 현재 활성화 상태인 Inner 컨텍스트의 environmentRecord에서 a를 검색합니다. a가 발견됐는데 여기에는 아직 할당된 값이 없습니다. (undefined 출력)
- 5번째 줄 : inner 스코프에 있는 변수 a에 3을 할당합니다.
- 6번째 줄 : inner 함수 실행이 종료됩니다. inner 실행 컨텍스트가 콜 스택에서 제거되고, 바로 아래의 outer 실행 컨텍스트가 다시 활성화되면서, 앞서 중단했던 7번째 줄의 다음으로 이동합니다.
- 8번째 줄 : 식별자 a에 접근하고자 합니다. 이때 자바스크립트 엔진은 활성화된 실행 컨텍스트의 LexicalEnvironment에 접근합니다. 첫 요소의 environmentRecord에서 a가 있는지 찾아보고, 없으면 outerEnvironmentReference에 있는 environmentRecord로 넘어가는 식으로 계속해서 검색합니다. 예제에서는 두 번째, 즉 전역 LexicalEnvironment에 a가 있으니 그 a에 저장된 값 1을 반환합니다. (1 출력)
- 9번째 줄 : outer 함수 실행이 종료됩니다. Outer 실행 컨텍스트가 콜 스택에서 제거되고, 바로 아래의 전역 컨텍스트가 다시 활성화되면서, 앞서 중단했던 10번째 줄의 다음으로 이동합니다.
- 11번째 줄 : 식별자 a에 접근하고자 합니다. 현재 활성화 상태인 전역 컨텍스트의 environmentRecord에서 a를 검색합니다. 바로 a를 찾을 수 있습니다.(1 출력) 이로써 모든 코드의 실행이 완료됩니다. 전역 컨텍스트가 콜 스택에서 제거되고 종료합니다.
한편 스코프 체인 상에 있는 변수라고 해서 무조건 접근 가능한 것은 아닙니다. 위 코드 상의 식별자 a는 전역 공간에서도 선언했고 Inner 함수 내부에서도 선언했습니다. inner 함수 내부에서 a에 접근하려고 하면 inner 스코프의 LexicalEnvironment에 a 식별자가 존재하므로 스코프 체인 검색을 더 진행하지 않고 즉시 inner LexicalEnvironment 상의 a를 반환하게 됩니다. 즉, inner 함수 내부에서 a 변수를 선언했기 때문에 전역 공간에서 선언한 동일한 이름의 a 변수에는 접근할 수 없는 셈입니다. 이를 변수 은닉화라고 합니다.
this
실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장됩니다. 실행 컨텍스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장됩니다. 그밖에는 함수를 호출하는 방법에 따라 this에 저장되는 대상이 다릅니다. 이에 대해서는 다음 부분에서 정리하겠습니다.
정리
실행 컨텍스트에 대해 배운 내용을 정리하면 다음과 같습니다.
1. 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다. 실행 컨텍스트는 전역 공간에서 자동으로 생성되는 전역 컨텍스트와 eval 및 함수 실행에 의한 컨텍스트 등이 있습니다. 실행 컨텍스트 객체는 활성화되는 시점에 VariableEnvironment, LexicalEnvironment, ThisBinding의 세 가지 정보를 수집합니다.
2. 실행 컨텍스트를 생성할 때는 VariableEnvironment와 LexicalEnvironment가 동일한 내용으로 구성되지만 LexicalEnvironment는 함수 실행 도중에 변경되는 사항이 즉시 반영되는 반면 VariableEnvironment는 초기 상태를 유지합니다.
3. VariableEnvironment와 LexicalEnvironment는 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집하는 environmentRecord와 바로 직전 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentReference로 구성돼 있습니다.
4. 호이스팅은 코드 해석을 좀 더 수월하게 하기 위해 environmentRecord의 수집 과정을 추상화한 개념으로, 실행 컨텍스트가 관여하는 코드 집단의 최상단으로 이들을 '끌어올린다'고 해석하는 것입니다. 변수 선언과 값 할당이 동시에 이뤄진 문장은 '선언부'만을 호이스팅하고, 할당 과정은 원래 자리에 남아있게 되는데, 여기서 함수 선언문과 함수 표현식의 차이가 발생합니다.
5. 스코프는 변수의 유효범위를 말합니다. outerEnvironmentReference는 해당 함수가 선언된 위치의 LexicalEnvironment를 참조합니다. 코드 상에서 어떤 변수에 접근하려고 하면 현재 컨텍스트의 LexicalEnvironment를 탐색해서 발견되면 그 값을 반환하고, 발견하지 못할 경우 다시 outerEnvironmentRecord에 담긴 LexicalEnvironment를 탐색하는 과정을 거칩니다. 전역 컨텍스트의 LexicalEnvironment까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환합니다.
6. 전역 컨텍스트의 LexicalEnvironment에 담긴 변수를 전역변수라 하고, 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수들은 모두 지역변수입니다. 안전한 코드 구성을 위해 가급적 전역변수의 사용은 최소화하는 것이 좋겠습니다.
7. this에는 실행 컨텍스트를 활성화하는 당시에 지정된 this가 저장됩니다. 함수를 호출하는 방법에 따라 그 값이 달라지는데, 지정되지 않은 경우에는 전역 객체가 저장됩니다.
마치며
자바스크립트의 기본에 대해 공부하면서, 기본도 정확하게 알지 못하고, 앞으로 나아가려 했구나 하는 생각이 들었습니다. 기초이기에 지금 공부하는 내용을 더 잘 이해해야겠다고 생각했습니다. 기본에 충실할 수 있는 개발자가 되고 싶습니다.
출처
'JavaScript > 코어 자바스크립트' 카테고리의 다른 글
[자바스크립트] 명시적으로 this를 바인딩하는 방법 (feat 코어 자바스크립트) (0) | 2021.10.06 |
---|---|
[자바스크립트] 상황에 따라 달라지는 this (feat 코어 자바스크립트) (0) | 2021.10.01 |
[자바스크립트] 실행 컨텍스트란? (feat 코어 자바스크립트) (1) | 2021.09.24 |
[자바스크립트] undefined와 null (feat 코어 자바스크립트) (0) | 2021.09.16 |
[자바스크립트] 불변 객체 (feat 코어 자바스크립트) (0) | 2021.09.13 |