Front-end/JavaScript

[JavaScript] 객체로서의 함수와 기명 함수 표현식

Nave 2022. 5. 9. 01:11

자바스크립트에서 함수는 값으로 취급되는데, 그렇다면 함수의 자료형은 무언인가?? --> 객체!!

함수는 호출이 가능한(callable)'행동객체'이다. 

즉, 함수를 호출할 수 있을 뿐만 아니라 객체처럼 함수에 프로퍼티를 추가/제거하거나 참조를 통해 전달할 수도 있다는 뜻이다.

 

1. 'name'프로퍼티

'name'프로퍼티를 사용하면 함수 이름을 가져올 수 있다.

function sayHi() {
  alert("Hi");
}

alert(sayHi.name); // sayHi

let sayHi = function() {
  alert("Hi");
};

alert(sayHi.name); // sayHi
// 익명함수라도 이름이 자동으로 할당된다.

객체 메서드의 이름도 'name'프로퍼티를 이용해 가져올 수 있다.

let user = {

  sayHi() {
    // ...
  },

  sayBye: function() {
    // ...
  }

}

alert(user.sayHi.name); // sayHi
alert(user.sayBye.name); // sayBye

 

2. 'length' 프로퍼티

length함수는 매개변수의 개수를 반환한다.

function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}

alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2

// 나머지 매개변수는 개수에 포함되지 않는다!!

++ length 프로퍼티는 다른 함수 안에서 동작하는 함수의 타입을 검사할 때도 종종 사용된다.

예를들어, 사용자의 답을 제출하는 ask함수를 살펴보자.

function ask(question, ...handlers) {
  let isYes = confirm(question);

  for(let handler of handlers) {
    if (handler.length == 0) {
      if (isYes) handler();
    } else {
      handler(isYes);
    }
  }

}

// 질문인 question과 답에 따라 호출하는 임의의 수 handler를 매개변수로 받음

--> 사용자가 답을 제출하면 두 종류의 핸들러 함수가 ask에 전달됨

  • 인수가 없는 함수로, 사용자가 OK를 클릭했을 때만 호출됨
  • 인수가 있는 함수로, 사용자가 OK를 클릭하든 Cancel을 클릭하든 호출됨

그리고 handler.length 프로퍼티를 사용하여 상황에 맞는 handler를 호출하는 그런 코드....

 

3. 커스텀 프로퍼티

함수에 자체적으로 만든 프로퍼티를 추가할  수도 있다.

function sayHi() {
  alert("Hi");

  sayHi.counter++;
}
sayHi.counter = 0; // 초깃값

//counter라는 프로퍼티를 만들어서 함수 호출 횟수를 세는 것

sayHi(); // Hi
sayHi(); // Hi
//2번 호출 했기 때문에 sayHi.counter = 2 가 된다.

4. 기명 함수 표현식

기명 함수 표현식(Named Function Expression, NFE)은 이름이 있는 함수 표현식을 나타내는 용어이다.

let sayHi = function(who) {
  alert(`Hello, ${who}`);
};
//일반함수 표현식

let sayHi = function func(who) {
  alert(`Hello, ${who}`);
};
//func라는 이름을 붙임

기명함수 표현식을 사용하는 이유

--> 이름을 사용해 함수 표현식 내부에서 자기 자신을 참조할 수 있다.

let sayHi = function func(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    func("Guest");
  }
};

sayHi(); // Hello, Guest
// sayHi는 who에 값이 없는 경우, "Guest"를 받고 자기자신을 호출함.

// 하지만 이 함수 외부에서 func를 호출하는 건 불가능함.

그렇다면 왜 위와같이 중첩호출을 할 때 sayHi 대신 func를 사용할까?

let sayHi = function(who) {
  if (who) {
    alert(`Hello, ${who}`);
  } else {
    sayHi("Guest");
  }
};
//만약 이렇게 중첩함수로자기 자신을 호출했다면, 


let welcome = sayHi;
sayHi = null;
//이렇게 외부 코드에서 sayHi에 null을 할당해서 중첩 sayHi의 호출이 불가능해져 에러가 발생할 수 있기 때문

--> 위의 에러는 함수가 sayHi를 자신의 외부 렉시컬 환경에서 가지고 오기 때문에 발생한다. 지역(local) 렉시컬 환경엔 sayHi가 없기 때문에 외부 렉시컬 환경에서 sayHi를 찾는데, 함수 호출 시점에 외부 렉시컬 환경의 sayHi엔 null이 저장되어있기 때문에 에러가 발생하는 것.