language/JavaScript

4. 함수와 프로토타입 체이닝 (3)

므낫 2019. 11. 21. 16:23

4.3 함수의 다양한 형태

4.3.1 콜백 함수

자바스크립트 함수 표현식에서 함수 이름은 꼭 붙이지 않아도 되는 선택 사항. 익명 함수

이러한 익명 함수의 대표적인 용도 콜백 함수

콜백 함수는 코드를 통해 명시적으로 호출하는 함수가 아니다.

개발자는 단지 함수를 등록하기만 하고, 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출되는 함수.

혹은 특정 함수의 인자로 넘겨서, 코드 내부에서 호출되는 함수.


대표적인 콜백 함수의 사용 예가 자바스크립트에서의 이벤트 핸들러 처리

웹 페이지가 로드되거나 키보드가 입력되는 등의 DOM 이벤트가 발생할 경우, 브라우저는 정의된 DOM 이벤트에 해당하는 이벤트 핸들러 실행. if 이러한 이벤트 핸들러에 콜백 함수가 등록되어 있다면, 콜백 함수는 이벤트가 발생할 때마다 브라우저에 의해 실행됨.


예제 4-15 window.onload 이벤트 핸들러 예제 코드

// 페이지 로드 시 호출될 콜백 함수
window.onload = function() {
    alert('This is the callback function.');
};

 

  • window.onload는 이벤트 핸들러
  • 웹 페이지의 로딩이 끝나는 시점에 load 이벤트가 발생하면 실행됨

4.3.2 즉시 실행 함수

immediate functions

함수를 정의함과 동시에 바로 실행하는 함수

익명 함수를 응용한 형태


예제 4-16 즉시 실행 함수 예제 코드

(function (name){
    console.log('This is the immediate function -> ' + name);
})('foo');

 

즉시 실행 함수를 만드는 방법

  1. 함수 리터럴을 괄호()로 둘러싼다. 이때 함수 이름이 있든 없든 상관없다.
  2. 함수가 바로 호출될 수 있게 () 괄호 쌍을 추가한다.
  3. 이때 괄호 안에 값을 추가해 즉시 실행 함수의 인자로 넘길 수 있다.

예제에서는 ('foo')로 즉시 실행 함수를 호출했으며, 이때 ‘foo’를 인자로 넘겼다. 이 값은 앞 예제 즉시 실행 함수의 name 매개변수로 넘겨지게 된다.


즉시 실행 함수의 경우, 같은 함수를 다시 호출할 수 없다.

최초 한 번의 실행만을 필요로 하는 초기화 코드 부분 등에 사용 가능


jQuery와 같은 자바스크립트 라이브러리나 프레임와크 소스들에서도 사용됨

jQuery 소스의 시작 부분과 끝 부분은 다음 예제와 같이 즉시 실행 함수 형태로 구성되어 있음. 즉, jQuery 소스 코드 전체가 즉시 실행 함수로 감싸여 있다.


예제 4-17 jQuery에서 사용된 즉시 실행 함수

(function( window, undefined ) {
...
})( window );

 

jQuery에서 즉시 실행 함수를 사용하는 이유 자바스크립트의 변수 유효 범위 특성 때문

자바스크립트에서는 함수 유효 범위를 지원.

기본적으로 자바스크립트는 변수를 선언할 경우 프로그램 전체에서 접근할 수 있는 전역 유효 범위를 가지게 됨. 그러나 함수 내부에서 정의된 매개변수와 변수들은 함수 코드 내부에서만 유효 (여기서 변수들은 var 문을 사용해서 정의해야 한다. 그렇지 않으면 함수 내 변수라도 전역 유효 범위를 갖게 됨) 함수 외부의 코드에서 함수 내부의 변수를 액세스하는 게 불가능 (5장에서 더 자세히)


라이브러리 코드를 즉시 실행 함수 내부에 정의해두게 되면, 라이브러리 내의 변수들은 함수 외부에서 접근할 수 없다. 따라서 이렇게 즉시 실행 함수 내에 라이브러리 코드를 추가하면 전역 네임스페이스를 더럽히지 않으므로 이후 다른 자바스크립트 라이브러리들이 동시에 로드되더라도 라이브러리 간 변수 이름 충돌 같은 문제 방지 가능


즉시 실행 함수 패턴

jQuery 외에도 대부분의 라이브러리가 즉시 실행 함수 패턴으로 감싸져 있음.


[ Underscore 1.3.3 ]

(function() {
	var root = this;
	var previousUnderscore = root._;
	...
	var _ = function(obj) { return new wrapper(obj)l };
	...
	root['_'] = _;
	...
}).call(this);

 

Underscore 1.3.3은 call 함수를 this 인자와 함께 사용. 이렇게 넘긴 this가 즉시 실행 함수 내부의 this에 바인딩된다. 물론 이것(this)도 전역 객체. 함수 내부에서 root라는 이름으로 사용된다. 그리고 root_를 추가한다. underscore는 브라우저뿐만 아니라, node.js에서도 사용이 가능하게 설계되었다.


[ Sugar 1.2 ]

(function() {
	...
	// Initialize
	buildObject();
	buildString();
	buildFunction();
	initializeClass(date);
})();

 

Sugar 1.2는 특별한 인자 없이 즉시 실행 함수를 호출.

sugar에서 제공하는 대부분의 함수는 Object.prototype이나 Function.prototype 등 기존에 있는 객체에 들어가므로, 특별히 네임스페이스를 정의하지 않음.


이와 같이 라이브러리 코드가 처음 로드되어 초기화될 때, 즉시 실행 함수 패턴이 많이 사용됨


4.3.3 내부 함수

자바스크립트에서는 함수 코드 내부에서도 다시 함수 정의가 가능.

내부 함수(inner function)


자바스크립트의 기능을 보다 강력하게 해주는 클로저를 생성하거나 부모 함수 코드에서 외부에서의 접근을 막고 독립적인 헬퍼 함수를 구현하는 용도 등으로 사용. (클로저의 자세한 내용은 5장에서)


예제 4-18 내부 함수 예제 코드

// parent() 함수 정의
function parent() {
    var a = 100;
    var b = 200;

    // child() 내부 함수 정의
    function child() {
        var b = 300;

        console.log(a); // 100
        console.log(b); // 300
    }

    child();
}

parent();
child();    // Uncaught ReferenceError: child is not defined

 

내부 함수에서는 자신을 둘러싼 부모 함수의 변수에 접근이 가능하다.
  • child() 내부 함수에 변수 a가 선언되지 않았음에도 child() 함수가 호출됐을 때 값 100이 출력됨. parent() 함수의 변수 a 값에 접근하여 출력한 것
  • 변수 bchild() 함수에 선언되어 있으므로 parent() 함수의 b 변수가 아닌 child() 함수의 변수 b 값이 바로 출력됨

자바스크립트의 스코프 체이닝 때문에 가능. (5장에서 더 자세히 다룸)


내부 함수는 일반적으로 자신이 정의된 부모 함수 내부에서만 호출이 가능하다.
  • 앞 예제에서는 parent() 함수 외부에서는 child() 함수 호출을 시도하지만, 함수가 정의되어 있지 않다는 에러 발생. 자바스크립트의 함수 스코핑 때문. 즉, 함수 내부에 선언된 변수는 함수 외부에서 접근이 불가. 내부 함수도 그대로 적용되는 규칙.
  • 반면에 부모 함수 안에서는 child() 내부 함수를 호출하는 것이 가능. 내부 함수를 호출하는 부분과 내부 함수가 정의된 부분이 모두 부모 함수 내부에 있기 때문.

하지만 함수 외부에서도 특정 함수 스코프 안에 선언된 내부 함수를 호출하는 것이 가능할 때가 있다.

예를 들어, 부모 함수에서 내부 함수를 외부로 리턴하면 부모 함수 밖에서도 내부 함수를 호출하는 것이 가능.


예제 4-19 함수 스코프 외부에서 내부 함수 호출하는 예제 코드

function parent() {
    var a = 100;
    // child() 내부 함수
    var child = function () {
        console.log(a);
    }
    // child() 함수 반환
    return child;
}

var inner = parent();
inner();

 

  • parent() 함수가 호출되면, inner 변수에 child 함수 변수 값이 리턴됨.
  • child 함수 변수는 내부 함수의 참조값을 가지므로, 결국 inner 변수도 child() 내부 함수를 참조함.

이와 같이 실행이 끝난 parent()와 같은 부모 함수 스코프의 변수를 참조하는 inner()와 같은 함수를 클로저라고 함. (5장에서 더 자세히 설명)


4.3.4 함수를 리턴하는 함수

자바스크립트에서는 함수도 일급 객체이므로 일반 값처럼 함수 자체를 리턴할 수 있다.

함수를 호출함과 동시에 다른 함수로 바꾸거나, 자기 자신을 재정의하는 함수를 구현 가능. (자바스크립트의 언어적인 유연성을 보여주는 좋은 활용 예)


예제 4-20 자신을 재정의하는 함수 예제 코드

// self() 함수
var self = function () {
    console.log('a')
    return function () {
        console.log('b')
    }
};
self = self();  // a
self();         // b

 

  • 처음 self() 함수가 호출됐을 때는 ‘a’가 출력된다. 그리고 다시 self 함수 변수에 self() 함수 호출 리턴값으로 내보낸 함수가 저장됨.
  • 두 번째로 self() 함수가 호출됐을 때는 ‘b’가 출력됨.
  • 즉, 처음 self() 함수 호출 후에, self 함수 변수가 가리키는 함수가 원래 함수에서 리턴받은 새로운 함수로 변경된 것.

#책/인사이드자바스크립트