[모던 자바스크립트 딥다이브] 12장 함수

2024. 4. 24. 21:41스터디/모던자바스크립트딥다이브

함수란?

수학의 함수

입력을 받아 출력을 내보내는 일련의 과정을 정의 한 것

 

프로그래밍 언어의 함수

수학의 함수와 같은 개념, 일련의 과정을 문으로 구현하고 그 문들을 코드블록으로 감싸서 하나의 실행 단위로 정의한 것

함수는 값이며 여러개 존재할 수 있으므로 함수를 구별하기 위해 식별자인 함수 이름을 사용할 수 있다.

 

함수 정의 - 함수 선언문이나 다양한 방법을 통해 일련의 과정을 함수로 정의 하는 것

매개변수 (parameter) - 입력을 전달받는 변수

인수 (argument)- 매개변수에 전달될 값, 입력하는 것

반환값(return value) - 함수의 결과로 출력되는 값

함수 호출 - 정의된 함수를 실행하는 것

 

함수를 정의한 뒤, 인수를 매개변수를 통해 함수에 전달하고

함수의 실행을 함수 호출을 통해 명시적으로 지시해야

함수가 실행되고 반환값이 출력된다.

 

함수를 사용하는 이유

  1. 코드의 재사용
  2. 유지보수의 편의성 & 실수 감소 → 코드의 신뢰성 향상
  3. 함수의 식별자를 분명하게 작성하는 등의 노력 → 코드블록 내의 문을 하나하나 이해하지 않아도 함수를 이해할수 있게 됨 → 코드의 가독성 향상

함수 리터럴

자바스크립트의 함수

- 객체 타입의 값. 일반 객체와는 다소 다름 (호출이 가능, 고유한 프로퍼티를 가짐)

- 따라서 다른 값을 리터럴로 생성하는 것 처럼 함수도 함수 리터럴로 생성 가능함.

 

리터럴

사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기 방식

 

함수 리터럴 = 함수 이름 + 매개변수 목록 + 함수 몸체

 

함수 이름

함수 몸체 내에서만 참조할 수 있는 식별자

생략 가능 → 생략할 경우 무명/익명 함수라고 부름, 생략하지 않은 경우 → 기명 함수

 

매개변수 목록

0개 이상의 매개변수를 소괄호로 감싸고 쉼표로 구분함

함수를 호출할 때 지정한 인수가 순서대로 할당된다. 즉 매개변수 목록의 순서는 유의미함.

 

함수 몸체

함수가 호출되었을 때 일괄적으로 실행될 문들을 하나의 실행단위로 정의한 코드 블록

함수 정의

매개변수와 실행될 문들, 반환값을 지정하는 것

함수 정의 방식

  1. 함수 선언문
  2. 함수 표현식
  3. Function 생성자 함수
  4. 화살표함수

변수는 선언 된다고 표현하고 함수는 정의 된다고 표현함. 왜 ?와이?

변수는 선언과 값이 할당되는 정의 되는 단계가 변수 선언 키워드에 따라 구분되지만

함수는 작성이 완료되면 엔진에 의해 평가되고 암묵적으로 식별자가 생성되고 할당되기 때문.

 

함수 선언문

함수 리터럴과 형태가 동일하다.

함수 리터럴은 함수 이름을 생략할 수 있으나 함수 선언문은 생략이 불가능하다.

// 함수 리터럴을 변수 exampleFun01에 할당함
const exampleFun01 = function (x, y) {
	return x+y;
}
const exampleFun02 = function exampleFun (x, y) {
	return x+y;
}

// 함수 선언문
function exampleFun03 (x, y) {
	return x+y;
}

// 함수 선언문은 이름 생략 불가!
function (x, y) {
	return x+y;
}

// SyntaxError : Function statements require a function name

 

함수 선언문은 ‘표현식이 아닌 문’ 이다.

즉, 값으로 평가 될 수 없는 문을 뜻한다.

 

따라서 개발자 도구에서 함수 선언문을 실행하면 undefined가 출력된다.

이는 완료 값이란 뜻으로. 해당 문을 평가한 결과가 아니라 해당 문을 실행했다 라는 뜻.

 

반면 값으로 평가 될 수 있는 문은 표현식이 이라고 한다. 새로운 값을 생성하거나 기존의 값을 참조해서 값을 가진다.

 

문 → 프로그램을 구성하는 기본 단위. 최소 실행단위.

표현식이 아닌 문 → 값으로 평가 될 수 없는 문

표현식 → 값으로 평가 될 수 있는 문

 

함수 선언문은 값이 아니기 때문에 변수에 할당이 불가함

 

엥..? 근데 이건 변수에 할당한거 아님??

const exampleFun02 = function exampleFun (x, y) {
	return x+y;
}

 

자바스크립트 엔진이 문맥에 따라서 '아, 저건 함수 리터럴(값으로평가될수 잇는 문. 즉, 표현식)이네.' 혹은 '아, 이건 함수 선언문이네' 하고 해석을 달리 하기 때문..

거의 뭐 사람이고?

 

함수 리터럴과 함수 선언문 모두 함수 객체를 생성한다는 점에서는 동일하지만 호출에 차이가 있다.

// 함수 리터럴을 변수에 할당
const exampleFun00 = function exampleFun (x) {
	return '저는 '+x+'입니다.';
}

// 함수 선언문
function exampleFun01 (x) {
	return '저는 '+x+'입니다.';
}

exampleFun('함수리터럴') // ReferenceError:exampleFun00 is not defined
exampleFun00('함수리터럴로 선언된 함수 객체를 할당받은 변수 exampleFun00') 
// '저는 함수리터럴로 선언된 함수 객체를 할당받은 변수 exampleFun00입니다.'
exampleFun01('함수선언문') // '저는 함수선언문입니다.'

함수 표현식

자바스크립트에서 함수 처럼 값의 성격을 가지는 객체를 일급 객체라고 한다.

 

함수는 일급 객체다. → 그래서 어쩔?

 

함수를 값처럼 자유롭게 사용할 수 있단 소리.

따라서 함수를 변수에 할당도 가능한데 위의 예시처럼 함수 리터럴을 변수에 할당 하는 것을 함수 표현식 이라고 한다.

함수 생성 시점과 함수 호이스팅

함수 선언문 - ‘표현식이 아닌 문’

함수표현식 - ‘표현식인 문’

 

함수 선언문과 함수 표현식의 이러한 차이는 ‘둘다 함수를 생성하는데 모?’

하면서 넘어갈 수도 있지만 미묘한 차이점을 가지는데 호이스팅에서나타난다.

 

자바스크립트엔진이 코드를 받으면 한줄 한줄 실행하기 앞서서 변수 선언문, 함수 선언문 같은 것들을 찾아서 미리 미리 식별자를 생성하고 할당이 필요하면 할당을 하게 된다.

 

따라서

var preVariable = ‘나는 변수. 호이스팅된 변수’;

 

 

이라는 변수를 선언하고 값을 할당하는 코드를 작성한 다음 코드가 선언된 라인보다 상위에서 호출하게되더라도 ‘나는 변수. 호이스팅된 변수’ 라는 변수 식별자에 대해 정의된 값을 얻을 수 있다.

 

이런 현상을 호이스팅 이라고 하는데 변수 선언문 처럼 함수 선언문도 호이스팅의 대상이다.

 

Hoisting, TDZ / parameter, argument

Hoisting 이란? TDZ란? Hoisting 자바스크립트 코드 내 선언만 분리하여 엔진이 미리 메모리에 공간을 할당하여 선언부들이 코드의 최상단으로 끌어 올리는 것처럼 보이는 현상 TDZ 자바스크립트 변수

youngsimi.tistory.com

 

따라서 함수선언문으로 작성된 함수는 호이스팅이 되므로 작성 라인 위에서 호출이 가능하다.

한편 함수 표현식으로 작성된 해당 표현식의 식별자인 변수명으로 호출하더라도 함수의 리터럴의 값을 얻을 수 없다.

exampleFun00('함수표현식') // 'TypeError: exampleFun00 is not function'
exampleFun01('함수선언문') // '저는 함수선언문입니다.'

// 함수 표현식
var exampleFun00 = function exampleFun (x) {
	return '저는 '+x+'입니다.';
}

// 함수 선언문
function exampleFun01 (x) {
	return '저는 '+x+'입니다.';
}

 

이런 특성 때문에 함수 선언문 대신 표현식을 사용할 것을 더글라스 라는 사람이 권장했다.

(더글라스는 똑똑한 사람임 JSON도 창안하고 자스 핵심가이드 책도 쓴 사람임.)

Function 생성자 함수

자바스크립트 기본 제공 함수 중에서 Function 생성자 함수를 통해서 함수를 정의할 수도 있다.

생성자 함수는 객체를 생성하는 함수를 뜻하는데… 자바스크립트 기본 제공 함수 중에서 약간 ‘함수 제너레이터’, ‘함수 마법사’ 이런 느낌으로다가 특정 인수를 전달하면 그걸로 촵촵찹 함수를 만들수 있는 함수가 있단 말.

 

결론 부터 말하면 권장하지 않는다.

 

Function 생성자 함수로 함수를 만들게 되면 클로저도 생성되지 않고, 함수 선언문이나 함수 표현식으로 생성한 함수와 다르게 동작하기 때문.

화살표 함수

ES6에서 도입됨.

function 키워드 대신 화살표로 간략하게 함수를 정의할 수 있다. 화살표 함수로 정의돈 함수는 항상 익명 함수이다…

아무리 눈 씻고 찾아봐도 이름따위 없기 때문…쿄쿄

 

타이핑 치는 문자들만 간략한게 아니라 내부 동작도 간략해진다.

  1. 생성자 함수로 사용될 수 없다. 즉 new 키워드 써가면서 만드는거 불가.
  2. this 바인딩도 다르다
  3. 화살표 함수로 만들어진 함수는 일급 객체가 가지는 프로퍼티중 prototype 프로퍼티가 없다.
  4. arguments 객체도 생성하지 않는다.

함수 호출

함수를 호출하면 현재의 코드실행 흐름을 잠시 멈추고 함수로 실행 흐름을 옮기게 된다.

이때 매개변수에 인수가 순서대로 할당되고.. 함수 몸체의 안에 문들이 실행되는 것이다.

매개변수와 인수

함수를 실행하기 위한 값을 외부에서 함수 내부로 전달할 때 쓰이는 개념.

매개변수가 인수를 전달한다.

 

매개변수 = 값이 통과되는 터널

인수 = 터널을 지나서 함수로 들어가는 값. 따라서 값으로 평가될 수 있는 표현식이어야 한다.

 

선언시점

- 매개변수는 함수를 정의할 때 선언 되야한다.

- 인수는 함수를 호출할때 지정한다.

 

개수 제한

- 둘다 개수 제한 없음.

 

특징

- 매개변수는 함수 내에서 변수와 동일하게 취급된다. 매개변수의 유효범위(스코프)는 함수 내부다. 즉 함수 외부에서 매개변수를 참조할 수 없다.

- 매개변수와 인수의 수가 반드시 일치 할 필요가 없다.

그저 인수들이 있으면 첫번째 매개변수부터 차례로 집어넣는다.

이때 매개변수가 더 많고, 인수가 적으면 인수가 통과되지 못한 매개변수는 함수 내부에서 undefined로 정의된다.

반대로 매개변수보다 인수가 더 많으면 그 인수는 무시된다. 정확히는 함수 객체의 arguments 객체 프로퍼티로 임시 보관된다. ( 터널 통과 신청자 명단 쯤으로 생각하면 되겠다.)

인수 확인

자바스크립트 함수에서 매개변수와 인수의 개수 일치 여부를 확인하지 않음 + 자바스크립트는 다이나믹 타입 언어

 

따라서 함수를 정의한 개발자의 의도와 다르게 동작할수가 있다.

 

엥..? 반드시 인수가 세개고 숫자여야 하는데 왠 문자열들이 합쳐져 있노??

 

이런것을 방지하기 위해서 코드를 작성할때 인수들이 적절히 잘 매개변수로 전달이 되었는지 확인할 필요가 있다.

매개변수의 최대 개수

명시적으로 ECMAScript 사양에서 제한을 하고 있지 않다.

물리적으로 제한이 있을수 있지만.. (진짜 5조5억개를 넘길수는 없지 않을까..? 어떤 사람이 계산 해봤을꺼같다. 오조는 안되겟지..)

 

하지만 매개변수가 너무 많으면 함수를 이해하기 힘들게 하기 때문에 이상적인 매개변수의 개수는 0개, 많아야 3개 정도라고 한다. 그 이상이라면 객체를 인수로 전달하는것이 나을 수도 있다.. 이거도 무조건 나은건 아님.

반환문

return 키워드로 함수의 표현식의 평가 결과를 반환값으로 내보낼수 있다.

 

역할

-  반환문은 함수를 중단하고 함수 몸체를 빠져나가는 역할

- return 키워드 뒤에 오는 평가문을 평가해서 반환하는 역할을 한다.

 

특징

반환문은 생략이 가능하다 → 이러면 해당 함수는 암묵적으로 undefined를 반환한다.

반환문은 함수 몸체에서만 사용가능하다!

참조에 의한 전달과 외부 상태의 변경

 

하지만 매개변수가 너무 많으면 함수를 이해하기 힘드므로 이상적인 매개변수의 개수는 0개, 많아야 3개 정도라고 한다. 그 이상이라면 객체를 인수로 전달하는것이 나을 수도 있다.. 이거도 무조건 나은건 아님.

 

 

객체 타입은 원시값이 아니기 때문에..(참조값임) 만약 객체 타입을 함수에 인수로 전달하게 되면

함수 내부에서 값을 요로로롱 바꿨을때 함수 외부의 객체 타입의 기존값이 바뀔수 있다. Side Effect

따라서 주의를 요함.

 

→ 객체의 변경을 추적하기 위한 옵저버 패턴 등을 통해 객체의 참조를 공유하는 모든 이들에게 변경 사실을 통지한다던가 이에 대처하는 추가 대응이 필요하다.

 

→ 혹은 객체체를 원시값처럼 변경 불가능한 값으로 동작하기 위해 매개변수에 전달하는 객체 인수를 전달할때 원본 객체인수를 전달하는게 아니라 그 객체인수를 깊은 복사를 해서 새로운 객체를 하나 만들어서 인수로 매개변수에 전달해주는 방법이 있다.

 

→ 외부값에 의한 영향을 최소화 하는 프로그래밍 패러다임을 함수형 프로그래밍이라고 한다. 외부값에 의해 영향을 받지 않는 순수함수를 최대한 이용하자.

다양한 함수의 형태

즉시 실행 함수

함수의 정의와 동시에 즉시 호출되는 함수. 단 한번만 호출되며 다시 호출 될 수 없다.

재귀 함수

함수가 자기 자신을 호출하는 함수.

반복 처리를 위해 주로 사용한다.

무한 재귀 호출을 하기 때문에 탈출조건을 반드시 만들어야 한다.

중첩 함수

함수 내부에 정의된 또 다른 함수 ( =내부함수)

콜백 함수

함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수.

콜백 함수를 가진 함수를 고차 함수라고 한다.

순수함수 & 비순수 함수

순수함수 - 외부 상태에 의존하지도 않고, 외부 상태를 변경하지도 않는 함수

비순수 함수 - 외부 상태에 의존하거나, 외부상태를 변경하는 함수