자바스크립트의 Function에는 Expression (FE) 과 Declaration (FD) 가 있다고 한다. 이에 대한 차이를 연구해 보자. 우선 존경해 맞이하는 소스니코브의 정리 내역과 구글링한 사이트를 읽고 정리한다
▶ Function Declaration
- 변수 할당 없는 이름있는 함수를 정의
- "var"로 시작하지 않고 "function"으로 시작해야 한다(must)
- 명확한 이름을 가져야 한다
- 소스코드중에 오고 function body를 가지면 다른 function 안에도 위치한다
- entering the context stage에서 생성된다 (즉, function 안에 FD 존재않음)
- variable object(VO)에 영향을 준다
function bar() {
return 3;
}
or
// 1) directly int the global context
function globalFD() {
// inside the body of another function
function innerFD() {}
}
▶ Function Expression
- 변수 할당이 있는 표현식으로서 함수를 정의
- 이름있는 또는 익명(anonymous) 함수 가능
- FE는 "function"으로 시작하지 않아야 한다(must)
- 소스중에 expression position에 정의된다
- 함수명은 optional 이다
- variable object(VO)에 저장되지 않는다
- code execution stage에서 생성된다
// anonymous function expression
var a = function() {
return 3;
}
// named function expression
var a = function bar() {
return 3;
}
//self invoking function expression or immediately invoked function expression (IIFE, 즉시실행함수)
(function sayHello() {
alert("dowon hi");
})();
// in parentheses (grouping operator) can be only an expression
(function foo() {});
// in the array initialiser - also only expressions
[function bar() {}];
// comma also operators with expressions
1, function baz() {};
// FE is not available neither before the defintion
// because it is created at code execution phase.
alert(foo); // foo is not defined
(function foo() {});
// nor after, because it is not in the VO
alert(foo); // foo is not defined
// parentheses로 grouping 하기
(function {}) ();
(function {}());
* Name Fuction Expression (NFE)
- 재귀 호출을 위해 이름으로 자기자신을 호출할 수 있다 (이때 함수를 Variable Object에 저장할 필요가 있을까? 없다!)
- 부모 context에서 생성할까? 노우!
- FE로 하면 VO에 영향을 미치지 않는다
(function foo(bar) {
if(bar) {
return;
}
foo(true); //foo name is available
})();
// but from the outside, correctly, is not
// 즉, foo는 부모 scope에 없고 function의 [[Scope]]안에 specialObject에 저장한다 (하기 설명 참조)
foo(); // foo is not defined
위 소스 해석 (원문)
- Interpreter가 code execution stage에서 FE를 만나면 FE를 생성하기 전에 special object를 만들고 현재 scope chain 앞에 놓는다
- FE 자신을 생성하고, 자신의 scope 프로퍼티에 scope chain 을 한다 (referencing)
- special object에 unique 프로퍼티로 FE를 추가한다
- 마지막에 parent scope chain으로 부터 special object를 제거 한다
specialObject = {};
Scope = specialObject + Scope;
foo = new FunctionExpression;
foo.__scope__ = Scope; // or foo.[[Scope]] = Scope;
specialObject.foo = foo; // [DontDelete], {ReadOnly}
delete Scope[0]; // remove specialObject from the front of scope chain
* FE의 특징
- Variable Object (VO)에 속하지 않다 (스크립트 수행시 Execution Context-EC- 에 한 부분 = VO)
- 따라서 FE는 Hoist안되고 FD만 Hoist 된다 (원문)
- 그리고 FE는 자신의 내부 Scope에만 의미가 있지 외부(Outside)에는 의미가 전혀 없다
- VO = Function Declaration + Variable Declaration(var) + Arguments 들이 속한다 (FD가 최우선 순위 -> VD 순서로 Hoist됨)
- FE로 하면 VO를 오염시키지 않기에 사용이 늘어나고 있다
예1)
bar(); // hoist 되어 정상 처리됨
foo(); // error 발생
function bar() { .... } // FD
var foo = function() { .... }; // FE
예2)
var myFunction = function sameFunction() {
return myFunction === sameFunction; // true
};
console.log(myFunction()); // true
console.log(sameFunction()); // [ReferenceError: contents is not defined] 에러 발생
* FE에 사용하는 Grouping Operator 와 Immediately Invoked Fuction Expression (IIFE) 특징
- Grouping Operator인 () 은 variable과 function을 global scope와 격리된 private scope로 만드는 방법을 제공한다
- IIFE는 jQuery, Underscore, Prototype, Dojo 등에서 global scope을 깔끔하게 유지키 위해 private scope로 캡슐화, 모듈화에 중요하게 사용된다
- 하기 예제는 외부세계로 부터 숨겨진 코드를 정의할 수 있고, private scope에서 동작하는 library variable을 만드는 것이다.
var Box = (function () {
var contents = [];
return {
insert: function (thing) {
contents.push(thing);
return this;
},
count: function () {
return contents.length;
}
};
})();
console.log(Box.insert('abc').insert(123).insert({
width: 640, height: 360
}).insert(function () {
return 'Hello!';
}).count()); // 4 를 출력
// This throws a ReferenceError since the JavaScript engine can't
// resolve the `contents` identifier.
// [ReferenceError: contents is not defined] 를 출력
try {
console.log(contents);
} catch(err) {
console.log(err);
}
- IIFE는 function 호출처럼 arguements를 받고, 실행시간에 in-scope에 있는 모든 데이터에 대한 access가 가능한 Closure를 반환할 수 있다 (variable 상태 저장 가능)
var d, Saved, testSubject, i, l, x;
// Capture a start time.
d = new Date().getTime();
// `Saved` exposes a single API method `get` which returns that
// start time: the variable `d` is supplied as the argument
// identified within the function definition as `start`.
// 최기의 시작시간값을 저장하고 있다
Saved = (function (start) {
return {
get: function () {
return start;
}
};
})(d);
testSubject = {
level1: {
level2: {
level3: {
level4: {
val: 'value'
}
}
}
}
};
for (i = 0, l = 1000000; i < l; i++) {
x = testSubject.level1.level2.level3.level4.val;
}
// Capture the time after diving five levels into the `testSubject`
// object literal one million times.
// 현재 시간을 구한다
d = new Date().getTime();
// Output the current value of `d`.
// 현재 시간이 찍힘
console.log(d);
// Output the original value of `d`.
// 초기 IIFE의 시간이 찍힘
console.log(Saved.get());
// Output the difference which indicates how many seconds it took
// to do that object crawl.
console.log(d - Saved.get());
//------ 결과
1352699019341 // d 현재 시간
1352699019322 // Saved.get() IIFE 시간
19 // 차이 값
<참조>
- javascript 블로그 : 간단하고 명확하게 정의내림 (표현형식만 설명)
- 소스니 코브 : 너무 심도 있게 설명해서 집중하지 않으면 놓치기 쉬움 (왜 구별해서 사용하는지 설명)
- 예제로 보는 javascript : Function Expression을 사용하는 이유를 가장 잘 설명함