Front-End

vsoghlv@naver.com

자바스크립트 함수

함수는 어떤 작업을 수행하기 위해 필요한 문들의 집합을 정의한 코드블록이다.

함수는 이름과 매게변수를 가지고 있으면 필요할 때는 몇번이고 호출할 수 있다. 만약 우리가 동일한 작업을 여러 번 반복할 것으로 예측된다면 함수로 정의해 재사용하는 것이 효율적이다.

함수는 보통 코드의 재사용을 위해 사용하지만 이 외에 객체 생성, 메소드, 정보은닉, 클로저, 모듈화 등 다른 기능들을 수행할 수 있다.

함수정의

함수를 정의하는 방법은 3 가지가 있다. 일단 화살표 함수를 제외하고 확인해 보자.

//함수 선언문
function square(number) {//function 함수명 (매개변수 - 타입 기술 필요없음)
  return number * number//함수 몸체 
}

//함수 표현식 - 함수는 일급객체이기 때문에 변수에 할당 가능
let square = function (number) {
  return number * number;
};

//Function 생성자 함수 - 일반적으로 사용하지는 않음
let square = new Function("number", "return number * number");

함수 호이스팅

위에서 3가지의 함수 정의 방식을 보았는데 위 3가지는 동작 방식에 약간씩 차이가 있다.

먼저 함수 선언문으로 정의한 함수를 보자.

let squ = square(4);

function square(number) {
  return number * number;
}

함수 선언문으로 함수를 정의하는 경우 자바립트에서 호이스팅 하므로 함수 선언 위치와 관계 없이 호출이 가능하다. - 자바스크립트 엔진이 스트립트가 로딩되는 시점에 함수 선언, 초기화, 할당을 한번에 한다.

함수 표현식의 경우 조금 다르다.

let squ = square(4);//square is not defined

let square = function (number) {
  return number * number;
};

함수 표현식의 경우 에러가 발생하는데 이는 함수 호이스팅이 아닌 변수 호이스팅이 발생하기 때문이다. 이전에 공부했듯이 변수의 경우 undefined 로 초기화 된 후 할당문에서 할당이 이루어 진다.

함수 표현식은 함수 선언문과는 달리 스크립트 로딩 시점에 변수 객체(VO)에 함수를 할당하지 않고 runtime에 해석되고 실행되므로 이 두가지를 구분하는 것은 중요하다 -poiemaweb.com

더글라스 크락포드는 위와 같은 문제 때문에 함수 표현식 만을 사용할 것을 권고한다고 한다.

매개변수

원시 타입 인수는 Call-by-value (값에 의한 호출) 로 동작하는데, 이는 함수 호출시 매개변수로 전달할 때 매개변수에 값을 복사하여 전달하는 방식이다. 이렇기 때문에 함수 내에서 매개변수를 통해 값이 변경되어도 기존의 원시타입 값은 변하지 않는다.

객체형 인수는 이와 다르게 Call-by-reference (참조에 의한 호출) 로 동작하는데, 함수 호출 시에 매개변수로 전달할 때 값이 복사되는 것이 아닌 객체의 참조값이 매개변수에 저장되어 전달된다. 이렇게 때문에 함수내에서 객체의 값을 변경하면 (매게변수의 참조값을 이용하므로) 전달 되어진 참조형의 인수값도 변경된다.

함수 객체의 프로퍼티

함수도 객체로 프로퍼티를 가지고 있는데 몇 가지 기본 내장 프로퍼티를 알아보자.

arguments 프로퍼티

arguments 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는 유사 배열 객체이다. 함수 내부에서 지역변수처럼 사용된다. 함수 외부에서는 사용이 불가능하다.

function multiply(x, y) {
  console.log(arguments);
  return x * y;
}

multiply();        // {}
multiply(1);       // { '0': 1 }
multiply(1, 2);    // { '0': 1, '1': 2 }
multiply(1, 2, 3); // { '0': 1, '1': 2, '2': 3 }

매개변수(parameter) 는 인수(argument) 로 초기화 되는데 매개변수의 갯수보다 인수를 적게 전달한 경우 전달되지 않은 값은 undfined 로 초기화 된다. 만약 인수를 더 많이 전달한 경우 초과된 인수는 무시한다.

런타임 시 호출된 함수의 인자 갯수를 확인하고 개수에 따라 동작을 다르게 정의할 필요가 있을 때가 있는데 이때 유용하게 사용하는 것이 arguments 객체이다.

arguments 객체는 유사배열객체이기 때문에 배열 메소드를 사용하려면 prototype 을 이용해야 한다.

caller 프로퍼티

caller 프로퍼티는 자신을 호출한 함수를 의미한다.

function foo(func) {
  let res = func();
  return res;
}

function bar() {
  return 'caller : ' + bar.caller;
}

console.log(foo(bar)); 
// caller : function foo(func) {...}
console.log(bar()); 
// caller : null

length 프로퍼티

length 프로퍼티는 함수 정의 시 작성된 매개변수 갯수를 알려준다. arguments.length 는 함수 호출 시 인자의 갯수로 둘의 값이 다를 수 있다.

function baz(x, y) {
  return x * y;
}
console.log(baz.length); // 2

proto 접근자 프로퍼티

모든 객체에는 [[Prototype]] 이라는 내부 슬롯이 있는데 이는 프로토타입 객체를 가리킨다. 객체간의 상속을 구현하기 위해 사용되는데 __proto__ 프로퍼티는 [[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티다.

내부 슬롯에는 직접 접근할 수 없고 간접적인 접근 방법을 제공하는 경우에 한하여 접근할 수 있는데 __proto__ 접근자 프로퍼티를 통해 간접적으로 프로토타입 객체에 접근할 수 있다.

함수의 여러형태

함수에는 여러 형태가 있다.

즉시 실행 함수

// 기명 즉시 실행 함수
(function myFunction() {
  var a = 3;
  var b = 5;
  return a * b;
}());

// 익명 즉시 실행 함수
(function () {
  var a = 3;
  var b = 5;
  return a * b;
}());

함수의 정의와 동시에 실행되는 함수이다. 최초 한번만 호출 후 재호출이 불가능하다.

내부함수

함수 내부에 정의된 함수이다.

function parent(param) {
  let parentVar = param;

  function child() {
    let childVar = 'Sun';
    console.log(parentVar + ' ' + childVar); // Hello Sun - 자식함수는 부모함수의 변수 접근 가능
  }

  child();
  console.log(parentVar + ' ' + childVar);
  //childVar is not defined - 부모함수는 자식함수의 변수에 접근 불가
}
parent('Hello');

또한 내부함수는 부모함수의 외부에서는 접근이 불가능하다.

재귀함수

재귀 함수(Recusive function)는 자기 자신을 호출하는 함수를 말한다.

// 피보나치 수열
function fibonacci(n) {
  if (n < 2) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

console.log(fibonacci(0)); // 0
console.log(fibonacci(1)); // 1
console.log(fibonacci(2)); // 1
console.log(fibonacci(3)); // 2
console.log(fibonacci(4)); // 3

// 팩토리얼
function factorial(n) {
  if (n < 2) return 1;
  return factorial(n - 1) * n;
}

console.log(factorial(0)); // 1
console.log(factorial(1)); // 1
console.log(factorial(2)); // 2
console.log(factorial(3)); // 6
console.log(factorial(4)); // 24

재귀함수는 자신을 계속해서 호출하므로 탈출 조건을 반드시 만들어 놔야 한다. 만약 조건이 없는 경우 무한루프로 인해 에러가 발생한다. 대부분의 재귀 함수는 반복문을 통해 구현이 가능하다. 때문에 재귀함수를 사용하는 것이 더욱 이해하기 쉬운 경우에만 사용하는 것을 추천한다.

콜백함수

콜백함수는 함수를 명시적으로 호출하는 것이 아닌 특정 이벤트 시에 시스템에 의해 호출되는 함수이다.

예를 들어 이벤트 핸들러 처리이다.

<body>
  <button id="btn">Click</button>
  <script>
    let button = document.getElementById('btn');
    button.addEventListener('click', function() {
      console.log('클릭!');
    });
  </script>
</body>

위의 예제를 보면 클릭 이벤트 발생시 매개변수로 전달되 사용되었다. 콜백 함수는 매개변수를 통해 전달되고 전달받은 함수의 내부에서 어느 특정시점에 실행된다.

자주 사용하는 setTimeout 같은 경우 두번째 파라미터로 보낸 시간이 경과하면 첫번째 파라미터로 보낸 콜백함수가 호출된다.

setTimeout(function () {
  console.log('1초 후 출력');
}, 1000);