실행 컨텍스트(Execution Context) : scope,hoistion,this,function,closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리이다. 실행 컨텍스트를 바로 이해하지 못하면 코드 독해가 어려워지며 디버깅도 매우 곤란해 질 것이다.
ECMAScript 스펙에 따르면 실행 컨텍스트를 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라고 정의한다. 좀 더 쉽게 말하자면 실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경이라고 말할 수 있다. 여기서 말하는 실행 가능한 코드는 아래와 같다.
일반적으로 실행 가능한 코드는 전역 코드와 함수 내 코드이다.
자바스크립트 엔진은 코드를 실행하기 위하여 실행에 필요한 여러가지 정보를 알고 있어야 한다.
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
실행 컨텍스트 스택(Stack)이 생성하고 소멸한다. 현재 실행 중인 컨텍스트에서 이 컨텍스트와 관련없는 코드(예를 들어 다른 함수)가 실행되면 새로운 컨텍스트가 생성된다. 이 컨텍스트는 스택에 쌓이게 되고 컨트롤(제어권)이 이동한다.
컨트롤이 실행 가능한 코드로 이동하면 논리적 스택 구조를 가지는 새로운 실행 컨텍스트 스택이 생성된다. 스택은 LIFO(Last In First Out, 후입 선출)의 구조를 가지는 나열 구조이다.
전역 코드(Global code)로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다. 전역 실행 컨텍스트는 애플리케이션이 종료될 때(웹 페이지에서 나가거나 브라우저를 닫을 때)까지 유지된다.
함수를 호출하면 해당 함수의 실행 컨텍스트가 생성되며 직전에 실행된 코드 블록의 실행 컨텍스트 위에 쌓인다.
함수 실행이 끝나면 해당 함수의 실행 컨텍스트를 파기하고 직전의 실행 컨텍스트에 컨트롤을 반환한다.
호이스팅 : 변수와 함수 선언이 최상단으로 올려지는 현상
console.log(a);
func();
function func() {
console.log('함수실행');
}
var a = '변수';
이 경우, 함수실행은 제대로 작동하고, 변수는 초기화가 아직 되지않은 값이라 undefined가 출력된다.
비슷한 경우로, 함수 표현식 (함수를 변수에 담하서 그것을 실행하는 경우) 으로 작성하면, 호이스팅으로 인해 선언만 먼저 되므로, 에러가 뜬다.
클로저 : 내부함수가 외부함수의 지역변수에 접근할 수 있는데, 외부함수의 실행이 끝나서 컨텍스트가 소멸이 된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있는 매커니즘.
function outter(){
var title = 'coding everybody';
return function(){
alert(title);
}
}
inner = outter();
inner();
이 예제에서 outter함수가 inner 에 초기화되고 컨텍스트가 끝났음에도 inner() 호출이 되었을 때 outter함수의 title변수가 사라지는 것이 아니라 내부 함수에 의해 불러와지고 잘 실행이 된다.
장점 : 이런 방식으로 내부함수에서는 변수를 활용할 수 있지만 외부함수에서는 참조하지 못하게 해서 변수조작이나 변수의 중복을 막을 수 있는 특징이 있다.
그래서, 라이브러리에 많이 사용되는 방식!
JAVA의 경우, this는 클래스의 인스턴스의 레퍼런스 변수를 의미하지만,
Javascript의 경우, this는 현재 실행 문맥에서의 호출자를 의미한다.
var value = "전역 값";
var obj = {
value: "함수 내부 값",
Outer: function() {
console.log(`${this.value}`); // "함수 내부 값" : 현재실행 호출자는 obj (why? obj 객체 내 메소드를 실행하였기 때문)
var Inner = function() {
console.log(`${this.value}`); // "전역 값" : 현재실행 호출자는 전역 (why? 이 때 function은 선언됨에 따라 호이스팅 되며, 전역 value를 참조)
};
Inner();
}
};
obj.Outer();
function.call(obj)의 형태로 쓰이는 데, 앞의 함수를 실행하는데 obj를 this로 사용한다는 것을 뜻한다.
var value = "전역 값";
var obj = {
value: "함수 내부 값",
Outer: function() {
console.log(`${this.value}`); // "함수 내부 값" : 현재실행 호출자는 obj (why? obj 객체 내 메소드를 실행하였기 때문)
var Inner = function() {
console.log(`${this.value}`); // "함수 내부 값" : call을 통해 obj 내부value를 불러옴
};
Inner.call(this);
}
};
obj.Outer();
call() : function.call(this,1,2,3)과 같이 함수의 파라미터를 값으로 전달
apply() : function.apply(this,[1,2,3])과 같이 함수의 파리미터를 배열로 전달.
bind()는 호출하지 않고 함수에 this만 전달해주는 역할
var obj1 = {
value: "obj1값",
alert1: function() {
console.log(this.value);
}
};
var obj2 = {
value: 'obj2값'
};
var bindPractice = obj1.alert1.bind(obj2);
bindPractice(); // "obj2값"
this.handler = this.handler.bind(this)
에서 앞의 this
는 window를 뒤의 this
는 해당 컴포넌트를 의미한다.
class App extends React.Component {
constructor() {
super();
this.handler = this.handler.bind(this); // bind 해줘야만 컴포넌트의 this의 state를 수정할 수 있음
}
handler() {
this.setState({
// ...
});
}
render() {
return <div
onClick={ this.handler }
/>;
}
}
화살표함수를 사용하면 자동 binding이 된다
handler = () => {
this.setState({
// ...
});