[JavaScript] 함수

soyeon·2022년 7월 5일
0

"함수는 객체"👻

함수?

: 일련의 과정을 수행하기 위한 statement를 중괄호( { } )를 이용해서 하나의 실행단위로 만들어 놓은 것. 반복적인 코드를 함수화 시킨다.

<함수화의 이점>

  • 유지보수가 좋다.
  • 오류가 발생할 확률이 적어진다. -> 코드의 신뢰도 상승

<용어 정리>

  • parameter - 매개변수. 함수 내에서 선언한 지역변수, formal parameter
  • argument - 실인자, 인수, actual parameter
  • return value

함수 정의 (Definition)

함수 선언문

: function keyword 사용
function 함수이름(파라미터/매개변수) { 리턴값 }

function add(x,y) {
	return x+y;
}

리터럴과 다르게 변수에 저장되지 않는다.

함수 표현식

: 함수 리터럴로 만드는 것

var myFunc = function() {
	return x+y;
}

Function 생성자 함수

: 상속 관계를 위해 만든 것이다. 사용을 권장하지 않는다.
JavaScript engine 안에 built-in 되어있다.

var add = new Function('x', 'y', 'return x+y');

-> add라는 함수 객체가 만들어진다.

ES6 -> 화살표 함수(Arrow function)

: 축약 표현

var add = (x,y) => x + y;

함수 호출 (Call / Invoke)

: 함수는 함수 이름으로 호출하지 않는다. 함수에 대한 식별자로 호출한다.
함수식별자(argument/인수/인자);

add(3,5);
  • JavaScript의 함수는 Overloading이 발생하지 않는다.
    : 인자의 개수가 달라도 식별자만 존재하면 해당 함수를 호출 할 수 있다.
function add(x,y) {
    return x+y;
}

console.log(add(2));  // y에는 undefined가 들어간다. 호출은 가능하다.
console.log(add(2,3,4));

실행 결과 : NaN
		   5
  • arguments
    : 함수를 실행하면 넘어온 argument(실인자)들을 저장하는 곳이다.
    유사 배열 객체이다. 함수 내에서만 사용할 수 있다.
function add(x,y) {
    // arguments
    let sum = 0;
    for(var i = 0; i < arguments.length; i++) {
        sum += arguments[i]
    } 
    return x + y;
}

console.log(add(2,3,4));  // 호출은 가능

실행 결과 : 9

유사 배열 객체 (Array-like Object)
: length property를 가지고, 배열처럼 index를 사용해서 접근과 순환을 할 수 있다. 배열은 아니다. 배열이 가진 method를 모두 가지고 있지 않다.
-> ES6에서는 Rest Parameter를 제공한다.

Rest Parameter
: '...'으로 표현한다. arguments 유사 배열 객체 대신 사용한다. 진짜 Array로 사용한다. rest parameter는 parameter의 제일 마지막에 들어가야 한다. 두 개 이상 나올 수 없다.
-> 가변 인자 함수를 만들 때 사용한다.

function foo(...args) {
    console.log(arguments);
    console.log(args);
}

foo(1,2,3,4,5);

실행 결과 : [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
		   [ 1, 2, 3, 4, 5 ]

: 함수 내에서 arguments와 Rest parameter 둘 다 이용할 수 있다.
단, arrow function에서는 arguments를 쓸 수 없다. Rest parameter만 사용할 수 있다.

  • 만약 return이 없으면?
    : undefined가 return 된다.

함수 literal

: 함수의 이름은 식별자이다.
함수 이름은 함수 내부에서만 사용 가능하다. 외부로 노출되지 않는다.

var func = function add(x,y) {
				return x+y;
           }

console.log(add(3,5));

실행 결과 : ReferenceError: add is not defined 발생
var myFunc = function add(x,y) {
    return x+y;
}

console.log(myFunc(3,5));  // 식별자로 사용할 수 있다.

실행 결과 : 8

add 이름이 필요 없기 때문에 이렇게 익명 함수 형태로 많이 사용한다.

var myFunc = function (x,y) {
    return x+y;
}

기명 함수(named function) - 함수 이름이 있다.
익명 함수(anonymous function) - 함수 이름이 없다.

함수 선언문 vs 함수 표현식 hoisting

// 함수 선언문
function foo() {
    console.log('foo 함수');
}

// 함수 표현식
var add = function bar() {
    console.log('bar 함수');
}

  1. 코드를 읽으면서 함수 선언문을 보고 함수 이름과 똑같은 변수를 하나 만든다.(묵시적 - script engine에 의해 자동으로 만들어진다.) 함수 이름과 똑같은 변수가 특정 메모리를 가리킨다.
  2. 특정 메모리에 foo 함수 내용을 넣고, 1번에서 변수가 가르키는 메모리에 함수 내용이 적힌 주소를 넣는다.
  3. 코드를 읽으면서 add 식별자를 특정 메모리를 가리키게 하고 undefined를 넣는다.(0x30에 undefined를 넣는다.)
  4. 코드가 실행되면, bar 함수 내용을 특정 메모리에 넣고, add 식별자가 가르키는 메모리 값을 bar 함수 내용이 적힌 메모리 주소로 바꾼다.

creation phase : 코드를 쭉 읽는 것
execution phase : runtime

함수 선언문은 runtime 이전에 만들어진다.
함수 표현식은 runtime에 만들어진다.

foo();  // 호출 된다.
add(); // add is not a function

// 함수 선언문
function foo() {
    console.log('foo 함수');
}

// 함수 표현식
var add = function bar() {
    console.log('bar 함수');
}

JavaScript에서는 함수 표현식을 사용하는 것이 좋다!
선언문은 호이스팅 되어서 사용될 수 있기 때문이다.

IIFE (Immediately Invoked Function Expression) - 즉시 실행 함수

: 함수를 선언함과 동시에 호출한다. 함수를 재사용 할 수 없다.
JavaScript 특유의 함수이다.

원래는 선언과 호출이 나뉘어져 있다.

function add() {
    let x = 10;
    let y = 20;

    console.log(x + y);
}

add();

실행 결과 : 30

선언과 호출을 동시에 한다.

(function add() {
    let x = 10;
    let y = 20;

    console.log(x + y);
}());

실행 결과 : 30

재사용을 못하기 때문에 함수 이름을 부여하지 않는다. (anonymous function)

(function() {
    let x = 10;
    let y = 20;

    console.log(x + y);
}());

실행 결과 : 30

IIFE를 사용하는 이유

  • 전역 변수를 지역 변수화 한다.
    : 여러 스크립트 파일을 불러와서 사용하면 전역변수들이 같은 공간을 공유해 같은 이름의 변수가 생기면 충돌이 발생할 수 있다. 즉시 실행 함수로 묶어 놓으면 각각의 파일에서 같은 이름의 변수가 있어도 지역변수로 여겨져 충돌이 발생하지 않는다.

전역 변수 사용을 줄여야 하는 이유

  • 가독성이 나빠진다. (오류의 여지가 많다.)
  • 메모리 resource를 소모한다.
  • 변수를 늦게 찾는다. (scope chain의 가장 상단에 위치하기 때문이다.)
  • 다른 file과 변수 충돌이 생길 수 있다.

-> 즉시 실행 함수로 바꾸는게 좋다.

중첩함수 (내부함수)

: nested function (inner function)

var x = 100;  // global scope(전역변수)
var y = 200;  // global scope(전역변수)

// outer function
function outer() {
    let x = 0;  // function level scope(지역변수)

    // inner function
    function inner() {
        let x = 10;  // function level scope(지역변수)
        console.log(y);
    }
}

외부함수 (outer function)

: 중첩함수를 가지고 있는 바깥쪽 함수

scope chain
: 모든 scope는 chain으로 이루어져 있다.
지역변수부터 찾기 시작해서 전역변수까지 올라간다. 전역변수는 scope chain의 가장 상단에 있기 때문에 전역변수를 사용하면 속도가 느려진다.

first-class citizen/object (1급 객체)

: 네가지 조건을 만족하는 객체를 1급 객체로 부른다.
함수형 언어에서 통용되는 용어이다.

  1. 익명의 literal로 생성이 가능하다.
    : 동적으로 생성이 가능하다. (runtime에 생성이 가능하다.)
    JavaScript 함수는 이 조건에 만족한다.

  2. 객체가 변수나 자료구조에 저장이 가능하다.
    : 다른 객체에 property로 저장이 가능하다.
    JavaScript 함수는 이 조건에 만족한다.

  3. 객체가 다른 함수의 인자로 전달이 될 수 있어야 한다.
    : 함수를 다른 함수의 인자로 넘길 수 있다.
    JavaScript 함수는 이 조건에 만족한다.

  4. 함수의 리턴값으로 객체를 사용할 수 있다.
    : 함수의 리턴으로 함수가 될 수 있다.
    JavaScript 함수는 이 조건에 만족한다.

-> JavaScript의 함수는 1급 객체이다.

함수 사용시 발생하는 문제

잘 만들어서 사용하고 있는 함수가 이미 존재한다.

그런데 이 함수의 기능을 변경(추가) 해야 한다.
-> i가 홀수인 경우만 출력하게 바꾸고 싶다.

기존의 코드
: n이 입력되면 0부터 n개를 출력한다.

function repeat(n) {
    for(var i=0; i<n; i++) {
            console.log(i);
    }
}

문제 해결 방법

  1. 원래 있던 함수를 수정한다.
    : 리스크가 존재한다.
function repeat(n) {
    for(var i=0; i<n; i++) {
        if(i % 2) {
            console.log(i);
        }
    }
}
  1. 함수를 새로 추가해서 만든다.
function repeat1(n) {
    for(var i=0; i<n; i++) {
        if(i % 2) {
            console.log(i);
        }
    }
}
  1. 함수를 추상화 시켜서 인자로 받아서 사용한다.
    : 고차 함수, 콜백 함수
function repeat(n, f) {  // 고차 함수
    for(var i=0; i<n; i++) {
        f(i)
    }
}

let logAll = function(i) {  // 콜백 함수
    console.log(i);
}

let logOdd = function(i) {  // 콜백 함수
    if(i % 2) {
        console.log(i);
    }
}

repeat(5, logOdd);
repeat(5, logAll);

콜백 함수(Callback Function)
: 고차 함수와 결합하는 함수
ex) 이벤트 처리, AJAX
고차 함수(Higher-Ordered Function)
: 콜백 함수를 받아서 합쳐서 하나의 기능을 하는 함수

Scope

: "식별자가 유효한 범위"

scope chain
: JavaScript engine이 식별자를 판단할 때 사용하는 메커니즘

JavaScript engine은 코드를 실행할 때 문맥(context)을 고려한다. 현재 실행 중인 코드가 어디에 있는 코드인지, 코드 주변 정보를 파악해서 실행한다. 이런 문맥이 저장되어 있는 곳을 context environment라고 한다.
=> lexical environment를 고려해서 실행한다.
=> 이를 실제로 구현한 것이 execution context이다.

함수 Scope

함수가 호출되었을 때, scope를 함수가 호출된 곳을 기준으로 설정할 것인지, 함수가 정의된 곳을 기준으로 설정한 것인지 애매하다.

  • dynamic scope(동적 scope)
    : 함수가 호출된 곳을 기준으로 scope를 설정한다.
  • static/lexical scope(정적 scope)
    : 함수가 정의된 곳을 기준으로 scope를 설정한다.
    대부분의 프로그래밍 언어(JavaScript 포함)는 해당 scope(정적 스코프)를 따른다.
var x = 1; // 전역 scope의 전역 변수

function foo() {
    var x = 10;  // 지역 scope의 지역 변수
    bar();
}

function bar() {
    console.log(x);
}

foo();

실행 결과 : 1

-> 따라서 위의 코드는 정의된 곳을 기준으로 scope를 설정한다.

0개의 댓글