[JavaScript] 자바스크립트의 함수

김유진·2022년 9월 27일
0

Javascript

목록 보기
10/22
post-custom-banner

자바스크립트에서 함수는 매우 중요하다. 자바스크립트의 핵심 개념이라고 할 수 있는 스코프 실행 컨텍스트 클로저 생성자 함수에 의한 객체 생성 메서드 this 등등.. 함수와 모두 깊은 관련이 존재한다. 따라서 함수는 자바스크립트를 정확히 이해하고 사용하기 위하여 피해갈 수 없는 핵심 중의 핵심이다.

1. 객체 타입의 함수

자바스크립트의 함수는 객체 타입의 값이다. 따라서, 함수를 함수 리터럴로 생성 가능하다.
여기서 리터럴이란, 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용하여 값을 생성하는 표기 방식이다.

var f = function add(x,y) {
  return x + y;
}

위 코드는 변수에 함수 리터럴을 생성한 것이다.
함수 리터럴의 구성 요소는 다음과 같다.

  • 함수 이름
    - 함수 이름은 식별자로 작동한다.
    • 함수 이름은 함수 몸체 내에서만 식별할 수 있는 식별자!
    • 함수 이름은 생략 가능하다.
  • 매개변수 목록
    - 순서대로 적용됨
  • 함수 몸체
    - 함수가 호출되었을 때 일괄적으로 실행될 문들을 하나의 실행 단위로 정의한 코드블록

이제 함수 리터럴이 어떻게 저장되는지 이해할 수 있다. 리터럴은 값을 생성하기 위한 표기법이므로, 함수 리터럴도 평가되어, 값을 생성하며 이 값은 객체라고 파악할 수 있다.
하지만 함수 객체가 가지는 큰 특징이 있는데..

일반 객체는 호출 불가능하지만, 함수는 호출할 수 있다.

일반 객체에는 없는 함수 객체만의 고유한 프로퍼티를 갖는다.

함수 정의에 대한 여러가지 방법

  • 함수 선언문
function add(x,y) {
  	return x + y;
}
  • 함수 표현식
var add = function(x,y) {
  return x + y;
};
  • Function 생성자 함수
var add = new Function('x', 'y', 'return x + y');
  • 화살표 함수
var add = (x,y) => x + y;

여기서 함수 선언문을 통하여 함수를 정의하는 방법에 대해서 재미있는 사실이 있다.

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

함수 선언문은 함수 리터럴과 형태가 동일합니다! 함수 리터럴

var add = function(x,y){
  return x + y;
};

와 같이 함수 이름을 생략할 수 있으나, 함수 선언문은 함수 이름을 생략할 수 없습니다!
그 이유는, 함수 선언문표현식이 아니라, 입니다..그래서 실행 완료하면 완료 값으로 당연히 undefined가 출력된다. 함수 선언문이 표현식인 문이라면 완료 값 undefined 대신에 표현식이 평가되어, 생성된 함수가 출력될 것이다.

이 개념대로 간다면... 표현식이 아닌 은 변수에 할당할 수 없을 텐데... 그럼 이건 뭐냐?

var add = function add(x,y) {
  return x + y;
};

이 경우에는 자바스크립트 엔진 탓을 해야 하는데..
자바스크립트 엔진이 동일한 함수 리터럴을 코드의 문맥에 따라서 두 가지로 나누어 해석한다.

  • 표현식이 아닌 인 함수 선언문으로 해석하는 경우
  • 표현식인 함수 리터럴 표현식으로 해석하는 경우

갑자기 이게 뭔 소린가 싶고..억지 부리는 것 같이 보이지만! 결론적으로는 함수 선언문은 함수 이름을 생략할 수 없다는 점을 제외하면, 함수 리터럴 표현식과 동일하다. 그래서 함수 이름이 있는 기명 함수 리터럴은 함수 선언문 또는 함수 리터럴 표현식으로 해석될 가능성이 있다는 것이다.

자바스크립트 엔진의 중의성 때문에 나타나는 문제이다. 이럴 경우 자바스크립트 엔진은 주변 코드의 문맥을 이용하여 상황을 해석하고, 해결해나간다.

위에 줄글로 풀어놓은 것을 일목요연하게 정리해보자면..

  • 함수 이름이 있는 함수 리터럴을 단독으로 사용하는 경우 (ex 함수 리터럴 피연산자 사용 X)에는 함수 선언문으로 해석
var add = function(x,y){
  return x + y;
};
  • 함수 리터럴이 값으로 평가되어야 하는 문맥 (ex 함수 리터럴을 변수에 할당하거나, 피연산자로 사용)에는 함수 리터럴 표현식으로 석!

함수 표현식 vs 함수 선언문, 생성 내부 동작 파헤치기

function foo() {console.log('foo')}
//기명 함수 리터럴을 단독으로 사용하였기 때문에 함수 선언문으로 해석된다. 

foo();
//함수 리터럴을 피연산자로 사용하면, 함수 선언문이 아니라 함수 리터럴 표현식으로 해석된다.
(function bar() {console.log('bar'); });
bar(); //bar is not defined 라면서 ReferenceError 발생 

단독으로 사용된 함수 리터럴 foo는 함수 선언문으로 해석된다.
그러나, 그룹 연산자인 ()은 값으로 평가될 수 있는 표현식이 피연산자로 들어와야 하는데, 표현식이 아닌 문인 함수 선언문은 피연산자로 사용할 수 없다. 즉 () 안에 존재하는 함수 리터럴 bar는 함수 선언문으로 해석되지 않고, 함수 리터럴 표현식으로 해석된다.

이렇게 이름이 있는 함수 정의는 문맥에 따라서 함수 선언문함수 리터럴 표현식 두 가지로 해석될 수 있다.

자. 여기까지는 이해가 잘 되었다. 그런데 함수를 호출할 때, 왜 에러가 발생할까?
함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자이다.
이는, 함수 몸체 외부에서는 함수 이름으로 함수를 참조할 수 없다는 것을 의미한다.

  1. foo()의 경우를 살펴보자
    아니, 함수 외부에서 사용할 수 없다면 foo도 에러가 발생해야 하는 것 아닌가? 싶을 것이다..
    foo라는 이름으로 함수를 호출하려면, 함수 이름이 아니라 함수 객체를 가리키는 식별자를 이용해야 한다. 그런데 나는 foo에 대한 식별자를 호출한 적이 없는데 참 이상하다...🙄

    foo는 자바스크립트 엔진이 암묵적으로 생성한 식별자이다.

자바스크립트 엔진은 암묵적으로 생성된 함수를 호출하기 위하여, 함수 선언문을 해석하여 함수 이름과 동일한 이름의 식별자를 생성하고, 거기에 함수 객체를 할당한다.

  1. bar()의 경우를 살펴보자
    bar 같은 경우에는, 함수 객체를 가리키는 식별자가 따로 없는 것이다. 그래서 ReferenceError가 발생하는 것이다.

우리는 이번 공부를 통해서 사실 이거 하나만 얻어가면 된다... 자바스크립트 엔진은 함수 선언문함수 표현식으로 변환하여, 함수 객체를 생성한다. 그리고...

함수는 함수 이름으로 호출하는 것이 아니라, 함수 객체를 가리키는 식별자를 통하여 호출된다!

함수 표현식 심화

자바스크립트의 함수는 객체 타입의 값이므로 다음 행동들이 가능하다.

  • 변수에 할당
  • 프로퍼티 값이 된다.
  • 배열의 요소
    이렇게 값의 성질을 갖는 객체를 일급 객체라고 하는데, 함수도 일급 객체라고 할 수 있다.

위에서 확인한 것과 같이, 함수 리터럴로 생성한 함수 객체를 변수에 저장할 수 있다. 이러한 방식을 함수 표현식이라고 하였다.

var add = function(x, y){
  return x + y;
};

이렇게 함수 리터럴의 이름은 생략이 가능하다. 어짜피 호출은 함수 이름이 아니라.. 함수 객체를 가리키는 식별자가 하는 것이니까!

var add = function foo(x,y){
  	return x + y;
};

console.log(add(2,5)) //정상 작동
console.log(foo(2,5)) //레퍼런스 에러 발생

위에서 언급한 애매한 말을 다시 정리할 필요가 있겠다.

  • 함수 선언문은 표현식이 아닌 문
  • 함수 표현식은 표현식인 문

Fucntion 생성자 함수

자바스크립트가 기본적으로 제공하는 빌트인 함수인 Function생성자 함수이다.
(여기서 생성자 함수는 객체를 생성하는 함수를 말한다.)

그런데 비추합니다. 😣 그 이유는 아래에..

  • 클로저 생성 안함
  • 함수 선언문, 표현식으로 생성한 함수와 다르게 동작하여 예측 불가능

화살표 함수

ES6에서 도입된 따끈따끈한 친구입니다. function키워드 대신에 =>를 사용하여 간략하게 함수를 선언한다. 이친구는 항상 익명 함수라는 점을 알아두세요..

const add = (x, y) => x + y;
console.log(add(2,5)); 

화살표 함수 문법 여러가지 보기

  • 매개변수 선언
const arrow = (x,y) => {...};

매개변수가 한 개인 경우, 소괄호를 생략 가능하다.

const arrow = x = > {...};

매개변수가 없으면 소괄호를 생략할 수 없다.

const arrow = () => { ... };
  • 함수 몸체 정의
    함수 몸체가 하나의 문으로 구성된다면 중괄호를 생략한다.
const power = x => x ** 2;

다만 중괄호를 생략하였을 때, 함수 몸체의 내부의 문이 표현식이 아닌 문이라면 에러를 발생시킨다. 표현식이 아닌 문을 반환하면 어떡하겠니...
여러줄로 이루어지면 중괄호 생략하면 안돼~

사실 자바스크립트 공부를 깊게 하다 보면 기존 함수와 화살표 함수는 다른 점이 많다.

  • 생성자 함수로 사용 X
  • 기존 함수와 this를 바인딩하는 방식이 다르다. (콜백 함수와 관련)
  • prototype 프로퍼티가 없다.
  • arguments 객체를 생성하지 않는다.
    듣기로는 많은 것이 달라 보이는데...위에 내용을 공부를 하고 확인해보아야겠다.

2. 함수 호출에 관하여

post-custom-banner

0개의 댓글