04 함수와 메서드

YoungMinKim·2021년 4월 28일
0
post-thumbnail

04 함수와 메서드

Goal

자바스크립트에서 함수는 function 키워드와 화살표 ⇒ 기호로 만드는
두 가지 방법이 있습니다. 타입스크립트는 이를 바탕으로 타입 기능을 추가한 것입니다.
이번 장은 함수를 효과적으로 구현하는 방법과 클래스의 메서드를 구현하는 방법을 설명합니다.

04-1 함수 선언문

자바스크립트에서 함수는 function 키워드로 만드는 함수와, ⇒ 기호로 만드는 화살표 함수
두 가지가 있다. 아래는 function 키워드로 만드는 함수 선언문의 구조이다.

함수 선언문 예제

function 함수 이름(매개변수1, 매개변수2[, ....]) {
	함수 몸통
}

타입스크립트 함수 선언문은 자바스크립트 함수 선언문에서 매개변수와 함수 반환값에
타입 주석을 붙이는 다음 형태로 구성이 된다.

function 함수 이름(매개변수1, 매개변수2[, ....]) : 반환값 타입 {
	함수 몸통
}

마지막으로 타입스크립트 함수 선언문의 예를 아래 코드를 통해 확인해보자.

function add(a: number, b: number) : number {
	return a + b;
}

매개변수와 인자

  • parameter : 매개변수
  • argument : 인수, 인자

기본적으로 parameter(매개변수)는 함수 선언문에서 함수 이름 뒤 괄호 안에 선언하는 변수이고,
argument(인수, 인자)는 함수를 호출할때 전달하는 값이다.

매개변수와 반환값의 타입 주석 생략

변수때와 마찬가지로 함수 선언문에서도 매개변수와 반환값에 대한 타입 주석을 생략할 수 있지만,
타입스크립트에서는 위 같이 타입 주석을 생략 하는것을 권장하지 않는다.

함수 시그니처

변수에 타입이 있듯이, 함수 또한 타입이 존재한다.
함수에 타입을 붙이는것을 함수 시그니처라 한다.

(매개변수1 타입, 매개변수2 타입[, ....]) => 반환 타입

함수 시그니처 사용 예

let printMe : (string, number) => void = function(name: string, age: number) : void {}

type 키워드로 타입 별칭 만들기

타입스크립트는 type이라는 키워드를 제공한다. type 키워드는 기존에 존재하는 타입을
단순히 이름만 바꿔서 사용할 수 있게 해준다.

type 새로운 타입 = 기존 타입

다음 코드는 함수 시그니처를 생성해 type 별칭에 넣는 예제를 나타내고 있다.

type stringNumberFunc = (string, number) => void
let f : stringNumberFunc = function(a: string, b: number) : void {}
let g : stringNumberFunc = function(c: string, d: number) : void {} 

함수 시그니처를 사용하는 이유는, 해당 변수의 타입을 강하게 지정함으로써
미연에 RunTimeException을 방지할 수 있기 때문이다.

04-2 함수 표현식

함수는 객체다?

자바시크립트는 함수형 언어 스킴과 프로토타입 기반 객체지향 언어 셀프를 모델로 만들어졌다.
따라서 자바스크립트는 객체지향 언어와 함수형 언어의 특징이 모두 있다.
또한 자바스크립트에서 함수는 Function 클래스의 인스턴스다.

let add = new Function('a', 'b', 'return a + b');
let result = add(1, 2);
console.log(result);

변수에 함수를 선언한다? 함수는 선언해두고 호출해서 사용 하는게 아닌가?? (이런 생각을 할수도 있지..음 내 생각은 아님)

뒤에서 설명하겠지만, 자바스크립트(타입스크립트)는 일등 함수를 지원하기에 변수에 함수를 넣을 수 있다.

// 함수 표현식
let add2 = function(a, b) {return a + b};

// 함수 표현식을 화살표 함수로 바꿔보자
let add2 = (a, b) => {return a + b}; // 실행문 방식
let add2 = (a, b) => a + b; // 표현식 문 방식

이처럼 함수 선언문에서 함수 이름을 제외한 function(a, b) {return a + b}와 같은 코드를
함수 표현식이라고 한다. 함수 표현식은 함수형 언어의 핵심 기능이기에 일등함수 라는 용어를 시작으로
함수 표현식이 무엇인지 살펴보자.

함수 표현식과 함수 선언문의 다른점

foo();		// foo 함수 호출 가능
boo();		// boo 함수 호출 불가능

function foo() {
  console.log('foo 호출');
}

var boo = function() {
  console.log('boo 호출');
};

var boo;	// boo 변수에 할당된 값이 없음
foo();		// foo 함수 호출 가능
boo();		// boo 함수 호출 불가능

var boo = function() {
  console.log('boo 호출');
};
  • 함수 선언은 스코프의 최상단으로 호이스팅 된다.
  • 반면 함수 표현식은 호이스팅 되지 않는다.
  • 따라서 함수 표현식으로 선언된 함수는 함수 표현식을 선언하기 전에는 함수를 호출할 수 없다.

일등 함수

프로그래밍 언어가 일등 함수 기능을 제공하면 함수형 프로그래밍 언어라 한다.
자바스크립트, 타입스크립트는 일등함수 기능을 가지고 있기에 함수형 프로그래밍 언어다.
여기서 일등 함수 란 함수와 변수를 구분하지 않겠다는 의미다.

함수와 변수를 구분하지 않겠다는말이 무슨 의미인가?
아래 예제를 보면서 일등 함수가 어떤건지 생각해보자.

// 함수 표현식 예제
let point = fucntion(a: number, b: number) : number { return a + b};

다음과 같이 함수지만 이름이 없는 것을 함수 표현식 이라고 한다.
이런 함수 표현식은 하나의 변수에 할당이 될 수 있다.

위 같이 변수에 함수를 할당하는게 Java를 하는 입장에서 말이 안되는 부분이지만
타입스크립트는 일등 함수를 제공하기 때문에 변수에 함수를 할당 할수있다.

그렇다면 여기서 point는 변수인가? 함수인가?

  • point가 변수가 될수도 있고, 함수가 될수도 있다.
  • 이런 특징이 일등 함수를 제공하는 함수형 프로그래밍 언어의 특징이다.

표현식

프로그래밍 언어 관점에서 표현식이란 리터럴, 연산자, 변수, 함수 호출, 등이 복합적으로
구성된 코드 형태를 의미한다. 아래 예시를 통해 표현식이 어떤건지 간단히 알아보자.

let num : number = 1 + 2;

1 + 2는 1과 2라는 리터럴과 덧셈 연산자 +로 구성된 표현식이다.
표현식은 항상 컴파일러에 의해 계산법(evaluation)이 적용되어 어떤 값이 된다.
예를 들어, 표현식 1 + 2는 컴파일러에 의해 3이라는 값이 된다.

위에서 설명한 부분을 다시 한번 말해보자면, 표현식은 리터럴, 연산자, 변수, 함수 호출 등이
표현식이라 할 수 있는데, 이러한 표현식은 컴파일러의 계산법에 의해 계산이 된다.
그렇다면 컴파일러의 계산법 종류에는 무엇이 있는지 알아보자.

컴파일러의 계산법

  • 조급한 계산법
  • 느긋한 계산법

우선 1 + 2로 구성된 표현식을 컴파일러가 만나게 되면 컴파일러는 조급한 계산법을 수행하여 해당 구성 표현식을 처리한다.

하지만 1 + 2와 같은 표현식이 아닌 함수 표현식을 컴파일러가 만나게 되면 컴파일러는 해당 함수 표현식의
매개변수 function(a, b)가 어떤 값인지 알 수 없기때문에 느긋한 계산법을 수행하게 된다.

위 같이 컴파일러가 함수 표현식을 만났을때 느긋한 계산법을 수행하기 때문에 타입스크립에서의
콜백(callBack)기능 구현이 가능한 것이다.

느긋한 계산법 흐름

/**
    @date 21.02.27
    @title callBack
 */

type callbackFn = (a: number, b:number) => number;
const sample : callbackFn = (a, b) => {return a + b};
const sample2 : (callback : (a:number, b:number) => number, b: number) 
										=> number = (cb: callbackFn, b: number) => {
    var callBackResult = cb(1, 2);
    let t1 : number;
    if(callBackResult > 1) {
        t1  =  1 + callBackResult;
        return t1;
    }
    return 2;
 }

let result: number = sample2(sample, 2);
console.log("result : " + result);

함수 호출 연산자

어떤 변수가 함수 표현식을 담고 있다면, 변수 이름 뒤에 함수 호출 연산자(function call operator)
()를 붙여서 호출할 수 있습니다.

let functionExpression = function(a, b) {return a + b}; // 변수
let value = functionExpression(1, 2);                   // 함수호출, 결과값 : 3

위 코드에서 중요한 부분은 함수 선언식을 변수에 넣은 뒤 호출하는게 중요한 것이 아니라,
functionExpression(1, 2)형태로 함수가 호출되면 컴파일러가 functionExpression 변수에 저장된
함수 표현식을 끄집어 낸 뒤 조급한 계산법을 적용한다는 점이 중요한 부분이다.

즉, 위와같이 컴파일러가 느긋한 계산법을 수행하고 해당 함수 호출 시 조급한 계산법을 수행하기 때문에
해당 함수 호출이 필요할 시 들고 다니다가 호출을 할수있는것이다.

익명 함수

함수 표현식은 사실 대부분 언어에서 언급되면 익명 함수의 다른 표현이다.

// 익명함수 
let value = (function(a, b) {return a + b}) (1, 2);

// 컴파일러는 두번째 function문을 보고 느긋한 계산법을 수행한다.
// 후에 세번째 줄에 함수 호출문 (1, 2)를 보고 조급한 계산법을 수행한다.
let value = 
(function(a, b) {return a + b})
(1, 2);

위같이 중괄호를 선언한 이유는 함수 호출 연산자는 연산자 우선순위가 매우 높으므로,
표현식 부분을 소괄호로 묶어서 컴파일러가 정상적으로 함수 표현식의 시작과 끝 부분을 알 수 있도록 해야 한다.

04-3 화살표 함수와 표현식 문

ESNext 자바스크립트와 타입스크립트는 function 키워드가 아닌 ⇒ 기호로 만드는 화살표 함수도 제공한다.

const 함수 이름 = (매개변수: 타입1, 매개변수2: 타입2) : 반환 타입 => 함수 몸통

그런데 화살표 함수의 몸통은 function 때와는 다르게 다음처럼 중괄호를 사용할 수도 있고 생략할 수도 있다.

const arrow1 = (a: number, b: number) : number => { return a + b } // 실행문 방식 몸통
const arrow1 = (a: number, b: number) : number => a + b            // 표현식 문 방식 몸통

실행문과 표현식 문

오래전부터 프로그래밍 언어는 실행문 지향 언어표현식 지향 언어로 구분되어 왔다.
ES5는 실행문 지향 언어지만, ESNext타입스크립트는 실행문표현식 문을 동시에 제공하는
다중 패러다임 언어다.

대표적인 실행문 지향 언어

  • C언어

대표적인 표현식 지향 언어

  • 스칼라(scala)

실행문 특징

프로그래밍 언어에서 실행문CPU에서 실행되는 코드를 의미한다. 하지만 실행문은 CPU에서 실행만 될뿐
결과를 알려주지 않는다. 실행문이 실행된 결과를 알려면 반드시 return 키워드를 사용해야 한다.

표현식 문 특징

return 키워드 없이도 결과값을 반환하는 실행문표현식 문
표현식 문은 CPU에서 실행될뿐만 아니라, return 키워드가 없어도 결과를 알려준다.

화살표 함수는 무엇이 다른가요?

const func1 = function() { 
	const num = 10; 
};

const func1 = () => { const num = 10; }; // function 키워드 생략 가능

const func2 = function(num) { 
  for(let i = 0; i < 10; i++) { 
		num++; 
	}
  return num;
};

const func2 = num => { // 함수의 매개변수에 괄호 생략 가능
  for(let i = 0; i < 10; i++) { 
		num++; 
	}
  return num;
};

const func3 = function (num) { return `입력된 숫자는 ${num}입니다.`; };
const func3 = num => `입력된 숫자는 ${num}입니다.`; // 중괄호와 return 문 생략 가능
  • function 키워드를 생략할 수 있다.
  • 함수의 매개변수가 1개라면 괄호() 생략 가능.
  • 함수 바디의 표현식이 하나라면 중괄호와 return 문을 생략할 수 있습니다.

실행문 표현식 문 질문

실행문은 CPU에서 실행이 되지만 return 키워드가 없을 시 값을 반환해주지 않지만,
표현식 문은 CPU에서 실행됨은 물론 return 키워드 없이도 결과값을 반환 해준다.

표현식 문은 위에서 말했다시피 return 키워드가 없이도 결과값을 반환하는 실행문이라 지칭하는데,
내가 알고 싶은 것은 코드 레벨에서 정확하게 어떻게 구분이 되는지 알고 싶다.

// 이렇게 해놓고 이건 실행문이다. 🤬
let x
x = 1
// 이렇게 해놓고 이건 return 값이 없이 결과값을 반환하는 실행문이다? 🤬
let x = 10
if(x > 0)
	x = 1

복합 실행문

IF와 같은 구문은 다음처럼 조건을 만족하면 단순히 한 줄의 실행문만 실행하는 형태로 설계한다.

if(조건식) 
	실행문

위 같은 설계가 가능한 이유는 복합 실행문이라는 또 다른 형태를 제공하기 때문이다.
복합 실행문은 컴파일러로 하여금 여러 실행문한 개처럼 인식하게 한다.

if(조건식) {
	실행문1
	실행문2
}

표현식과 문

표현식(expression)의 역할값을 생성하는 것이다. (statement)의 역할표현식으로 생성한 값을 사용해
자바스크립트 엔진에게 명령을 내리는 것이다.

04-4 일등 함수 살펴보기

콜백 함수

  • 매개변수 형태로 동작하는 함수
  • 함수의 래퍼런스 주소값을 전달
  • 전달해주는 함수를 전달 받는 함수측에서 호출
  • 콜백은 동기 / 비동기에서 둘 다 사용이 된다

아래 예제를 통해 콜백 함수에 대한 부분을 알아보자.

// callback은 매개변수를 받지 않으며 반환형은 void 타입인 함수가 넘어온다.
const f = (callback: () => void) : void => callback()

아래 코드는 조금 더 현실적인 콜백 함수의 표현식이다.

export const init = (callback: () => void) : void => {
	console.log('default initialization finished');
	callback();
	console.log('all initialization finished');
}

아래 코드는 위 코드를 호출하는 호출문이다.

import {init} from './init'

// () => console.log('중략') 이 부분이 인자로 넘어간다
init( () => console.log('custom initialization finished') );

프레임워크 API 구현에 유용한 콜백 함수

프로그램의 전체 구조를 쉽게 작성할 수 있게 설계된 라이브러리를 보통 프레임워크라 한다.
프레임워크는 여러 프로그램이 공통으로 구현해야 할 함수를 API라는 이름으로 제공한다.
그런데 API는 프로그램마다 새로운 내용을 추가로 구현할 수 있게 지원해야 하는데, 이러한
면에서 콜백 함수는 프레임워크의 API 구현에 매우 유용하다.

중첩 함수

쉽게 말해 함수 안에 함수를 또 선언하는 것이다.

const calc = (value : number, cb: (number) => void) : void => {
	let add = (a, b) => a + b;
	function multifly(a, b) {return a * b};
	
	let result = multifly(add(1, 2), value);
	cb(result); 
}

calc(30, (result: number) => console.log(`result is ${result}`));

고차 함수와 클로저, 그리고 부분 함수

고차 함수또 다른 함수를 반환하는 함수를 말한다.

함수형 언어에서 함수는 단순히 함수 표현식이라는 값이므로 다른 함수 반환이 가능하다.

const add1 = (a: number, b: number) : number => a + b;
const add1 = (a: number) : (number) => number => (b: number) => number => a + b // 고차함수

위 부분에서 헷갈렸던 부분은 하이라이트를 걸어두었다.

고차 함수는 생각보다 난해한 부분이 많아서 나머지는 책을 통해 참고 할거임.

export type NumberToNumberFunc = (number) => number;
export const add = (a: number) : NumberToNumberFunc => {
	const _add : NumberToNumberFunc = (b: number) : number => {
		return a + b; // 클로저
	}
	return _add;
}
import {NumberToNumberFunc, add} from './add'

let fn : NumberToNumberFunc = add(1);

let result = fn(2);
console.log(result);    // 3
console.log(add(1)(2)); // 3

04-5 함수 구현 기법

매개변수 기본값 지정하기

Spring Controller에서 defaultValue를 사용하는데, 이것과 동일한 맥락이라 생각하면 된다.
즉, 디폴트 매개변수라 하고 아래와 같이 사용이 된다.

(매개변수: 타입 = 매개변수 기본값)

아래 코드를 통해 파라미터가 전달되지 않은 경우의 코드를 살펴보자.

export type Person = {name: string, age: number}

export const makePerson = (name: string, age: number = 10) : Person => {
	const person = {name: name, age: age};
	return person;
}

console.log(makePerson('Jack')) // {name: 'Jack', age: 10} -> 값이 없을 시 기본값이 셋팅 된다
console.log(makePerson('Jack', 33)) // {name: 'Jack', age: 33}

객체 생성 시 값(Value)부분을 생략할 수 있는 구문

const makePerson = (name: string, age: number) => {
	const person = {name, age} // 단축구문
}

객체를 반환하는 화살표 함수 만들기

객체를 반환해야 한다.. 근데 화살표 함수로??

export type Person = {name: string, age: number}

// bf : 컴파일러는 아래 코드를 복합 실행문으로 인식한다
const arrow = (name: string, age: number) : Person => {name, age};

// af : 컴파일러가 이제서야 해당 코드를 객체로 인식을 한다
// 객체 반환시에는 반드시 ()로 감싸서 반환해야 한다.
const arrow = (name: string, age: number) : Person => ({name, age});

매개변수에 비구조화 할당 사용하기

매개변수에 비구조화 할당이라, 지금까지 정리하면서도 난해한 부분이 너무 많은데..
하여튼 아래 코드를 보면서 어떻게 사용이 되는지 체크 해보자.

export type Person = {name: string, age: number}

const t1 = ({name, age} : Person) : void => {
	console.log(`He's name : ${name}, age : ${age}`);
}

t1({'Jack' , 22});

색인 키와 값으로 객체 만들기

객체의 속성 이름을 변수로 만들려고 할 때 사용이 된다.
솔직히 무슨 말인지 모르겠으니 아래 코드를 보면서 생각 해보자.

// key, value 두가지 파라미터를 각각 받아서, key value형태의 객체를 생성해 반환한다.
const convert = (key, value) => ({[key], value});

console.log(convert('Name', 'Jack'));

let obj = convert('FirstName', 'Kim');
console.log("obj : " + obj);

타입스크립트에서는 {[key]: value} 형태의 타입을 '색인 가능 타입' 이라 한다.
또한 다음과 같은 형태로 key와 value타입을 명시한다.

type KeyType = {
	[key: string] : string
}

04-6 클래스 메서드

function 함수와 this 키워드

함수 선언식에서는 this 키워드를 사용할 수 있지만, 화살표함수 에서는 this 키워드를 사용할 수 없다고 한다.
위 부분에 대해서 화살표 함수를 사용할 시, 왜? this 키워드를 사용할 수 없는지 알아봐야한다.

메서드

함수형 프로그래밍 언어인 타입스크립트 역시 객체지향 언어다
또한 객체지향 언어에는 당연히 클래스, 생성자, 메서드, 필드 변수가 존재한다.
위 부분중 아래 코드를 통해 메서드에 대해 정리 해보자.

// 변경 전
class A {
	name : string = 'Jack';
	
	method : () => void = function() {
		console.log("Hello " + this.name);
	}

	getTime : () => number = function() {
		// getTime은 그냥 메서드 이름이다
		// public String getTime() {} 이거랑 똑같음
	}	
}

const a : A = new A();
a.method();

하지만 위 코드의 메서드 부분은 가독성도 떨어지고, 솔직히 뭔지도 모르겠다.
그래서 타입스크립트는 클래스 내부에서 메서드를 생성할때 function 키워드 생략가능하다.

// 변경 전
class A {
	name : string = 'Jack';
	
	method : void {
		console.log("Hello " + this.name);
	}

	getTime : number {
		// getTime은 그냥 메서드 이름이다
		// public String getTime() {} 이거랑 똑같음
	}	
}

const a : A = new A();
a.method();

정적 메서드

메서드 앞에 static을 선언한 메서드를 지징한다.
즉, 인스턴스 레벨에서 선언이 되는것이 아닌, 클래스 레벨에서 선언이 되는 메서드이다.
또한 클래스임.정적메서드 형태로 호출하여 사용을 한다.

export class C {
	static whoAreYou() : string {
		return "I'm spiderman";
	}
}

export class D {
	// 중략..
}

console.log(C.whoAreYou());

참고 자료

https://velog.io/@parksj3205/2019-08-30-1208-작성됨


profile
https://ym1085.github.io

0개의 댓글