익명함수 (Anonymous Function)
TypeScript는 (JavaScript와 같이) 이름이 있는 일반 함수(named function)와 함수명이 없는 익명함수(anonymous function)를 지원한다. 일반적으로 함수를 외부에서 호출하거나 여러번 사용할 경우 Named function을 사용하고, 함수를 한번만 사용하는 일회성의 함수에는 익명함수(anonymous function)를 사용한다.
익명함수는 함수의 기본구조에서 함수명만을 삭제한 함수인데, 이러한 익명함수는 통상 일회성 콜백함수에 사용하거나 익명함수 전체를 변수에 할당하여 사용한다. 아래 예제에서 setInterval() 함수 안에 있는 function은 함수명을 가지고 있지 않은데, 이것이 익명함수이다. 익명함수는 또한 함수전체를 변수에 할당하여 함수 타입으로 사용할 수 있는데, 아래의 squareFunc가 그 예이다.
// named function function say() { console.log("Say Hello"); } // anonymous function setInterval(function(){ console.log("Hello"); }, 1000); // anonymous function let squareFunc = function(n: number) { return n * n; }
Arrow Function
Arrow Function은 익명함수를 보다 간략하게 표현한 것으로 입력파라미터와 함수본문을 => (fat arrow라고 불리움) 을 분리해서 표현한 함수이다. C#에서는 람다식(lambda expression)으로 불리우며, 타 언어에서도 종종 람다함수로 불리운다. 아래 예제는 익명함수를 Arrow 함수로 변환한 예이다.
// anonymous function let add = function (a, b) { return a + b } // arrow function let addFn = (a, b) => a + b; console.log(addFn(2,3));
아래는 익명함수를 Arrow 함수로 변환하는 방법을 표현한 것인데, Arrow 함수로 function 키워드를 생략하고, => 좌측에 입력파라미터를 나열한다. 입력파라미터가 0개 혹은 2개 이상이면 괄호 (...) 안에 입력 파라미터를 넣고, 입력파라미터가 1개이면 괄호를 생략할 수 있다. => 우측에는 함수본문들을 적는데, 문장이 2개 이상이면 괄호 {...} 안에 문장을 나열한다. 함수 문장이 1개이면 괄호 {} 를 생략할 수 있다. arrow function은 기본적으로 값을 리턴하는 것을 전제하므로 마지막 문장에 return을 생략할 수 있다. 즉, 위 예제(addFn)에서와 같이 => 우측에 "return a + b" 대신 "a + b" 처럼 return 을 생략할 수 있다.
TypeScript에서 함수타입을 정의할 때, Arrow 함수를 사용하여 함수 원형을 정의할 수 있다. 예를 들어, 아래 예제는 2개의 숫자를 받아들여 어떤 계산을 한 후 숫자를 리턴하는 함수타입을 변수 calc에 할당한 예이다.
let calc: (a: number, b: number) => number; calc = function(a, b) { return a + b; } console.log(calc(1,2)); // 3 calc = (x, y) => x - y; console.log(calc(1,2)); // -1
TypeScript에서 Arrow 함수는 클래스의 속성에 할당되어 사용될 수 있다. 아래 예제는 print 속성에 Arrow 함수를 할당하여 사용한 예이다.
class Person { name: string; age: number; print = () => console.log(this.name + "," + this.age); } let p = new Person(); p.name = "Tom"; p.age = 25; p.print();
Arrow 함수는 그 함수 내부에서 외부 변수를 사용하게 되면, 외부 변수를 캡쳐(capture)하여 자신의 함수 안에 포함하게 된다. 즉, 외부변수의 상태를 Arrow 함수 내부에서 계속 사용할 수 있도록 그 컨텍스트를 가지고 있게 된다.
예를 들어, 아래 예제를 살펴보면, counterFunction 속성은 익명함수를 할당받는데, 그 함수는 내부 함수를 리턴하고 있다. 이때, 내부 함수는 cnt 라는 속성을 포함하고 있으며, 이 카운트를 계속 증가시킨 후, 카운트 값을 리턴하게 된다. 하지만, 이 예제를 실행하면, 증가되는 숫자가 아닌 NaN를 출력하게 된다. 이는 counter()가 실행될 때, 내부함수에 있는 this 값이 CounterClass 객체를 가리키는 것이 아니라 window 객체를 가리키기 때문이다. JavaScript에서 this는 그 함수가 실행되는 싯점에 전달되는데, Top 레벨의 함수의 경우 this는 window로 설정된다. 아래 예제의 내부함수는 객체에 종속되지 않은 Top 레벨 함수로서 this는 window로 설정되고, 이 컨텍스트에서는 this.cnt 속성을 갖지 않는다.
class CounterClass { cnt: number = 0; counterFunction = function() { return function() { this.cnt++; return this.cnt; } } } let c = new CounterClass(); let counter = c.counterFunction(); let n = counter(); console.log(n); // "NaN" 출력
위와 같은 문제는 아래와 같이 Arrow 함수를 써서 해결할 수 있다. 즉, counterFunction의 내부 함수를 익명함수에서 Arrow 함수로 변경함으로해서 cnt 속성을 캡쳐하게 되어 this.cnt를 실행 컨텍스트에서 가지고 있게 된다.
class CounterClass { cnt: 0; counterFunction = function() { return () => { this.cnt++; return this.cnt; } } }