Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 코드리뷰
- JavaScript
- Promise
- 비동기 요청 처리
- tanStack Query
- 자바스크립트
- React
- 동적 웹 페이지
- dom
- 웹개발
- 퍼포먼스 최적화
- 비동기프로그래밍
- fe
- html
- 웹 성능 최적화
- 프론트엔드
- css
- Node.js
- 프라미스체이닝
- 웹 성능
- 풀스택 개발
- 컴포넌트
- 자바스크립트공부
- 웹 개발
- jsx
- 패키지 스크립트
- 상태 관리 라이브러리
- 리액트 기초
- #프론트엔드개발
- 국제화(i18n)
Archives
- Today
- Total
sodol-dotcom
[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240730 본문
𝜗𝜚 오늘 공부한 내용
- JavaScript 문법 종합반 3주차
- 실행 컨텍스트, 콜 스택, VE&LE, 호이스팅, 함수 선언문&함수 표현식, 스코프, 스코프체인, outerEnverionmentReference(= outer), this의 함수, 메서드에 따른 작동
1-1) 실행 컨텍스트 구성 예시 코드
주어진 코드의 실행 결과와 각 단계의 해석을 설명드리겠습니다.
- 코드
javascript코드 복사
var a = 1;
function outer() {
function inner() {
console.log(a); // undefined
var a = 3;
}
inner();
console.log(a);
}
outer();
console.log(a);
- 실행 결과
javascript코드 복사
undefined
1
1
- 해석
- 전역 컨텍스트:
javascript코드 복사 var a = 1;
- 전역 변수
a
가 선언되고 값이1
로 초기화됩니다.
- 함수 outer 선언:
outer
함수가 선언됩니다.outer
함수 안에inner
함수가 선언됩니다.
javascript코드 복사 function outer() { function inner() { console.log(a); // undefined var a = 3; } inner(); console.log(a); }
- outer 함수 호출:
outer
함수가 호출됩니다.
javascript코드 복사 outer();
- inner 함수 호출:
outer
함수가 호출되면서inner
함수가 호출됩니다.inner
함수 내부에서console.log(a)
가 실행됩니다. 여기서a
는 호이스팅(hoisting)으로 인해 선언이 함수의 최상단으로 끌어올려지지만 초기화는 되지 않은 상태이므로undefined
가 출력됩니다.var a = 3;
이 실행되어inner
함수의 지역 변수a
가3
으로 초기화됩니다.
javascript코드 복사 function inner() { console.log(a); // undefined var a = 3; } inner();
- outer 함수에서 console.log(a):
inner
함수 실행이 끝난 후,outer
함수 내의console.log(a)
가 실행됩니다.outer
함수 내에는var a
로 선언된 변수가 없으므로,outer
함수는 전역 변수a
를 참조하여1
을 출력합니다.
javascript코드 복사 console.log(a);
- 전역에서 console.log(a):
outer
함수 실행이 끝난 후, 전역에서console.log(a)
가 실행됩니다.- 전역 변수
a
는 여전히1
로 초기화된 상태이므로1
이 출력됩니다.
javascript코드 복사 console.log(a);ß
1-2) 요약
inner
함수에서var a
선언이 호이스팅되어console.log(a)
에서undefined
가 출력됩니다.outer
함수와 전역에서는 전역 변수a
의 값1
이 출력됩니다.
1-3) 콜 스택(Call Stack) 분석
- 콜 스택 분석 내용
- 초기 상태
- 전역 컨텍스트:
- 전역 변수
a
가 선언되고1
로 초기화됩니다.
- 전역 변수
- 전역 컨텍스트:
- outer 함수 호출
- outer 함수가 호출됨:
- 콜 스택:
[outer]
outer
함수가 콜 스택에 추가됩니다.
- 콜 스택:
- inner 함수 선언 및 호출:
outer
함수 안에서inner
함수가 선언되고 호출됩니다.- 콜 스택:
[outer, inner]
inner
함수가 콜 스택에 추가됩니다.
- outer 함수가 호출됨:
- inner 함수 실행
- inner 함수 실행:
console.log(a)
가 실행됩니다.inner
함수 안에서var a
가 선언되지만 아직 초기화되지 않은 상태이므로 호이스팅으로 인해undefined
가 출력됩니다.- 콜 스택:
[outer, inner]
- 이후
var a = 3;
가 실행되어inner
함수의 지역 변수a
가3
으로 초기화됩니다. inner
함수 실행이 끝나면서 콜 스택에서 제거됩니다.- 콜 스택:
[outer]
- inner 함수 실행:
- outer 함수 실행
- outer 함수 실행:
inner
함수 호출이 끝난 후,outer
함수에서console.log(a)
가 실행됩니다.outer
함수 내에var a
로 선언된 변수가 없으므로 전역 변수a
의 값1
이 출력됩니다.outer
함수 실행이 끝나면서 콜 스택에서 제거됩니다.- 콜 스택:
[]
- outer 함수 실행:
- 전역 컨텍스트 실행
- 전역 컨텍스트:
outer
함수 호출이 끝난 후, 전역 컨텍스트에서console.log(a)
가 실행됩니다.- 전역 변수
a
는 여전히1
로 초기화된 상태이므로1
이 출력됩니다.
- 전역 컨텍스트:
- 콜 스택의 시각적 흐름: 아래와 같이 콜 스택에 함수가 쌓이고 제거되는 과정을 통해 코드의 실행 흐름을 이해할 수 있습니다.
- 초기 상태:
[Global Execution Context]
outer
함수 호출:[Global Execution Context] [outer Execution Context]
inner
함수 호출:[Global Execution Context] [outer Execution Context] [inner Execution Context]
inner
함수 실행 (console.log(a)
):[Global Execution Context] [outer Execution Context] [inner Execution Context] // Logs: undefined
inner
함수 종료:[Global Execution Context] [outer Execution Context]
outer
함수 실행 (console.log(a)
):[Global Execution Context] [outer Execution Context] // Logs: 1
outer
함수 종료:[Global Execution Context]
- 전역 컨텍스트 실행 (
console.log(a)
): [Global Execution Context] // Logs: 1
- 초기 상태
**아래 순서 요약**코드 실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드 종료
1-4) 실행 컨텍스트(Execution Context)
- 객체임. 어떤 객체나하면 실행할 코드에 제공할 환경 정보를 모아놓은 객체.
- 그 객체 안에는 3가지가 존재
1-5) LE
호이스팅 규칙 실습 (1)
1-5-1) LE
호이스팅 적용전 코드의 기초적인 해석
function a (x) {. // 1.
console.log(x); // 2.
var x; // 3.
console.log(x); // 4.
var x = 2; // 5.
console.log(x); // 6.
};
a(1); // 7.
- function a(x) {...};
- 함수 선언. 함수의 이름과 동작을 정의함. 실행은 x
- 이 함수는
매개변수 ‘x’
를출력
하는 기능을 가지고 있는 함수임. a
: 함수의 이름으로 호출시 사용. 함수의 동작을 식별하는데 필요함. 의미 있는 이름을 사용하여 명확하게 것이 더 좋음.(예: calculateSum)x
: 매개 변수. 함수가 호출될 때 전달받는 매개 변수- 매개 변수(Parameter, 인자(값)): 함수가 호출될 때 외부에서 전달된 값을 함수 내부에 사용할 수 있도록 정의하는 변수. +) x = 2; 일때 x는 매개변수 2는 인수(Arguments)이다.
- 인수: 함수를 호출 시 함수의 매개 변수에 전달되는 실제 값
- 매개변수 = Parameter = 인자 = 인자값 / 인수 = Arguments
- 변수 값: 변수에 저장된 데이터 ≠ 인수: 한수 호출시 매개 변수에 전달되는 값
- console.log(x);
- 함수 내부에서 매개 변수
x
의 값인1
을 출력함.
= 함수가 호출되면서매개 변수 'x'
에 값(= 인자)1
이 전달된다.
- 함수 내부에서 매개 변수
- ‘var x;’
- 변수 ‘x’를 선언함 (= x라는 변수의 이름(식별자)을 설정함)
- 선언은 코드에서 사용할 이름을 정의, 그 이름이 어떤 데이터나 동작을 의미하는지 명시하는 과정
- 선언을 통해 변수, 함수, 클래스 등 다양한 구성 요소를 정의하고 사용할 수 있게 됨
- console.log(x);
- x의 값 출력
- var x =2;
- 매개 변수 ‘x’의 값을 2로 설정 → 변수 ‘x’는 이제 2로 초기화됨
- ** 이 할당은 ‘var x;’ 선언과는 별개로 실행됨
- console.log(x);
- (변수 ‘x’의 값이 2로 설정된 이후 실행)2를 출력
- a(1);
- 함수 ‘a’를 호출하면서 매개 변수 ‘x’에 값 1을 전달함
- 위의 모든 단계가 이 호출로 인해 실행 됨
1-5-2) LE
호이스팅 적용전 코드의 처리 과정에 대한 주석
// action point 1: 매개 변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
// action point 2: 결과 예상하기
// action point 3: hoisting 적용해본 후 결과를 다시 예상해보기
function a (x) {. // 1. 함수가 호출되면서 매개 변수 'x'에 값 '1'이 전달, 이 시점에서 x는 1이다.
console.log(x); // x의 값인 1이 출력
var x; // 2. var x;는 변수 x를 선언. JavaScript에서는 var로 선언된 변수는 함수의 시작 부분으로 끌어올려짐(hoisting). 즉, 이 var x; 선언은 함수의 처음으로 이동하지만 실제 값 할당은 여기서 이 후에 이루어짐.
console.log(x); // 3. 다시 console.log(x);를 호출, 이 시점에서는 변수 x가 선언되었지만 값이 할당되지 않았기 때문에 x의 값은 'undefined'임. 'undefined' 출력됨.
var x = 2; // 4. 변수 x에 2를 할당. 변수 x의 값이 2로 설정됨. 이 줄의 변수 할당은 함수의 맨 처음으로 호이스팅된 선언 var x;와는 별개로 진행됨
console.log(x); // 5. 이 시점에서 변수 x의 값은 2로 설정되었으므로 2가 출력된다.
};
a(1); // 6. 함수 a를 호출. 이 호출로 인해 위의 코드가 실행됨. 매개 변수 x에는 값 1이 전달되므로, 함수 내부의 실행 흐름이 시작됨
- 위 과정의 2~3. 과정에 대한 추가적인 설명
- C줄에서의 ‘변수 x의 선언(var x;)’은 A줄의 매개 변수 x와 별개로 새로운 변수가 선언된 것
- C줄은 변수 x는 선언 되었지만 값은 할당되지 않은 상황 → undefined가 출력 됨(으로 예상)
- 즉, 호이스팅을 모르면 undefined로 출력될 것으로 예상하지만 실제로는 ‘1’이 출력됨
// action point 1: 매개 변수 다시 쓰기(JS 엔진은 똑같이 이해한다) // action point 2: 결과 예상하기 // action point 3: hoisting 적용해본 후 결과를 다시 예상해보기 function a(x) { // A console.log(x); // B var x; // C: 호이스팅 되는 부분 console.log(x); // D var x = 2; // E console.log(x); // F } a(1); // G
1-5-3-1) LE
호이스팅 된 코드의 실제 코드 진행과정 살펴보기
**function a(x) {
var x; // 이 선언이 함수 최상위로 호이스팅됨
console.log(x);
console.log(x);
x = 2; // 할당은 여전히 여기에 있음
console.log(x);
}**
- 중요한 점은 매개 변수가 변수 ‘x’로 선언되어 초기값 ‘1’을 가지게 됨. 즉, ‘x’는 함수가 실행될 때 이미 ‘1’로 설정됨. 변수 선언만 호이스팅되고, 할당은 호이스팅되지 않음!
1-5-3-2) 콜 스택을 통한 설명
- 함수 호출 ‘a(1)’
- 코드 실행 시작 → ‘a(1);’ 함수 호출 발생 → 콜 스택에 ‘a’ 함수 추가됨
// 콜스택 상태: | | | | | a(1) | |__________|
- 첫 번째 ‘console. log(x);’
- 함수 ‘a’의 매개 변수 ‘x’는 ‘1’로 초기화됨
- 첫 번째 ‘console.log(x);’가 실행됨 → ‘1’이 출력됨
// 콜스택 상태: | | | a(1) | |__________|
- ‘var x;’ 선언
- 변수 ‘x’가 재선언됨. (이는 함수 스코프에서 이미 존재하는 변수이므로 아무런 영향을 미치지 않음.)
- 호이스팅에 의해 이 선언은 함수의 최상위로 끌어올려짐
- 그러나 변수 ‘x’는 이미 매개 변수로 선언되어 있으므로 값 ‘1’이 유지 됨
// 콜스택 상태: | | | a(1) | |__________|
- 두 번째 ‘console.log(x);’
- 두 번째 ‘console.log(x);’가 실행됨.
- ‘x’는 여전히 ‘1’ 임. (재선언은 영향을 미치지 않으므로) → ‘1’이 출력됨
// 콜스택 상태: | | | a(1) | |__________|
- ‘var x = 2; ‘ 할당
- 변수 ‘x’에 값 ‘2’가 할당됨. 이제 ‘x’는 ‘2’임
// 콜스택 상태: | | | a(1) | |__________|
- 세 번째 ‘console.log(x);’
- 세 번째 ‘console.log(x);’가 실행됨.
- 출력: ‘2’
// 콜스택 상태: | | | a(1) | |__________|
- 함수 종료
- 함수 ‘a’의 실행이 끝납니다. 콜 스택에서 ‘a(1)’이 제거됩니다.
// 콜스택 상태: | | | | |__________|
1-5-3-3) 콜 스택 요약
- 함수
a
가 호출되면x
는1
로 초기화됩니다. - 첫 번째
console.log(x);
는1
을 출력합니다. var x;
선언은 호이스팅되어 이미 처리되었습니다. 값은 여전히1
입니다.- 두 번째
console.log(x);
도1
을 출력합니다. var x = 2;
할당으로x
는2
로 바뀝니다.- 세 번째
console.log(x);
는2
를 출력합니다. - 함수 실행이 끝나고 콜 스택에서 제거됩니다.
- 콜 스택: 함수 호출 시에만 콜 스택에 쌓입니다.
console.log
와 같은 내장 함수는 콜 스택에 직접적으로 표시되지 않고, JavaScript 런타임 환경의 내장 기능으로 처리됩니다. console.log(x)
: 내부적으로 호출되지만 콜 스택에는 표시되지 않습니다. 호출이 완료되면 콜 스택에 나타나지 않습니다.
1-6) LE
호이스팅 규칙 실습 (2)
- 예시 코드 (1. 호이스팅을 배제하고 생각할 경우 출력 값)
function a() { console.log(b); // 예상 출력: 오류 var b = 'bbb'; console.log(b); // 예상 출력: bbb function b() { } console.log(b); // 예상 출력: function } a();
- 예시 코드 (2. 호이스팅 적용)
- A. 호이스팅의 함수 선언: 함수 선언은 전체가 호이스팅됨. 즉, 함수 정의 자체가 스코프의 최상위로 끌어올려진다.
(이로 인해 ‘b’는 변수보다는 함수로 정의됨. 함수 선언이 변수 선언을 덮어쓰게 된다. - B. 선언부 호이스팅:
var b;
는 함수의 최상위로 끌어올려지지만, 할당부인b = 'bbb';
는 코드에서 변수 선언 이후의 실제 위치에서 수행됩니다.var b = ‘bbb’;
에서 변수의 선언부는‘var b;’
이고 변수의 할당부는‘b = ‘bbb’;
이다. **중요!!**
- A. 호이스팅의 함수 선언: 함수 선언은 전체가 호이스팅됨. 즉, 함수 정의 자체가 스코프의 최상위로 끌어올려진다.
function a() { var b; // 변수 선언부 호이스팅 function b() { } // 함수 선언은 전체를 호이스팅 -> 아래 'A.' 참고 console.log(b); b = 'bbb'; // 변수의 할당부는 원래 자리에 -> 아래 'B.' 참고 console.log(b); console.lob(b); } a();
- 예시 코드 (3. 호이스팅 적용 후 예상 출력 값)
- A. 호이스팅의 함수 선언: 함수 선언은 전체가 호이스팅됨. 즉, 함수 정의 자체가 스코프의 최상위로 끌어올려진다.
(이로 인해 ‘b’는 변수보다는 함수로 정의됨. 함수 선언이 변수 선언을 덮어쓰게 된다. - B. 선언부 호이스팅:
var b;
는 함수의 최상위로 끌어올려지지만, 할당부인b = 'bbb';
는 코드에서 변수 선언 이후의 실제 위치에서 수행됩니다.var b = ‘bbb’;
에서 변수의 선언부는‘var b;’
이고 변수의 할당부는‘b = ‘bbb’;
이다. **중요!!**
- A. 호이스팅의 함수 선언: 함수 선언은 전체가 호이스팅됨. 즉, 함수 정의 자체가 스코프의 최상위로 끌어올려진다.
function a() { var b; function b() { } console.log(b); // 예상 출력: function b = 'bbb'; console.log(b); // 예상 출력: bbb console.lob(b); // 예상 출력: bbb } a();
1-7) 함수 선언문 & 함수 표현식
- ‘함수 선언문과 함수 표현식이 있다.’ 정도만 기억하면 됨. 추가적으로 밑줄 친 부분만 기억!
- 함수 선언문은 호이스팅시 함수 전체가 위로 끌어올려짐
- 함수 표현식은 호이스팅시 변수 선언부만 위로 끌어올려짐
→ 함수 표현식을 활용하는 습관이 필요함(협업 많이하고 복잡한 코드일수록, 전역 공간에서 이루어지는 코드 협업일수록 함수 표현식을 활용히야 위험한 코드, 에러를 만드는 코드를 방지할 수 있음)
- 함수 선언문(함수의 정의부만 존재하는 방식)
- 함수 이름을 소괄호 앞에 씀. (함수명 a가 곧 변수명)
- function 정의부만 존재, 할당 명령이 없는 경우
function a () { /*…*/ } a( ); // 실행 oK
- 함수 표현식(변수에 할당하는 방식)
- 함수 이름을 변수 이름으로 씀
- 정의한 functinon을 별도 변수에 할당하는 경우
- 익명 함수 표현식:
- 변수명 b가 곧 변수명(일반적인 case에)
// 추가 예 var myFunction = function() { console.log('Hello, world!'); }; myFunction(); // 실행 ok!
var b = function ( ) { /*…*/ } b( ); // 실행 ok
- 기명 함수 표현식:
- 변수명은 c, 함수명은 d
- d( )는 c( ) 안에서 재귀적으로 호출될 때만 사용 가능하므로 사용성에 대한 의문
// 추가 예 var myFunction = function namedFunction() { console.log('Hello, world!'); }; myFunction(); // 실행 ok! namedFunction(); // 에러!!
var c = function d ( ) { /*…*/ } c( ); // 실행ok d( ); // 에러!
- 기명 함수 표현식은 활용성이 거의 없음
- 익명 함수 표현식:
1-8) LexicalEnvironment, 스코프, 스코프체인, outerEnvironmentReference(= outer)
- 요약: 각각의 실행 컨텍스트는 LE(LexicalEnvironment) 안에 record와 outer를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어 올 수 있다.
- 실행 컨텍스트 관점에서의 스코프
- 스코프
- 식별자에 대한 유효 범위
- 대부분 언어에서 존재, JS에도 존재
- 스코프 체인
- 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것
- 실행 컨텍스트 생성될때 형성되며 변수와 함수에 접근할 수 있는 범위를 결정한다.
(변수와 함수의 접근을 관리하는 중요한 메커니즘!) - 위 두가지로 인해 변수 충돌을 방지하고 코드의 가독성을 높인다.
- 클로저 지원을 한다. (아래 형성 과정 참고)
- 클로저: 함수가 자신이 정의된 스코프의 변수에 접근할 수 있도록 하여, 함수가 정의될 때의 환경을 기억하게 한다.
- 이는 함수가 자신의 외부 스코프의 변수에 접근할 수 있게 해주어 강력한 프로그래밍 패턴을 가능하게 한다. → 스코프 체이닝을 하는 이유!
- 함수 호출 시 새로운 실행 컨텍스트가 생성되고, 이 컨텍스트는 자신이 정의된 스코프와 상위 컨텍스트를 참조한다.
| D | | C | | B | | A | | 전역 context |
스코프 체인의 형성 과정:
-
- 위와 같이 call stack이 생성되어 있을때 - 각각의 실행 컨텍스트는 `record(environmentRecord)`와 `outer(outerEnvironmentReference)` 를 갖는다. - `실행 컨텍스트 A`는 `전역 context`의 `LE`를 `outer`로 갖는다.(= 참조한다) LE: LexicalEnvironment, VE와 다르게 변경사항을 실시간으로 반영하는 실행 컨텍스트의 구성요소 중 하나 (스냅샷을 유지하지 않음) - 즉, A의 상위 컨텍스트는 전역 context! - `실행 컨텍스트 B`는 `실행 컨텍스트 A`의 `LE`를 `outer`로 갖는다.(= 참조한다) - `실행 컨텍스트 C`는 `실행 컨텍스트 B`의 `LE`를 `outer`로 갖는다.(= 참조한다) - `실행 컨텍스트 D`는 `실행 컨텍스트 C`의 `LE`를 `outer`로 갖는다.(= 참조한다)
- 스코프
- 스코프 체인의 콜스텍을 그리며 푸는 예제(scope 관점에서)```jsx
// 아래 코드를 여러분이 직접 call stack을 그려가며 scope 관점에서 변수에 접근해보세요!
// 어려우신 분들은 강의를 한번 더 돌려보시기를 권장드려요 :) (JS 문법 3주차) 요약
- ‘inner’ 함수 내 ‘console.log(a)’
: ‘undefined’ (호이스팅에 따라 변수 ‘a’가 선언되지만 초기화되지 않음) - ‘outer’ 함수 내 ‘console.log(a)’
: ‘1’(전역 스코프의 ‘a’를 참조) - 전역 내 ‘console.log(a)’
: ‘1’ (전역스코프의 ‘a’를 참조)
- ‘inner’ 함수 내 ‘console.log(a)’
- // 호이스팅 전*
};console.log(a); // 이 값은 뭐가 나올까요?(scope 관점에서) var a = 3;
inner();
console.log(a); // 이 값은 또 뭐가 나올까요? 이유는요? scope 관점에서!
};
outer();
console.log(a); // 이 값은 뭐가 나올까요? 마찬가지로 이유도!
``` - ```jsx
// 아래 코드를 여러분이 직접 call stack을 그려가며 scope 관점에서 변수에 접근해보세요!
// 어려우신 분들은 강의를 한번 더 돌려보시기를 권장드려요 :) (JS 문법 3주차) - var a = 1;
var outer = function () {
var inner = function () { - // 호이스팅 후*
var inner = function () { // 내부 스코프에서 해결var a; **// 호이스팅 되며 var a; 위로 올라옴**
console.log(a); // 이 값은 뭐가 나올까요?(scope 관점에서)
a = 3; // 호이스팅된 var a; 때문에 'undefined'이 출력
};
inner();
console.log(a); // 이 값은 또 뭐가 나올까요? 이유는요? scope 관점에서! // 위의 inner 함수는 이미 나가고 없어서 3은 x
}; // 이 때
outer();
console.log(a); // 이 값은 뭐가 나올까요? 마찬가지로 이유도! - var a = 1;
var outer = function () {
- 전역 코드 실행
- 전역 스코프:
var a = 1;
→ 변수 a 선언 및 ‘1’로 초기화var outer = function () {…}
→ 함수 정의
- 콜 스텍:
- 전역 실행 컨텍스트 (Global Context)
- ```sql
- ----------------+
| Global Context |
| (a: 1, outer) | - ----------------+
- 전역 스코프:
- ‘outer’ 함수 호출
- 전역 스코프:
- ‘outer’ 함수 호출
- 새로운 실행 컨텍스트 생성: ‘outer’ 실행 컨텍스트
- 콜 스택:
- ‘outer’ 실행 컨텍스트가 스택 푸시됨
- 전역 실행 컨텍스트가 아래에 남아있음
- ```sql
- ----------------+
| Outer Context | <-- 현재 실행 중
| (inner) | - ----------------+
| Global Context |
| (a: 1) | - ----------------+
- 전역 스코프:
- ‘inner’ 함수 호출
- ‘outer’ 실행 컨텍스트:
- ‘inner’ 함수 호출
- 새로운 실행 컨텍스트 생성: ‘inner’ 실행 컨텍스트
- 콜 스택:
- ‘inner’ 실행 컨텍스트가 스택에 푸쉬됨
- ‘outer’ 실행 컨텍스트가 아래에 남아있음
- 전역 실행 컨텍스트가 가장 하단에 있음
- ```sql
- ----------------+
| Inner Context | <-- 현재 실행 중
| (a: undefined) | - ----------------+
| Outer Context |
| (inner) | - ----------------+
| Global Context |
| (a: 1) | - ----------------+
- ‘outer’ 실행 컨텍스트:
- ‘inner’ 함수 내 ‘console.log(a)’
- 스코프 체인:
- ‘inner’ 함수의 로컬 스코프에서 ‘a’를 찾으려고 시도하지만 ‘a’는 아직 ‘inner’ 함수의 로컬 스코프에서 ‘var a;’가 선언만 되어 있고 초기화 되지 않은 상태이다.
- JavaScript는 호이스팅을 고려하여 ‘a’는 ‘undefined’로 초기화함
- 상위 스코프인 ‘outer’의 ‘a’는 아직 접근되지 않음
- 출력: ‘undefined’
- 콜 스택:
- ‘inner’ 실행 컨텍스트에서 ‘console.log(a)’ 호출 후 계속 실행
- 로컬 변수 초기화: 3이 아닌 1이 출력되는 이유
inner
함수 내에서var a = 3
으로 초기화되지만, 이 초기화는console.log(a)
호출 이후에 발생- 초기화는 변수에 대한 값을 설정하지만, 이미
console.log(a)
에서a
는undefined
로 출력된 상태입니다.
- ----------------+
| Inner Context | <-- 현재 실행 중
| (a: undefined) | - ----------------+
| Outer Context |
| (inner) | - ----------------+
| Global Context |
| (a: 1) | - ----------------+
- // outer 함수 내 console.log(a);의 출력 값이 1 인 이유는:
// 전역 스코프의 a를 참조, inner 함수의 a는 로컬 변수로 3으로 설정되었지만 outer 함수에는 영향을 미치지 않음)
- 스코프 체인:
- ‘inner’ 함수 내 ‘var a = 3’
- ‘inner’ 함수의 로컬 스코프:
- ‘a’를 ‘3’으로 할당
- 콜 스택:
- ‘inner’ 함수의 실행 컨텍스트가 제거됨(실행 컨텍스트가 종료됨)
- ```sql
- ----------------+
| Outer Context | <-- 현재 실행 중
| (a: undefined) | - ----------------+
| Global Context |
| (a: 1) | - ----------------+
- ‘inner’ 함수의 로컬 스코프:
- ‘outer’ 함수 내 ‘console.log(a)’
- 스코프 체인:
- ‘outer’ 함수의 로컬 스코프 ‘a’가 정의되어 있지 않음
- 전역 스코프의 ‘a’를 참조함
- 출력: ‘1’
- 콜 스택:
- ‘outer’ 함수의 실행 컨텍스트가 제거됨
- ```sql
- ----------------+
| Global Context | <-- 현재 실행 중
| (a: 1) | - ----------------+
- 스코프 체인:
- 전역 ‘console.log(a)’
- 스코프 체인:
- 전역 스코프의 ‘a’를 참조함
- 출력: ‘1’
- 스코프 체인:
- 최종 콜 스택 시각화
- 전역 실행 컨텍스트: 모든 함수 호출이 완료되었으므로 전역 실행 컨텍스트만 남아 있음
- ```sql
- ----------------+
| Global Context | <-- 현재 실행 중
| (a: 1) | - ----------------+
- 오늘 들은 강의는 대체적으로 내용도 많고 중간에 한번 놓치면 처음부터 다시 들어야 이해가 가능했다. 내일은 좀 더 쉬운 내용이 나왔으면..ㅠㅠㅠ
2. ThisBindings
- this가 전역환경에서 어떤 것을 의미하는지?
- 전역 환경을 설명할때는 항상 '런타임'을 고려해야 한다.
- 런타임: runtime, 러닝하는 타임 -> 코드가 돌아가는 시간 또는 환경을 의미함
- 자바스크립트에는 두가지 환경이 있다. (노드&브라우저)
- 1️⃣ **노드**
- 터미널에서 node 검색 -> 커맨드라인이 바뀜 -> console.log(this) 검색 -> global이 나옴
- -> this === global 검색 -> true 나옴 (this가 전역 변수임을 알 수 있음)
- 크롬에서 option + command + I -> 개발자 도구가 뜸 -> 콘솔 열기
- -> console.log(this) -> window가 나옴 -> this === wnindow 검색 -> true
- 즉 전역 환경에서 this -> 노드(global 객체), 브라우저(window 객체)이다.
- 메서드로 호출될 때와 함수로 호출될 때 this가 어떻게 바뀌는지 차이점 확인
- 기준은 '독립성'
- 함수는 독립적으로 쓰이고 메서드는 종속적으로 쓰임
- 함수는 그 자체로 독립적인 기능을 수행, 메서드는 누군가에 의해서 수행됨
= 함수는 스스로 수행되고 실행되는 주체가 없고 자신임, 메서드는 실행하는 주체가 있어야 함
= 함수의 this -> 전역 객체, 메서드의 this -> 호출의 주체 - 메서드는 자신을 호출한 대상 객체에 대한 동작을 수행한다.
( 객체.메서드(); -> 객체가 메서더의 this가 됨 ) - 이 차이점이 중요한 이유: this를 결정하는 데 있어서 매우 중요하기 때문
2-1. Case 1: 함수
javascript코드 복사
var func = function (x) {
console.log(this, x);
};
func(1); // window {...} 1
- 설명
var func = function (x) { ... };
는func
라는 이름의 변수를 선언하고, 여기에 함수를 저장합니다. 이 함수는x
라는 입력값을 받습니다.func(1);
는 함수를 호출하면서1
을x
에 전달합니다.- 함수 내부에서
console.log(this, x);
를 실행합니다. 여기서this
는 함수가 호출될 때 누가 호출했는지를 나타내는 특별한 키워드입니다. - 이 경우, 함수는 아무 객체도 아닌 그냥
func
로 호출되었습니다. 그래서this
는 기본적으로 전역 객체를 참조하게 됩니다. 브라우저 환경에서는 전역 객체가window
입니다. - 따라서
console.log(this, x);
는window
객체와1
을 출력하게 됩니다.
- 즉,
func(1);
을 실행하면 브라우저 콘솔에window { ... } 1
이 출력됩니다.
2-2. Case 2: 메서드
javascript코드 복사
var obj = {
method: func,
};
obj.method(2); // { method: f } 2
- 설명
var obj = { method: func };
는obj
라는 이름의 객체를 선언합니다. 이 객체는method
라는 속성을 가지며, 그 값은 앞에서 정의한 함수func
입니다.obj.method(2);
는obj
객체의method
를 호출하면서2
를x
에 전달합니다.- 이 경우
method
는 객체obj
의 일부로 호출됩니다. 그래서 함수 내부에서this
는obj
객체를 참조합니다.
(메서드는 호출 주체를 명시할 수 있기 때문에 this가 해당 객체(obj)를 의미하는 것임) - 따라서
console.log(this, x);
는obj
객체와2
를 출력하게 됩니다.
- 즉,
obj.method(2);
을 실행하면 브라우저 콘솔에{ method: f } 2
가 출력됩니다. - 핵심 포인트:
- 전역 함수 호출: 함수를 그냥 호출하면 (
func(1)
)this
는 전역 객체를 참조합니다. 브라우저 환경에서는window
가 됩니다. - 객체의 메서드 호출: 객체의 메서드를 호출하면 (
obj.method(2)
)this
는 그 객체를 참조합니다. 이 경우에는obj
입니다. - 따라서, 이 두 가지 경우에 대해 다른 결과가 출력되는 이유는
this
키워드가 어떻게 해석되느냐에 따라 달라지는 것입니다.*
2-3. 메서드 내부에서의 this(예제 1)
- 위의 내용에서 보았듯, this에는 호출을 누가 했는지에 대한 정보가 담긴다.
- 아래 예제을 분석해보면 메소드로서의 호출인지 함수로서의 호출인지를 구분할 수 있는 눈이 생길 수 있을 것임!
- vscode에 꼭 찍어보기
var obj = {
methodA: function () { console.log(this) },
inner: {
methodB: function () { console.log(this) },
}
};
obj.methodA();
obj['methodA']();
obj.inner.methodB();
obj.inner['methodB']();
obj['inner'].methodB();
obj['inner']['methodB']();
- 해석:
obj.methodA();
methodA
가 호출됩니다.this
는methodA
를 호출한 객체를 가리킵니다.- 여기서
this
는obj
객체를 가리킵니다. - 출력:
{ methodA: f, inner: { methodB: f } }
obj['methodA']();
methodA
가 배열 표기법으로 호출됩니다.- 배열 표기법으로 접근해도
this
는 동일하게obj
객체를 가리킵니다. - 출력:
{ methodA: f, inner: { methodB: f } }
obj.inner.methodB();
methodB
가 호출됩니다.this
는methodB
를 호출한 객체를 가리킵니다.- 여기서
this
는inner
객체를 가리킵니다. - 출력:
{ methodB: f }
obj.inner['methodB']();
methodB
가 배열 표기법으로 호출됩니다.- 배열 표기법으로 접근해도
this
는 동일하게inner
객체를 가리킵니다. - 출력:
{ methodB: f }
obj['inner'].methodB();
inner
객체에 접근한 후methodB
가 호출됩니다.this
는methodB
를 호출한 객체를 가리킵니다.- 여기서
this
는inner
객체를 가리킵니다. - 출력:
{ methodB: f }
obj['inner']['methodB']();
inner
객체와methodB
에 배열 표기법으로 접근한 후 호출됩니다.- 배열 표기법으로 접근해도
this
는 동일하게inner
객체를 가리킵니다. - 출력:
{ methodB: f }
- 요약:
obj.methodA();
와obj['methodA']();
는this
가obj
를 가리킵니다.obj.inner.methodB();
,obj.inner['methodB']();
,obj['inner'].methodB();
,obj['inner']['methodB']();
는this
가inner
를 가리킵니다.
2-4. 메서드 내부에서의 this(예제 2)
- this가 단계별로 어떻게 작동하는지 아래 예제도 풀어보자.
var obj1 = {
outer: function() {
console.log(this);
avr innerFunc = function() {
console.log(this);
}
innerFunc();
var obj2 = {
innerMethod: innerFunc
};
obj2.innerMethod();
}
};
obj1.outer();
- 해석:
- 이 코드는
this
가 어떻게 동작하는지 이해하기 위한 좋은 예제입니다.this
는 함수가 어떻게 호출되었는지에 따라 달라집니다.obj1.outer()
호출:- 여기서
obj1
객체의outer
메서드를 호출합니다. outer
메서드 내부에서console.log(this);
를 실행합니다.this
는outer
메서드를 호출한 객체인obj1
을 가리킵니다.- 따라서 첫 번째 출력은
obj1
객체가 됩니다.
- 여기서
obj1.outer();
innerFunc
호출 (outer 메서드 내부):innerFunc
는outer
메서드 내부에서 선언된 함수입니다.innerFunc
가 별도로 호출될 때,this
는 전역 객체를 가리킵니다. (브라우저 환경에서는window
, Node.js 환경에서는global
)- 따라서
console.log(this)
는 전역 객체를 출력합니다. - 출력:
window
(브라우저 환경)
var innerFunc = function() { console.log(this); } innerFunc();
obj2.innerMethod()
호출 (outer 메서드 내부):obj2
라는 새로운 객체를outer
메서드 내부에서 선언합니다. 이 객체는innerMethod
라는 메서드를 가지며, 이 메서드는 앞서 선언한innerFunc
함수를 가리킵니다.obj2.innerMethod()
를 호출할 때,this
는innerMethod
를 호출한 객체인obj2
를 가리킵니다.- 따라서 세 번째 출력은
obj2
객체가 됩니다.
var obj2 = { innerMethod: innerFunc }; obj2.innerMethod();
- 이 코드는
- 요악:
- 첫 번째 출력:
obj1.outer()
호출 시this
는obj1
을 가리킵니다. { outer: f, inner: { innerMethod: f } }
- 두 번째 출력:
innerFunc()
호출 시this
는 전역 객체를 가리킵니다. window // 브라우저 환경
- 세 번째 출력:
obj2.innerMethod()
호출 시this
는obj2
를 가리킵니다. { innerMethod: f }
- 첫 번째 출력:
- 전체 출력:
{ outer: f, inner: { innerMethod: f } } window { innerMethod: f }
𝜗𝜚 어려웠던 내용
- 오늘은 실행 컨텍스트, 콜스택, 호이스팅 등등 많은 내용을 배웠는데 한번에 이해하기에는 좀 어려웠다. 강의를 여러번 다시 들으면서 하느라 진도를 많이 못나가서 좀 아쉽다.
𝜗𝜚 궁금한 내용과 부족한 내용
- 오늘의 공부 마지막 쯤에 thisBindings가 메서드나 함수에 의해 호출될때 각각 다르게 작동하는 부분을 마무리 짓지 못했는데 개념 자체를 내일 다시한번 들어보고 예제를 꼼꼼히 뜯어봐야할 것 같다.
𝜗𝜚 느낀 점
- 오늘은 공부가 참 쉽지 않았다. 내용도 많고 중간에 놓치면 처음부터 다시들어야만 이해가 가는 것들도 많았다. 내일은 좀 쉬운 내용이 나왔으면…ㅜㅜ
'TIL' 카테고리의 다른 글
[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240805 (0) | 2024.09.25 |
---|---|
[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240803 (1) | 2024.09.24 |
[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240729 (0) | 2024.09.24 |
[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240725 (1) | 2024.09.24 |
[TIL] 스파르타코딩클럽 프론트엔드 엔지니어 양성과정_240724 (0) | 2024.09.24 |