Created by 이항희 / atconsole.com (Team Blog)
JavaScript의 함수가 하는 일
Declaration
function helloWorld() { /* Implemenet... */ };
Expression
var helloWorld = function hw() { /* Implemenet... */ };
var helloWorld = function() { /* Implemenet... */ };
(function() { /* Implemenet... */ });
(function helloWorld() { /* Implemenet... */ });
JavaScript의 스코프는 보통 언어와 다른 범위
// Java
public class Scope {
private void scope() {
boolean isPass = true;
String coupang = "쿠팡!";
if(isPass) {
String costco = "코스트코";
}
System.out.println(coupang + " vs " + costco);
}
public static void main(String [] args) {
new Scope().scope();
}
}
Cannot Compile...
But JavaScript...?
함수 정의 영역에서만 범위가 결정
// JavaScript
function scope() {
var isPass = true;
var coupang = "쿠팡!";
if(isPass) {
var costco = "코스트코";
}
alert(coupang + " vs " + costco);
}
scope();
RUN
// Hmm...?
var coffee = "아메리카노";
function hoist() {
if(coffee == undefined) {
var coffee = "카페모카"
}
alert(coffee + " 사드릴게요");
};
hoist();
RUN
스코프내의 모든 변수는 실행 전 선언 (컴파일 단계)
변수는 실행되면서 할당 (엔진에 의한 코드 실행 단계)
var a = 2
실제 엔진이 실행할땐 이런식으로
// 실제 인터프리터가 해석하는 방법
var coffee = "아메리카노";
function hoist() {
var coffee;
if(coffee == undefined) {
coffee = "카페모카"
}
console.log(coffee + " 사드릴게요");
};
hoist();
JavaScript 는 이 코드를 몇단계로 나누어 생성하고 실행
컴파일러는 코드의 렉싱(lexing)을 거친 뒤 토큰된 문자열을 분석하기 시작함
var 는 선언구문이라 변수를 선언하는 코드를 생성해야 한다
먼저 a가 해당 스코프에 선언되어 있는지 판별하여 없으면 선언, 아니면 그냥 무시한다
그 다음 컴파일러는 a에 변수를 할당하는 코드를 생성한다
나머지 컴파일이 완료되고, 실행될 때가 되면 엔진은 a가 현재 스코프에서 찾을 수 있는지 확인한다
이 과정에서 스코프 체이닝이 발생. 찾아낸 변수 a에 2를 할당
체이닝 결과로 변수를 못찾으면 ReferenceError 발생
이러한 과정에서 중요한 것은 매 과정에 스코프가 깊이 관여하고 있다는 점.
함수는 객체!
JavaScript의 Function은 객체이기에 속성을 가진다.
function A() { /* ... */ }
A.coupang = "쿠팡!";
alert(A.coupang);
alert(A.toString()); // 기본으로 상속된 메서드도 있다.
RUN
객체의 속성에 접근하는 방법은 '.' 연산자로 접근
var obj = { coupang: "쿠팡!" };
obj.coupang = "쿠팡!";
alert(obj.coupang);
alert(obj.costco);
RUN
JavaScript 는 특정 변수를 찾을 때 해당 이름을 키로 객체를 뒤져 찾는다
함수 객체에만 있는 인터프리터만 사용하는 내부 속성.
함수가 "선언" 되면 인터프리터에 의해 일련의 과정을 거쳐 할당된다
// 전역에 함수를 생성.
function getById(id, prefix) {
prefix = prefix || "coupang_";
return document.getElementById(prefix + id);
}
// 다음과 같이 [[Scope]] 속성이 할당.
getById.[[Scope]] = [ {
getById: getById,
window: window,
document: window.document
/* ...기타 전역변수들... */
} ]
아직 함수는 실행되지 않았다는 사실에 주의.
단지 생성되어 있다.
함수 실행 시 이 속성을 바탕으로 변수를 식별
그런데 잘 보시면, key-value 배열 형태이다. 이 배열의 요소들을 변수 객체라고 부른다
변수 객체는 현재 접근할 수 있는 변수로 초기화된다.
getById("someElement");
함수가 실행되면 실행 컨텍스트가 생성된다.
실행 컨텍스트는 변수를 식별하기 위해 [[Scope]] 를 사용하여 스코프 체인을 초기화.
이 상태라면 아마도 이런 구조일 것이다.
[ExecutionContext::getById].ScopeChain = [ {
A: A,
window: window,
document: window.document
/* ...기타 전역변수들... */
} ]
이어서 실행 컨텍스트는 활성 객체(Activation Object)라고 부르는 객체를 하나 만든다.
이 객체는 앞서 설명한 호이스팅된 객체들이 매핑된다
추가로, this 변수와 arguments 변수도 초기화된다
이 상태라면 아마도 이런 구조일 것이다.
[ExecutionContext::getById].ScopeChain = [
{
id: "someElement",
prefix: undefined,
this: window,
arguments: [ "someElement" ]
},
{
A: A,
window: window,
document: window.document
/* ...기타 전역변수들... */
}
]
인터프리터는 변수를 만날 때마다 ScopeChain 속성에서 배열 첫번째 부터 차례대로 찾는다
function getById(id, prefix) {
prefix = prefix || "coupang_";
return document.getElementById(prefix + id);
}
[ExecutionContext::getById].ScopeChain = [
{
id: "someElement",
prefix: undefined,
this: window,
arguments: [ "someElement" ]
},
{
A: A,
window: window,
document: window.document
/* ...기타 전역변수들... */
}
]
실행 컨텍스트는 파괴되고, 생성된 활성 객체도 사라진다.
그런데 예외가 있다
바로 Closure라고 부르는 영역이 등장할 때이다.
단어의 뜻은 폐쇄.
뭘 폐쇄한다는 뜻일까요?
실행 컨텍스트와 활성 객체는 운명을 같이 .
하지만 예외가 발생할 때가 있다.
함수는 말했지만 "생성" 시 [[Scope]] 를 초기화.
[[Scope]] 는 현재 접근 가능한 변수객체로 초기화된다.
function A(val) {
function B() {
var b = "B";
return val + b;
}
return B;
}
var b = A("A");
A를 실행하면 A안이 실행되면서 B가 선언되고,
반환하고 있다.
반환된 함수의 [[Scope]] 는 어떤 구조일까요?
// b의 [[Scope]] 속성 구조.
// 뭔가 하나 늘었다...
b.[[Scope]] = [
{
B: B,
val: "A",
arguments: [ "A" ],
this: window
},
{
A: A,
window: window,
document: window.document
/* ...기타 전역변수들... */
}
]
ScopeChain 이 초기화되며 다음과 같을 것이다.
// 스코프체인의 변수객체가 3개.
[ExecutionContext::b].ScopeChain = [
{
b: "B",
arguments: [],
this: window
},
{
B: B,
val: "A",
arguments: [ "A" ],
this: window
},
{
A: A,
window: window,
document: window.document
/* ...기타 전역변수들... */
}
]
이런식으로 함수가 자신을 생성해준 스코프 체인을 유지하는 것
var foo = 'foo';
function fn7() {
return foo;
}
function fn8(fn) {
var foo = 'huk?';
alert(fn()); // ?
}
fn8(fn7);
RUN
function privateClosure(name, age) {
var myName = name;
var myage = age;
return {
profile: function() {
return myName + " / " + myage;
}
};
}
try {
var pr = privateClosure("javarouka", "26");
alert(pr.profile());
pr.myName = "이항희";
alert(pr.profile());
}
catch(e) { alert("Error! > " + e); }
RUN
Happy Coding!