✔ 메서드(동작을 나타냄)는 자신이 속한 객체의 상태, 즉 프로퍼티를 참조하고 변경 할 수 있어야 함
-> 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
✔ this
- 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수
- this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있음
- 자바스립트 엔진에 의해 암묵적으로 생성되며, 코드 어디서든 참조 가능
- this가 가리키는 값, 즉 this 바인딩(식별자와 값을 연결하는 과정)은 함수 호출 방식에 의해 동적으로 결정됨
- 객체 리터럴 예제
// 객체 리터럴 const circle ={ radius: 5; getDiameter() { // this는 메서드를 호출한 객체를 가리킴 return 2 * this.radius; } }; console.log(circle.getDiameter()); // 10
- 생성자 함수 예제
// 생성자 함수 function Circle(radius){ // this는 생성자 함수가 생성할 인스턴스를 가리킴 this.radius = radius; } Circle.prototype.getDiameter = function(){ // this는 생성자 함수가 생성할 인스턴스를 가리킴 return 2 * this.radius; }; // 인스턴스 생성 const circle = new Circle(5); console.log(circle.getDiameter()); // 10
✔ this 바인딩(this에 바인딩될 값)은 함수 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
✔ 함수를 호출하는 방식 = 일반 함수 호출, 메서드 호출, 생성자 함수 호출, Function.prototype.apply/call/bind 메서드에 의한 간접 호출
var value = 1;
const obj = {
value: 100;
foo(){
console.log("foo's this: ", this); // {value: 100, foo: f}
// 콜백 함수 내부의 this에는 전역 객체가 바인딩 됨
seTimeout(function(){
console.log("callback's this: ", this); // window
console.log("callback's this.value ", this.value); // 1
}, 100);
}
};
obj.foo();
메서드 호출
✔ 메서드 내부의 this에는 메서드를 호출한 객체, 즉 메서드를 호출할 때 메서드 이름 앞의 마침표(.) 연산자 앞에 기술한 객체가 바인딩 됨
✔ 메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩 됨
생성자 함수 호출
✔ 생성자 함수 내부의 this에는 생성자 함수가 (미래에) 생성할 인스턴스가 바인딩 됨
✔ new 연산자와 함께 생성자 함수를 호출하지 않으면 일반 함수로 동작함
Function.prototype.apply/call/bind 메서드에 의한 간접 호출
✔ apply, call, bind 메서드는 Function.prototype의 메서드이다.
✔ apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것
✔ apply - 호출할 함수의 인수를 배열로 묶어 전달
✔ call - 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달
function getThisBinding(){
console.log(arguments);
return this;
}
// this로 사용할 객체
const thisArg = { a: 1 };
// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩함
// apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달함
console.log(getThisBinding.apply(thisArg, [1,2,3]));
// Arguments(3) [1, 2, 3, callee: f, Symbol(Symbol.iterator): f]
// {a: 1}
// call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달함
cosnole.log(getThisBinding.call(thisArg, 1, 2, 3));
// Arguments(3) [1, 2, 3, callee: f, Symbol(Symbol.iterator): f]
// {a: 1}
✔ bind - 함술 호출은 하지 않고, 첫번 째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환
✔ bind 메서드는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치하는 문제를 해결하기 위해 유용하게 사용됨
const person = {
name: 'Lee',
foo(callback){
// bind 메서드로 callback 함수 내부의 this 바인딩을 전달
setTimeout(callback.bind(this), 100);
}
};
person.foo(function() {
console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee.
});
🎯 정리
함수 호출 방식 | this 바인딩 |
---|---|
일반 함수 호출 | 전역 객체 |
메서드 호출 | 메서드를 호출한 객체 |
생성자 함수 호출 | 생성자 함수가 (미래에) 생성할 인스턴스 |
Function.prototype.apply/call/bind 메서드에 의한 간접 호출 | Function.prototype.apply/call/bind 메서드에 첫번째 인수로 전달한 객체 |
✔ 실행 컨텍스트는 자바스립트의 동작 원리를 담고 있는 핵심 개념이다.
✔ ECMAScript 사양은 소스코드를 4가지 타입으로 구분 -> 4가지 타입의 소스코드는 실행 컨텍스트를 생성
✔ 4가지로 구분하는 이유는 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문
소스코드의 타입 | 설명 |
---|---|
전역 코드 | 전역에 존재하는 소스코드 전역에 정의된 함수, 클래스 등의 내부 코드는 포함되지 않는다. |
함수 코드 | 함수 내부에 존재하는 소스코드 함수 내부에 중첩된 함수, 클래스 등의 내부 코드는 포함되지 않는다. |
eval 코드 | 빌트인 전역 함수인 eval 함수에 인수로 전달되어 실행되는 소스코드 |
모듈 코드 | 모듈 내부에 존재하는 소스코드 모듈 내부의 함수, 클래스 등의 내부 코드는 포함되지 않는다. |
전역 코드
✔ 전역 코드는 전역 변수를 관리하기 위해 최상위 스코프인 전역 스코프를 생성해야 함
✔ var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 함
-> 전역 코드가 평가되면 전역 실행 컨텍스트가 생성됨
함수 코드
✔ 함수 코드는 지역 스코프를 생성하고 지역변수, 매개변수, arguments 객체를 관리해야 함
✔ 생성한 지역 스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결해야 함
-> 함수 코드가 평가되면 함수 실행 컨텍스트가 생성됨
eval 코드
✔ eval 코드는 strict mode에서 자신만의 독자적인 스코프를 생성
-> eval 코드가 평가되면 eval 실행 컨텍스트가 생성됨
모듈 코드
✔ 모듈 코든느 모듈별로 독립적인 모듈 스코프를 생성함
-> 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생성됨
✔ 자바스크립트 엔진은 소스코드를 소스코드의 평가와 소스코드의 실행으로 나누어 처리함
✔ 소스코드의 평가 과정 - 실행 컨텍스트를 생성하고 변수, 함수등의 선언문만 먼저 실행하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록
✔ 소스코드의 실행 과정(런타임) - 평가 과정이 끝나면 변수나 함수의 참조를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득하고 변수값의 변경등 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록됨
전역 코드 평가 -> 전역 코드 실행 -> 함수 코드 평가 -> 함수 코드 실행
✔ 실행 컨텍스트는 식별자(변수, 함수, 클래스등의 이름)를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리됨
✔ 식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리
✔ 코드 실행 순서는 실행 컨텍스트 스택으로 관리
✔ 실행 컨텍스트 스택 - 실행 컨텍스트는 스택 자료구조로 관리됨, 코드의 실행 순서를 관리
✔ 실행 중인 실행 컨텍스트 - 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스
const x = 1;
function foo(){
const y = 2;
functcion bar(){
console.log(x+y+z);
}
bar();
}
foo();
전역 코드의 평가와 실행 -> foo 함수 코드의 평가와 실행 -> bar 함수 코드의 평가와 실행 -> foo 함수 코드로 복귀 -> 전역 코드로 복귀
✔ 렉시컬 환경 - 식별자와 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트
✔ 렉시컬 환경은 키와 값을 갖는 객체 형태의 스코프를 생성하여 식별자를 키로 등록하고 식별자에 바인딩된 값을 관리함(저장소 역할)
✔ 실행 컨텍스트 = LexicalEnvironment 컴포넌트 + VariableEnvironment 컴포넌트로 구성
✔ 렉시컬 환경
= Environment Record(환경 레코드) + OuterLexicalEnvironmentReference(외부 렉시컬 환경에 대한 참조)로 구성
✔ 환경 레코드 - 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소(소스코드의 타입에 따라 관리하는 내용에 차이가 있음)
✔ 외부 렉시컬 환경에 대한 참조 - 외부 렉시컬 환경에 대한 참조는 상위 스코프(해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경)를 가리킴, 외부 렉시컬 환경에 대한 참조를 통해 단방향 linked list인 스코프 체인을 구현
var x = 1;
const y = 2;
function foo(a){
var x = 3;
const y = 4;
function bar(b){
const z = 5;
console.log(a+b+x+y+z);
}
bar(10);
}
foo(20); //42
✔ 전역 객체는 전역 코드가 평가되기 이전에 생성됨
✔ 전역 객체에는 빌트인 전역 프로퍼티와 빌트인 전역 함수, 그리고 표준 빌트인 객체가 추가되며 동작 환경 또는 특정 환경을 위한 호스트 객체를 포함함
- 전역 실행 컨텍스트 생성
✔ 비어있는 전역 실행 컨텍스트를 생성하여 실행 컨텍스트 스택에 푸시
- 전역 레시컬 환경 생성
✔ 전역 레시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩함2.1 전역 환경 레코드 생성
✔ 전역 레시컬 환경을 구성하는 컴포넌트인 전역환경 레코드는 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 제공
✔ 전역 환경 레코드는 객체 환경 레코드와 선언전 환경 레코드로 구성됨
✔ 객체 환경 레코드 - 기존의 전역 객체가 관리하더 var 키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 관리
✔ 선언전 환경 레코드 - let, const 키워드로 선언한 전역 변수를 관리2.1.1 객체 환경 레코드 생성
✔ 객체 환경 레코드는 BindingObjdect라고 부르는 객체와 연결됨
✔ BindingObject는 전역 객체 생성에서 생성된 전역 객체
2.1.2 선언적 환경 레코드 생성
✔ let, const 키워드로 선언한 변수는 런타임에 컨트롤이 변수 선언문에 도달하기 전까지 일시적 사각지대에 빠지기 때문에 참조할 수 없음
2.2 this 바인딩
✔ 전역 환경의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩 됨
2.3 외부 렉시컬 환경에 대한 참조 결정
✔ 외부 렉시컬 환경에 대한 참조는 현재 평가 중인 소스코드를 포함하는 외부 소스코드의 렉시컬 환경, 즉 상위 스코프를 가리킴
✔ 전역 코드가 순차적으로 실행
✔ 동일한 이름의 식별자가 다른 스코프에 여러 개 존재할 수도 있다 -> 어느 스코프의 식별자를 참조하면 되는지 결정할 필요가 있음 -> 식별자 결정
✔ 식별자 결정을 위해 식별자를 검색할 때는 실행 중인 실행 컨텍스트에서 식별자를 검색하기 시작
- 함수 실행 컨텍스트 생성
- 함수 렉시컬 환경 생성
2.1 함수 환경 레코드 생성
2.2 this 바인딩
2.3 외부 렉시컬 환경에 대한 참조 결정
- console 식별자 검색
- log 메서드 검색
- 표현식 a+b+x+y+z 의 평가
- console.log 메서드 호출
✔ var 키워드로 선언한 변수는 오로지 함수의 코드 블록만 지역 스코프로 인정하는 함수 레벨 스코프를 따른다.
✔ let, const 키워드로 선언한 변수는 모든 코드 블록(함수, if, for, while, try/catch)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.
-> 코드 블록이 반복해서 실행될 때마다 코드 블록을 위한 새로운 렉시컬 환경을 생성함