전체 페이지뷰

2018년 11월 7일 수요일

Fuction expression과 arrow

자바 스크립트에서 함수는 특별한 구조가 아니라 값의 하나로 취급됩니다.
지금까지 함수에서 살펴보았던 문법은 이른바 함수선언(function declaration)이라는 것이었습니다.
function sayHi() {
  alert"Hello" );
}
cs

Function expression(함수표현식)이라고 하는 또 다른 문법을 살펴보고자 합니다.

예를 들어 이런 것입니다.
let sayHi = function() {
  alert"Hello" );
};
cs

함수가 생성되고 마치 일반 값들처럼 명시적으로 변수에 할당되었습니다. 함수가 어떻게 정의되었는가는 중요치 않고 그저 변수 sayHi에 저장된 것을 볼 수 있습니다.

그리고 나아가서는 alert를 이용하여 출력도 가능합니다.
function sayHi() {
  alert"Hello" );
}
alert( sayHi ); // shows the function code
cs

마지막 줄을 유의하시기 바랍니다. 함수를 실행하는 것이 아니라 그 코드를 보여주는 것입니다. 어떤 언어에서는 저런 경우 저장된 코드를 실행하지만 자바스크립트에서는 그렇지 않습니다.

자바스크립트에서 함수는 value입니다. 물론 특별한 value이므로 호출하려면 sayHi()로 합니다. 그렇다고 해서 value가 아닌 것은 아니라서 다른 변수로 복사도 가능합니다.
function sayHi() {   // (1) create
  alert"Hello" );
}
let func = sayHi;    // (2) copy
func(); // Hello     // (3) run the copy (it works)!
sayHi(); // Hello    //     this still works too (why wouldn't it)
cs

(2) 라인의 func라는 변수에 복사할 때 sayHi 뒤에 괄호가 없다는 점에 주의하시기 바랍니다. 만약 괄호가 있다면 함수 그 자체가 아니라 실행 결과값이 저장됩니다. 그리고 (3)에서 호출할 때는 괄호가 들어갑니다.

선언된 함수 뿐 아니라 함수 표현식으로 된 것도 가능합니다.
let sayHi = function() { ... };
let func = sayHi;
// ...
cs


함수의 callback


함수를 값으로 처리하여 넘겨주고, 함수표현식을 사용하는 예를 더 살펴 보겠습니다.

세 개의 인수를 가진 함수 ask(question, yes, no)를 작성합니다.

question: 질문 내용
yes: 답이 예스일 때 실행될 함수
no: 답이 노일 때 실행될 함수

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}
function showOk() {
  alert"You agreed." );
}
function showCancel() {
  alert"You canceled the execution." );
}
// usage: functions showOk, showCancel are passed as arguments to ask
ask("Do you agree?", showOk, showCancel);
cs

인수 ask는 callback 함수 혹은 그냥 callback이라고 불립니다.

그것은 함수를 인수로 넘겨주고 나중에 필요할 때 callback한다는 뜻입니다. 여기서는 showOk가 "yes"라는 대답의 콜백이 되고, showCancel이 "no"의 콜백이 됩니다.

함수 표현식을 사용하면 훨씬 간단하게 작성할 수 있습니다.

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}
ask(
  "Do you agree?",
  function() { alert("You agreed."); },
  function() { alert("You canceled the execution."); }
);
cs

함수들이 ask 함수의 호출 시에 직접 선언되었습니다. 이것들은 이름이 없는 소위 "익명함수"입니다. 그 함수들은 ask() 외부에서 접근할 수가 없습니다(변수에 할당된 것이 아니므로).

이런 코드들은 스크립트에서 자연스럽게 나타나고, 자바스크립트의 정신에도 부합됩니다.


함수표현식  vs  함수 선언


두 가지 방식의 핵심적 차이점을 알아봅시다.

첫번째로, 문법입니다.

function declaration: 주 코드의 흐름과는 독립적으로 선언됩니다.
// Function Declaration
function sum(a, b) {
  return a + b;
}
cs

function expression:  표현식이나 구문 내에서 만들어지는 함수입니다.
// Function Expression
let sum = function(a, b) {
  return a + b;
};
cs

문법보다 더 중요한 것은 자바스크립트 엔진에 의해 함수가 생성되는 때가 언제인가 하는 것입니다.

함수표현식은 실행 흐름에 따라 생성되고 그 이후로 사용 가능하고, 함수 선언은 전체 스크립트 또는 코드 블럭에서 사용 가능합니다.


다시 말해서, 자바스크립트가 코드 블럭(여기서 코드 블럭은 스코프를 말하는 것 같습니다)을 시핼할 준비 중일때, 블럭 안에 함수 선언이 있는지를 먼저 확인하고 생성한다는 것입니다. 이것을 "초기화 단계"라고 생각해도 좋습니다.

함수 선언의 처리가 끝나고서야 실행이 이루어집니다. 그 결과, 함수 선언으로 만들어진 함수는 그 정의가 이루어진 코드가 실행되기 이전에도 호출 가능하다는 것입니다.

예를 들어 아래 코드는 잘 동작합니다.
sayHi("John"); // Hello, John
function sayHi(name) {
  alert( `Hello, ${name}` );
}
cs

그러나 같은 순서의 함수 표현식은 동작하지 않습니다.
sayHi("John"); // error!
let sayHi = function(name) {  // (*) no magic any more
  alert( `Hello, ${name}` );
};
cs

코드 블럭 내에서 함수 선언이 이루어지면 그 함수는 그 블럭 내 어디에서나 접근이 가능합니다. 그러나 그 코드 블럭을 벗어나면 접근이 불가합니다.

이런 성질은 간혹 문제를 일으키기도 하는데, 실행 중에 얻어진 변수 age를 사용하는 함수 welcome()을 가정하고, 그 함수를 나중에 다시 사용할 계획이라고 해 봅시다.

아래의 코드는 작동하지 않습니다.
let age = prompt("What is your age?"18);
// conditionally declare a function
if (age < 18) {
  function welcome() {
    alert("Hello!");
  }
else {
  function welcome() {
    alert("Greetings!");
  }
}
// ...use it later
welcome(); // Error: welcome is not defined
cs

 함수가 가정문 내의 블럭에서 선언되었으므로, 그 밖에서는 사용할 수가 없습니다.

또 다른 예를 봅시다.

let age = 16// take 16 as an example
if (age < 18) {
  welcome();               // \   (runs)
                           //  |
  function welcome() {     //  |
    alert("Hello!");       //  |  Function Declaration is available
  }                        //  |  everywhere in the block where it's declared
                           //  |
  welcome();               // /   (runs)
else {
  function welcome() {     //  for age = 16, this "welcome" is never created
    alert("Greetings!");
  }
}
// Here we're out of curly braces,
// so we can not see Function Declarations made inside of them.
welcome(); // Error: welcome is not defined
cs


이 경우 welcome이 밖에서도 보이게 하려면 적절한 위치에 변수 welcome을 미리 선언해두고, 가정문 내에서 함수표현식으로 생성하여 welcome에 할당하는 편이 더 적절합니다.

let age = prompt("What is your age?"18);
let welcome;
if (age < 18) {
  welcome = function() {
    alert("Hello!");
  };
else {
  welcome = function() {
    alert("Greetings!");
  };
}
welcome(); // ok now
cs


또는 더 간단하게 삼항연산자를 사용할 수도 있습니다.
let age = prompt("What is your age?"18);
let welcome = (age < 18) ?
  function() { alert("Hello!"); } :
  function() { alert("Greetings!"); };
welcome(); // ok now
cs



Arrow function


함수를 생성하는 보다 간단한 방법이 하나 더 있습니다. 그것이 arrow function(람다인가 봅니다)이며, 다음과 같은 형식입니다.

let func = (arg1, arg2, ...argN) => expression
cs

보시는 바대로 화살표 오른쪽의 표현식을 평가하여,  arg1, arg2 등의 인수를 가지는 함수를 생성하고 func 에 할당해 줍니다. 이를 함수표현식 문법으로 하자면 대략

let func = function(arg1, arg2, ...argN) {
  return expression;
}
cs

인데 보시다시피 훨씬 간단하다는 것을 알 수 있습니다.

예제를 한번 보도록 하겠습니다.
let sum = (a, b) => a + b;
/* The arrow function is a shorter form of:
let sum = function(a, b) {
  return a + b;
};
*/
alert( sum(12) ); // 3
cs

만약 인수가 하나라면 괄호는 생략 가능합니다.
// same as
// let double = function(n) { return n * 2 }
let double = n => n * 2;
alert( double(3) ); // 6
cs

인수가 없다면 빈 괄호를 사용합니다(생략은 안 됩니다).
let sayHi = () => alert("Hello!");
sayHi();
cs

이 arrow function은 함수표현식과 같은 방식으로 사용이 가능합니다. 전에 나왔던 welcome() 함수를 화살표로 다시 작성해 봅시다.

let age = prompt("What is your age?"18);
let welcome = (age < 18) ?
  () => alert('Hello') :
  () => alert("Greetings!");
welcome(); // ok now
cs

처음에는 낯설어 보이고 가독성이 떨어지는 듯 하지만, 익숙해지면 아주 간단하게 함수를 작성할 수 있습니다.

화살표 옆 표현식이 여러 줄이라면 curly brace를 열고 끝에 return을 붙여줍니다.
let sum = (a, b) => {  // the curly brace opens a multiline function
  let result = a + b;
  return result; // if we use curly braces, use return to get results
};
alert( sum(12) ); // 3
cs


댓글 없음:

댓글 쓰기