JavaScript | 12장 함수

설탕·2024년 4월 2일
0

함수 리터럴

함수 리터럴은 function 키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성된다.

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

함수 이름은 함수 몸체 내부에서만 유효한 식별자이고, 함수 이름으로 호출하면 ReferenceError(참조 에러)가 발생한다.

const greet = function sayHello(name) {
  console.log(name + ", Hello!");
}

greet("Seoltang"); // 'Seoltang, Hello!'
sayHello("Seoltang"); // ReferenceError: sayHello is not defined.

함수 정의

함수 선언문 vs 함수 표현식

  • 함수 선언문은 표현식이 아닌 문이다. 단독으로 사용된 함수 리터럴은 함수 선언식으로 평가된다.
  • 자바스크립트 엔진은 함수 리터럴이 값으로 평가되어야 하는 문맥—예를 들면 변수에 할당하거나 피연산자로 사용하는 경우—에서는 함수 리터럴 표현식으로 해석한다.
  • 따라서 그룹 연산자() 내에 있는 함수 리터럴은 함수 선언문으로 해석되지 않고 함수 리터럴 표현식으로 해석된다.
  • 크롬 개발자 도구에서 표현식이 아닌 문을 실행하면 언제나 undefined를 출력한다. 이를 완료 값이라 한다. 완료 값은 표현식의 평가 결과가 아니기 때문에 다른 값과 같이 변수에 할당할 수 없고 참조할 수도 없다. 반대로 표현식인 문을 실행하면 언제나 평가된 값을 반환한다.

함수 호이스팅 vs 변수 호이스팅

함수 호이스팅과 변수 호이스팅은 선언문이 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행되어 식별자를 생성한다는 점에서 동일하다. 하지만 var 키워드로 선언된 변수는 undefined로 초기화되고, 함수 선언문을 통해 암묵적으로 생성된 식별자는 함수 객체로 초기화된다. 따라서 var 키워드를 사용한 변수 선언문 이전에 변수를 참조하면 변수 호이스팅에 의해 undefined로 평가되지만 함수 선언문으로 정의한 함수를 함수 선언문 이전에 호출하면 함수 호이스팅에 의해 호출이 가능하다.

함수 표현식으로 함수를 정의하면 함수 호이스팅이 아니라 변수 호이스팅이 발생하는데, 변수 선언은 런타임 이전에 실행되어 undefined로 초기화되지만 변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.

👉🏻 함수 선언문은 함수 호이스팅을 따르고 함수 표현식은 변수 호이스팅을 따른다!

함수 선언문으로 함수를 정의하면 자바스크립트 엔진이 런타임 이전에 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 생성한 함수 객체를 할당한다.

console.log(add(3, 2)); // 5

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

console.log(add(3, 2)); // 5

var 키워드로 선언한 변수는 자바스크립트 엔진에 의해 undefined로 초기화되기 때문에 함수를 호출하려고 하면 TypeError가 발생한다.

console.log(subtract(3, 2)); // TypeError: subtract is not a function

var subtract = function(a, b) {
  return a - b;
}

console.log(subtract(3, 2)); // 1

const 키워드로 선언한 변수는 선언문에서 초기화되기 전에 참조할 수 없기 때문에 ReferenceError가 발생한다.

console.log(subtract(3, 2)); // ReferenceError: subtract is not defined

const subtract = function(a, b) {
  return a - b;
}

console.log(subtract(3, 2)); // 1

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

함수가 호출될 때 인수가 복사되어 매개변수에 전달된다.

원시 타입 인수 vs 객체 타입 인수

  • 원시 타입 인수
    • 값 자체가 복사되어 매개변수에 전달된다.
    • 함수 내부에서 값을 변경(재할당을 통한 교체)해도 원본은 훼손되지 않는다. (부수 효과 발생하지 않음)
  • 객체 타입 인수
    • 참조 값이 복사되어 매개변수에 전달된다.
    • 함수 내부에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손된다. (부수 효과 발생)
function changeVal(name, score) {
  name = 'Seoltang'; // 원시 값은 변경 불가능한 값이므로 재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체
  score.math = 85; // 객체는 변경 가능한 값이므로 재할당 없이 직접 할당된 객체를 변경
}

let name = 'Sooya';
let score = { math: 100 };

changeVal(name, score);

console.log(name); // 'Sooya'
console.log(score); // { math: 85 }

다양한 함수의 형태

콜백 함수

  • 콜백 함수: 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수
  • 고차 함수: 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수

고차 함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출한다.

function printAge(birthYear, fn) {
  console.log("age: " + fn(birthYear));
}

const getAge = function(birthYear) {
  return 2023 - birthYear;
}

printAge(1997, getAge); // getAge 콜백 함수는 단 한 번만 생성된다.

printAge(1995, function(birthYear) {
  return 2023 - birthYear;
}); // 고차 함수가 호출될 때마다 콜백 함수가 매번 새로 생성된다.

순수 함수

  • 순수 함수는 외부 상태에 의존하거나 외부 상태를 변경하지 않는 함수이다. (부수 효과side effect가 없다.)
    • 외부 상태에는 전역 변수, 서버 데이터, 파일, Console, DOM 등이 있다.
  • 순수 함수는 동일한 인수가 전달되면 언제나 동일한 값을 반환한다.
  • 내부 상태에만 의존한다 해도 그 내부 상태가 호출될 때마다 변화하는 값(예: 현재 시간)이라면 비순수 함수이다.
  • 순수 함수는 인수를 변경하지 않는 것이 기본이다. 즉, 인수의 불변성을 유지한다.

순수 함수의 예

  • Array.map
  • Array.slice

비순수 함수의 예

  • Date.now, Math.random: 호출할 때마다 다른 결과를 반환
  • Array.push: 원본 배열을 변경
profile
공부 기록

0개의 댓글