자바스크립트 함수

김정훈·2023년 3월 30일

JavaScript

목록 보기
4/10

자바스크립트의 함수는 C, C++과 같은 언어와 다른 매우 독특한 문법이다.

자바스크립트 함수의 특징을 알아보자

호이스팅

console.log(add(2, 7)); // 9

function add(x, y){
  return x + y;
}
  • 자바스크립트 실행환경에서 해당 코드를 실행시켜보자. 잘 작동한다.

자바스크립트는 인터프리터언어라서 위쪽부터 차례대로 한 줄 씩 실행되는데 어떻게 함수 사용 명령을 먼저 사용할 수 있지?

  • 자바스크립트는 문서를 실행할 때 fucntion으로 정의된 함수를 문서 위쪽으로 배치시키는 호이스팅이라는 기능이 있다.

(호이스팅은 추후 더 자세히 다뤄보자, 일단 자바스크립트에서 함수를 선언하면 문서의 맨 위로 끌어 올려진다는 것으로 기억해두자)

함수를 정의하는 방법들이 독특하다

긴말 필요 없이 바로 예시로 보자

일반적인 선언

function add(x, y){
  return x + y;
}
  • function키워드, 함수의 이름을 결합한 다른 언어의 함수와 비슷한 형태를 갖는 선언방법이 있다. (얘는 호이스팅 됨, 기억!)

상수나 변수에 함수 대입

.....❓어떻게 이런 일이

자바스크립트에서 함수는 값, 즉 객체로 취급된다. 객체는 변수에 할당할 수 있었다. 그렇다면?

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

console.log(add(7,3)) // 10
  • 상수의 이름을 함수의 이름으로 정해주었기 때문에 일반적인 선언처럼 function다음에 함수 이름을 붙일 필요가 없다.
    (변수에도 대입이 가능하고 얼마든지 변경할 수 있다.)

  • 함수를 호출하는 방법은 일반적인 선언의 함수와 동일하다.

화살표 함수

  • 매우 독특한 방식이다. 살펴보자
const add = (x, y) => x + y;

console.log(add(7,3)) //10

인자를 받고 값을 반환하는 용도로 특히 많이 사용한다.
화살표 함수는 function키워드 때로는 블록{}으로 묶어 줄 필요가 없다.

  • 위 코드는 다음 코드와 같은 기능을 한다.
function add(x, y){
  return x + y;
}

이런식으로 함수의 형태가 간단한 경우(인자로 반환값을 바로 만들어내어 반환하는 경우)에 사용할 수 있다.

만약 조금 더 복잡한 함수라면 블록{}을 사용해 함수의 동작을 구현할 수 있다.

const add = (x, y) => {
  console.log(`${x} + ${y} = ?`);
  return x + y;
}

console.log(add(7,3));
// 7 + 3 = ?
// 10
  • 그러나 function으로 선언된 함수와 대입된 함수, 화살표 함수와 차이점이 존재한다.
console.log(add(7,3)) // 10

const add = add(x, y){
  return x + y;
}
  • 대입된 함수, 화살표 함수는 호이스팅이 되지 않는다.

그래서 해당 코드는 오류가 발생한다.

일급 객체(First Class Object)

  • 자바스크립트의 함수는 일급 객체라는 표현을 사용한다.
  • 일급 객체는 함수를 변수처럼 다루는 언어에서 사용하는 개념이다.
    • 위쪽 토픽에서 함수를 변수나 상수에 대입 할 수 있다는 사실을 알아보았다.
  • 따라서 자바스크립트의 함수도 객체로 취급된다.
function add(x, y) { return x + y; }

console.log(typeof add); // function
console.log(add); // f add (x, y) { return x + y; }
console.log(add instanceof Object); // true

출력된 결과의 내용을 미루어 볼때 자바스크립트의 함수는 function이라는 객체라는 사실을 알 수 있다.

일급객체의 특성

1. 할당

  • 상수나 변수에 할당 될 수 있다.
function isOddNum (number) {
  console.log(
    (number % 2 ? '홀' : '짝')
    + '수입니다.'
  );
  return number % 2 ? true : false;
};

const checkIfOdd = isOddNum; // 뒤에 괄호 없음 유의

console.log(checkIfOdd(23)); 
// 홀수입니다.
// true

function으로 함수를 선언하고 괄호를 붙이지 않은채 상수에 할당하고 있다.

할당된 변수나, 상수의 이름으로 할당한 함수를 호출 할 수 있다.
물론 함수의 이름으로도 호출 할 수 있다.

상수나 변수는 할당된 함수를 참조하기때문에 실행 할 수 있다.

함수도 객체니까!

💡함수는 객체와 배열의 값으로도 할당이 가능하다.

let person = {
  name: '홍길동',
  age: 30,
  married: true,
  introduce: function (formal) {
    return formal
    ? '안녕하십니까. 홍길동 대리라고 합니다.'
    : '안녕하세요, 홍길동이라고 해요.';
  }
};

console.log(person.introduce(true)); // 안녕하십니까. 홍길동 대리라고 합니다.
console.log(person.introduce(false)); // 안녕하세요, 홍길동이라고 해요.
let arithmetics = [
  (a, b) => a + b,
  (a, b) => a - b,
  (a, b) => a * b,
  (a, b) => a / b
];

for (arm of arithmetics) {
  console.log(arm(5, 3));
}
/*
8
2
15
1.6666666666666667
*/

⭐ 객체에 함수 프로퍼티를 포함할 때 기억해야 할 것

let person = {
  name: '홍길동',
  age: 30,
  married: true,
  introduce: function () {
    return `저는 ${this.name}, ${this.age}살이고 `
    + `${this.married ? '기혼' : '미혼'}입니다.`;
  }
}

console.log(person.introduce()); // 저는 홍길동, 30살이고 기혼입니다.

person이라는 객체에 introduce라는 함수 프로퍼티가 존재한다. 해당 함수가 객체의 다른 프로퍼티에 접근하려면 this, 즉 해당 객체의 프로퍼티를 가져와야 한다.

함수의 형태가 간단하니 화살표 함수로 바꿔 볼 수 있겠다.

let person = {
  name: '홍길동',
  age: 30,
  married: true,
  introduce: () => {
    return `저는 ${this.name}, ${this.age}살이고 `
    + `${this.married ? '기혼' : '미혼'}입니다.`;
  }
}

console.log(person.introduce()); // 저는 , undefined살이고 미혼입니다.

?! 출력 결과가 이상하다. nameage에 접근이 되지 않았다. introduce 프로퍼티 함수에 this를 콘솔에 출력해보자

Window {0: Window, 1: global, window: Window, self: Window,
document: document, name: '', location: Location, …}

.... 왜 전역 객체인 window를 출력할까? this에 대해서 자세히 다루는 포스트를 만들어야 겠다.

객체 리터럴의 프로퍼티로 this를 사용하는 화살표 함수를 사용하지 말자.

2. 인자로 전달

  • 함수가 다른 함수를 인자로 전달 받을 수 있다.
  • 전달받는 함수를 고차함수, 전달되는 함수를 콜백 함수라고 한다.
let list = [1, 2, 3, 4, 5];

// 배열의 요소들을 인자로 어떤 함수를 실행하는 함수
function doInArray (array, func) {
  for (item of array) {
    func(item);
  }
}

// console.log - console이란 객체에서 log란 키에 할당된 함수
doInArray(list, console.log);
/*
1
2
3
4
5
*/

해당 코드에서는 doInArray가 고차함수, console.log가 콜백함수가 되겠다.

  • 인자로 변수나 상수에 할당되지 않고 이름이 없는 익명 함수도 콜백함수로 전달 할 수 있다.
function doNTimes (func, repeat, x, y) {
  let result = x;
  for (i = 0; i < repeat; i++) {
    result = func(result, y);
  }
  return result;
}

console.log(
  doNTimes((x, y) => x * y, 3, 5, 2),
  doNTimes((x, y) => x / y, 3, 5, 2),
);
// 40 0.625

콜백함수는 이후 함수형 프로그래밍에서 아주 유용하게 사용된다

3. 반환값

  • 함수는 다른 함수에서 결과 값으로 반환 될 수 있다.
function getIntroFunc (name, formal) {
  return formal
  ? function () {
    console.log(`안녕하십니까, ${name}입니다.`);
  } : function () {
    console.log(`안녕하세요~ ${name}이라고 해요.`);
  }
}

const hongIntro = getIntroFunc('홍길동', true);
const jeonIntro = getIntroFunc('전우치', false);

hongIntro();
jeonIntro();

반환 값인 함수에는 이름을 붙일 필요는 없다.

반환 된 함수는 상수나, 변수에 저장 되어 사용할 수 있다.

함수를 반환 할 수 있는 기능은 필요에 따라서 함수, 즉 기능을 생성해 낼 수 있다는 의미이다.

불변성
다음 코드를 실행해 보자

let x = 1;
let y = {
  name: '홍길동',
  age: 15
}
let z = [1, 2, 3];

function changeValue (a, b, c) {
  a++;
  b.name = '전우치';
  b.age++;
  c[0]++;

  console.log(a, b, c); // 2 {name: '전우치', age: 16} [2, 2, 3]
}

changeValue(x, y, z);

음! 성공적으로 x, y, z 값이 바뀌었군 출력해 볼까?

console.log(x, y, z); // 1 {name: '전우치', age: 16} [2, 2, 3]

🙄❓❓❓❓ 왜 x값이 그대로지

함수에 원시값을 넣어서 사용 할 때는 그 복사본을 사용한다. 그래서 원시 타입은 인자로 들어간 함수 내에서의 변경에 영향을 받지 않는다.

반면에 참조 타입(객체, 배열 등)은 인자로 들어간 함수 내에서 요소가 변하면 실제의 값도 변한다. 함수에 복사된 값은 참조값이라 객체나 배열을 가리키기 때문이다.

그런데 저런식으로 함수의 주어진 인자를 변경하는 것은 좋지 않다. 꼭 필요할 때만 변경하도록 하자.

기본적으로 함수는 외부에 영향을 받지 않고 자신의 매개변수만을 제어하는 것이 이상적이다.

정리

자바스크립트에서의 함수는 일급 객체로 취급되며 할당, 반환, 매개변수로 사용할 수 있다. 함수를 변수에 할당해 참조하게 하여 변수의 이름으로 호출할 수 있다. 또한, 함수속에서 함수를 반환 할 수 있다. 마지막으로 함수의 매개변수로 전달해 콜백함수로 사용할 수 있다. 함수도 객체이기 때문에 여러가지의 프로퍼티를 가지고 있는데 그 중에서 prototype이라는 프로퍼티는 오직 함수에만 존재하며 생성자 함수로서 사용될 때 생성될 인스턴스의 프로토타입으로 사용된다.

0개의 댓글