Post

(JS) 함수 1. 유효범위(scope), callback, 클로저(closure), 접근 제어

함수선언식 vs 함수표현식

  • 함수선언식
1
functino foo( ) {}
  • 함수표현식 ( 함수리터럴 )
1
var foo = function () {};
  • 일반적으로 함수 표현식을 사용하는 것이 좋다.

  • JS를 잘 사용하기 위해서는 함수도 값이라는 것을 이해하는 것이 중요하기 때문.
  • 이라고 “자바스크립트 핵심 가이드”에서 얘기하고 있음

  • 일반적으로 함수를 선언 할 때, 선언식으로 하냐 표현식으로 하냐 관점에서는 hoisting 정도가 차이점이고,
  • JS가 함수표현식을 지원하기 때문에 = 함수는 값 = [클로저, 일회용 함수(즉시eval), 함수를 인자로 전달]이 가능
hoisting 규칙
  • let/const 변수는 hoisting 되지 않는다.
  • var 변수는 hoisting되어 선언부만이 유효스코프 최상단으로 이동 한다

  • var는 function-scope이기 때문에 함수 최상단으로 호이스팅된다.

  • 함수표현식var를 이용하기 때문에 선언부가 호이스팅된다.

  • 따라서 일찍 “호출”하는 경우 에러가 발생한다.
  • 선언부가 호이스팅 되는거라 변수 접근 자체는 가능하다.

  • 함수선언식 은 hoisting되어 함수선언 및 바디가 유효스코프 최상단으로 이동한다

  • 그래서 어디 정의부보다 위쪽에서 “호출”하는 것이 가능하다
유효범위 ( Scope )

원래 C언어 유형의 구문을 가진 모든 언어는 블록 유효범위가 있다. 블록 내에서 정의된 모든 변수는 블록의 바깥쪽에서 접근할 수 없고, 블록의 실행이 끝나면 해제된다. 예를 들면 function, for등 모든 { }로 묶인 부분이 블록 유효범위 적용 대상이 된다. JS는 블록 유효범위가 없다. 대신 함수 Scope(유효범위) 가 있다. 그래서 for문이 끝나도 변수가 사라지지 않기 때문에, for내부에서 선언한 변수를 js for 외부에서도 접근할 수 있다. 또한 반복문 안에 변수를 넣어도 변수의 생성과 삭제가 루프만큼 일어나는 것이 아니다. 보통 블록 유효범위가 적용되는 대부분의 언어들은 변수를 사용하기 직전에 선언하기를 권고하고 있다. 그러나 JS는 블록 유효범위를 지원하지 않기 때문에 이와 무관하며, 함수에서 사용하는 모든 변수를 함수 첫 부분에 선언하는 것이 최선의 방법이라고 한다. - 더글라스 크락포드 * 반복문 내에 선언해도 되지만 유지보수 측면에서 좋지 않은 듯.

함수 이름 재정의

함수도 변수이기 때문에 prompt같은 함수에 사용되는 이름을 재정의하면 더 이상 함수 prompt()는 사용할 수 없다.

1
2
prompt = "kkz";
alert(prompt);
1
2
prompt ---X---> function prompt() { prompt code... }
---------> var prompt = "value"
함수를 호출할 때, 생성할 때 입력했던 인수의 개수보다 많거나 적게 입력해도 동작한다.

적게 입력하는 경우 undefined가 되고, 많게 입력하는 경우 무시한다.

arguments 변수

JS에서 모든 함수는 명시되어 있는 매개변수에 더해서 자체적으로 arguments 변수를 가지고 있다. arguments 변수는 파라미터의 배열이므로, 이를 이용해 가변길이 입력 을 받을 수 있다. * 엄밀히 말하면 실제 배열은 아니고 배열같은 객체다. 따라서 메소드가 몇개 없다.

내부 함수

함수 내부에서도 함수를 정의할 수 있다.

일회용 함수
1
2
3
(function ($) {
    code...
})($);

선언과 동시에 호출할 수 있다. 이게 언제 유용하냐면, 네임스페이스 충돌 예방할 때. 결국 함수 안에서 실행되는 것이기 때문에 감싼 부분 최상단 스코프에 변수를 선언해도 이는 전역 변수가 아니라 함수 변수가 된다.

파라미터로 함수 넘기기 ( callback )

함수 자료형을 지원하기 때문에 파라미터로 함수를 넘길 수 있다. 주로 callback에 사용되며 익명 함수로 바로 작성해서 넘기는 경우가 많다. 이벤트나, 비동기식 함수에 사용된다.

콜백 함수는 어떤 이벤트가 발생하거나, 비동기 작업이 완료되면 이 함수를 실행하라는 의미로 전달하는 함수를 말한다.

1
2
3
4
function foo(callback){...}
var cf = function () {...}
foo(cf);
foo(function () {!!!});
함수 리턴

함수 자료형을 지원하기 때문에 함수를 리턴하는 것 또한 가능하다. 이는 클로저에 사용된다.

* 리턴된 함수를 바로 실행할 수도 있다. }();같이 ();로 끝나게 되는 경우가 그렇다. 리턴 함수가 아니라 리턴 함수의 실행 결과를 반환받고 싶은 경우 적어주게 된다.

클로저 (closure)

클로저란 내부 함수가 외부 함수1의 변수를 참조하는 상황에서 외부 함수1이 내부 함수를 리턴하는 것을 말한다.

리턴되는 내부 함수가 외부함수1의 변수를 참조할 경우, 이 내부 함수를 클로저 함수라고 부른다. 클로저 함수를 리턴하고 이 클로저 함수에서 외부함수1의 지역변수를 이용하는 경우 외부함수1이 종료되어도 클로저 함수에서 참조하는 지역변수는 사라지지 않는다.

** 클로저는 참조를 가지고 있는 것이지, 값을 가지고 있는 것이 아니다.

그래서 외부함수2에서 외부함수1을 호출하면서 리턴받은 클로저 함수를 변수에 넣어 사용할 수 있다.

즉, 클로저 패턴은 반드시 함수의 실행 결과를 리턴하는 것이 아니라, 함수 자체를 리턴한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo(name) {
  var output = "hello " + name;
  return {
    get_output() {
      return output;
    },
  };
}

var foo1 = foo("closure1");
alert(foo1.get_output());
foo("closure2")();
foo1();

JS는 함수 유효범위를 적용하기 때문에 함수의 호출과 종료 시 변수의 생성과 삭제가 일어난다. 위 함수가 만약 다음과 같은 형태라면, 변수 output의 생성과 삭제가 루프 만큼 일어난다.

1
2
3
4
function foo(name) {
  var output = "hello " + name;
  return output;
}

그러나 클로저를 사용했기 때문에 foo()를 호출해 리턴값을 foo1에 할당하고 foo()가 종료되어도 var output은 사라지지 않아 생성과 삭제가 계속 일어나지 않는다.

접근제어

클로저를 사용하면 변수에 직접 접근하지 못하고 메소드를 통해야만 접근할 수 있도록 만들 수 있으므로 접근제어를 구현할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var myObject = (function () {
  var value = 0;
  var output = {
    increment: function (inc) {
      value += typeof inc === "number" ? inc : 1;
    },
    getValue: function () {
      return value;
    },
  };
  return output;
})();

myObject.increment(3);
alert(myObject.getValue());
alert(myObject.value); // undefined
  • 내부 함수 value는 외부 함수에 있는 변수의 복사본이 아니라 실제 변수에 접근한다는 것에 유의한다.
  • return { };으로 바로 리턴해도 되고, output = { }; return output; 해도 동일하다.
This post is licensed under CC BY 4.0 by the author.