Function

이짜젠·2021년 10월 2일
0

자바스크립트의 Function에 대해 정의해본다.
"함수"를 한마디로 정의하면 아래정도가 될 것 같다.

값을 입력받아 다른 값을 반환하는 코드의 묶음(또는 블럭)

"값을 반환하지 않는 함수가 있지않나요?" 라고 생각할 수 있다.
그러나 값을 반환하지 않는 것이 아니라, 반환하는 값이 undefined라고 이해하는게 맞다고 본다.
종종 다른언어(ex. PL/SQL)들은 return이 없는 코드묶음을 프로시저라는 분류로 나누기도 한다.

조금 특이한 점이라고 하면 Javascript는 함수를 값으로 인식한다.
자바스크립트에서는 값으로 인식되는 데이터들은 모두 변수에 담을 수 있다.
따라서 함수를 변수에 담을 수도, 또다른 함수의 파라미터로 넘길수도 있다.

조금 고급진 말로 "Javascript에서 함수는 일급객체다!"라고 표현한다.

// primitive 한 '값'들
const a = 10
const b = true
const c = 'c'
const d = undefined
const e = null

// 함수도 '값'
const x = function() {}
const y = function(a) {}

// 파라미터(변수)에 함수넣기
y(x)

함수 만들기

자바스크립트에서 함수는 다양한 방식으로 만들 수 있다.
크게 식(expression)을 이용한 방법과 문(statement) 이용한 방법으로 나눌 수 있다.


  • - 함수표현식
    - Fucntion 생성자

  • - 함수선언문

식 (Expression)

식(expression)은 값을 return하는 코드를 의미한다.
대표적으로 함수표현식, 정의식 등이 있다.

return 하는 값이 있기때문에 이 값을 담을 변수가 필요하다.
변수가 없다면 아래와 같은 식들은 무의미한 코드가 된다.

10 // 10을 return
true // true를 return 
1 + 1 // 2를 return 

식형태로 함수를 만들 수 있는 방법을 알아보자.

함수표현식

const sum1_1 = function (a, b) { return a + b }
const sum1_2 = function fn (a, b) { return a + b } // 함수선언문처럼 생겼지만, 자바스크립트엔진에 의해 함수선언식으로 변환되어 동작한다.
const sum1_3 = (a, b) => { return a + b } // arrow function

Function 생성자

const sum2 = new Function('a', 'b', 'return a + b') 

문 (Statement)

문(statement)은 식과 반대로 값을 return하지 않는 코드를 말한다.
대표적으로 반복문, 조건문, 대입문 등이 있다.

// 조건문
if () {  /* do something */ }

// 반복문
for (const i = 0; i < 5: i++) { /* do something */ }

// 선언문
var a;
const ac;
let al;

// 대입문
const b = 1 + 1

// 에러발생 (변수에 담을 값이 없기 때문)
const c = if () {  /* do something */ }
const d = for (const i = 0; i < 5: i++) { /* do something */ }

함수선언문

"문"형태의 코드로 함수를 만들려면 함수선언문(=함수리터럴) 이라는 형태의 코드를 사용한다.

function sum (a, b) { return a + b; }

둘의 차이점

이 둘의 가장 큰 차이는 호이스팅에 있다.
호이스팅은 선언문 코드가 제일 먼저 수행되는 자바스크립트의 메커니즘이다.

함수선언문은 값을 담는 변수의 선언과 변수에 값을 할당하는 초기화가 함께 진행된다.

함수표현식은 변수의 선언과 초기화가 각각 단계별로 나누어져 진행된다.
변수의 선언문이 상단에서 먼저 수행이되고, 변수에 함수값을 넣는 행위는 나중에 수행된다.

이는 호출가능시점의 차이로 이어지는데, 함수선언문으로 만들어진 함수는 코드 위치에 상관없이 어디서든 호출이 가능하며 함수표현식은 불가능하게 된다.

fnByStatement(); // I'm a function made by statement
fnByExpression(); // Uncaught ReferenceError: Cannot access 'fnByExpression' before initialization

function fnByStatement () {
    console.log('I\'m a function made by statement');
}

const fnByExpression = function () {
    console.log('I\'m a function made by expression');
}

어떤걸 써야할까?

개인의 취향이라고 생각하지만, 개인적으로 함수표현식을 지향한다.
자바스크립트에서는 함수를 일급객체로 취급하는만큼, 다른 일급객체들과 동일하게 표현식을 이용해서 정의하는게 일관성이 있지않나하는 생각이 든다.

그렇다고 항상 함수표현식을 사용하는건 아니고, 상황에따라 가독성이 높은 방향으로 사용하려고 한다.

전역스코프에 선언되는 함수는 함수표현식으로 선언한다.
함수내의 함수를 정의할땐 함수선언문을 사용한다.
함수내에서 함수를 정의할 때, 함수를 최상단에 적어놓으면 외부함수의 동작을 확인하기위해선 밑으로 내려야하는 번거로움이 있기때문이다.


함수객체의 속성

자바스크립트에서는 모두 객체형태로 관리된다.
함수도 객체형태로 존재하고, 함수만이 갖고있는 속성들이있다.
전체를 정리할 순 없고, 몇 개만 정리해본다.

Function.arguments

사라질 예정인 속성으로, 전달받은 매개변수들을 저장하고있다.
이 값은 Arguments 라는 객체이며, Array와 비슷한 형태로 데이터를 저장하고 있다.

Arguments는 Array와 비슷해보일 수 있지만, 엄연히 다른객체다.
Array 객체가 갖고잇는 map, forEach와 같은 메소드들은 사용할 수 없다.

function testFn (a, b, c) {
    console.log(testFn.arguments);
}

testFn() // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
testFn(1, 2) // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
testFn(1, 2, 3) // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
testFn(1, 2, 3, 4, 5) // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]

사라질 예정이기에 대안으로 사용될만한 방법들을 정리해본다.

arguments

Function.arguemnts와 동일하게 Arguments객체를 담고있는 변수입니다.
Arguments이 저장하고있는 값의 형태가 동일할뿐, 이 둘이 같은 참조값을 갖는건 아니다.
(각각 다른 메모리공간에 저장되어있다.)

함수내에서 지역변수형태로 사용할 수 있다.

function testFn (a, b, c) {
    // testFn.arguments === arguments -> false
    console.log(arguments);
}

testFn() // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
testFn(1, 2) // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
testFn(1, 2, 3) // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
testFn(1, 2, 3, 4, 5) // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]

rest 파라미터

입력받은 파라미터를 Array객체로 제공한다.

  • Arguments 객체가 아닌 Array 객체의 형태
  • arguments는 전체 파라미터값을 배열로 갖고있는반면, rest파라미터는 rest파라미터가 선언된 순서 이후의 파라미터들에 대해서만 배열로 갖고있다.
function testFn (a, b, ...rest) {
    console.log(rest);
}

testFn() // []
testFn(1, 2) // []
testFn(1, 2, 3) // [3]
testFn(1, 2, 3, 4, 5) // [3, 4, 5]

Function.caller

사라질 예정인 속성값으로, 자신을 호출한 함수(=부모 콜스택)를 의미한다.

function testFn1 () {
    console.log(testFn1.caller)
    testFn2()

    function testFn2 () {
        console.log(testFn2.caller)

        testFn3()
        function testFn3() {
            console.log(testFn3.caller)
        }
    }
}

testFn1()
/**
 * Call Stack
 *      testFn1() -> testFn2() -> testFn3()
 * 
 * Output
 *      null
 *      f testFn1() {...}
 *      f testFn2() {...}
 * /

Function.length

함수에 정의된 파라미터의 갯수다.
파라미터의 갯수를 지키지않는다고해서 에러가 발생하지는 않는다. (느슨한 타입검사)

function testFn (a, b, c) {
    console.log(testFn.length)
}

testFn() // 3
testFn(1, 2, 3) // 3
testFn(1, 2, 3, 4) // 3

Function.name

함수의 이름이다.

function testFn () {
    console.log(testFn.name)
}

testFn() // 'testFn'

참고문헌

profile
오늘 먹은 음식도 기억이 안납니다. 그래서 모든걸 기록합니다.

0개의 댓글