블로그 이미지
윤영식
Frontend Application Architecter, Full Stacker, KnowHow Dispenser and Bike Rider

Publication

Statistics Graph

Recent Comment

2012.11.12 11:34 Languages/JavaScript

자바스크립트의 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을 사용하는 이유를 가장 잘 설명함

저작자 표시 비영리 변경 금지
신고
posted by peter yun 윤영식