본문 바로가기
IT/JavaScript & TypeScript

[JavaScript] 함수

by 저당단 2025. 11. 2.

4장 내용.

 

자바스크립트 함수의 특징

  • 자바스크립트 함수는 일급 객체(first-class object)로 다루어짐
  • 즉 변수에 할당되거나 함수에 파라미터, 반환값으로 사용되는 등, '값'처럼 다룰 수 있음
  • 다른 함수를 인수로 받거나 반환하는 함수를 고차 함수라고 함
  • 함수에 파라미터로 넘긴 변수는 값이 '복사'됨, 즉 함수 내에서 값이 변경되어도 외부에선 그대로임
  • 하지만 객체를 넘겼을 경우 함수 내부에서 객체의 속성값을 변경하면 외부에도 영향을 줌 (엄격 모드 제외)

 

함수 선언문

  • function 키워드를 통해 이름 있는 함수를 정의하는 방식
  • 호이스팅이 발생하여 정의하기 전에도 호출할 수 있음
foo();

function foo() {
    console.log("Hello, World!");
}

 

함수 표현식

  • 동일하게 function 키워드를 사용하지만 변수에 할당하거나 함수에 파라미터로 전달할 때 쓰는 방법
  • 즉, 다른 곳에서 표현식으로서 사용될 수 있음
  • 함수 이름을 지정하지 않은 익명 함수를 정의할 수 있음
const hi = function () {
    console.log("Hello, World!");
};

 

 

화살표 함수

  • 함수 표현식을 간결하게 정의할 수 있도록 함
  • 이름을 지정할 수 없음
  • 호출 시점에 this가 바인딩되지 않음
// 일반 함수

function foo() {
    console.log(this);
}

const obj = {foo};

console.log(foo);    // Window 객체
console.log(obj);    // {foo: f}  (= obj 객체)
  • 일반 함수는 위의 예시처럼 어디서 foo()를 호출했는지에 따라 foo()의 this가 달라짐
  • 하지만 화살표 함수는 어디서 호출했는지랑 상관 없이 무조건 this가 상위 스코프를 가리킴
  • 그래서 React의 컴포넌트 안에서 화살표 함수를 사용하면 무조건 컴포넌트를 가리킴
// 화살표 함수

const foo = () => {
    console.log(this);
}

const obj = {foo};

console.log(foo);    // Window 객체
console.log(obj);    // Window 객체
  • 함수가 정의된 시점의 this를 그대로 유지하기 때문에 콜백에서 유용 (아래 setTimeout 예제)
  • 기본적으로 arguments 객체를 사용할 수 없으며, 쓰려면 직접 정의해야 함.
// 일반 함수

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

foo(10, 20, 30);    // [10, 20, 30]

// 매개변수는 2개만 지정했지만 모든 인수를 가져올 수 있음
// 배열처럼 생겼지만 Array-Like 객체
// .length, arguments(0)은 되지만 forEach(), map() 같은 배열 전용 메서드는 불가
// 화살표 함수

const foo = (...args) => {
    console.log(args);   // Array-Like가 아닌 진짜 배열
};
  • setTimeout에서 화살표 함수를 사용하면 정의된 시점의 this 그대로 유지 가능
// 일반 함수

const obj = {
  value: 42,
  foo: function () {
    setTimeout(function () {
      console.log(this.value);
    }, 1000);
  }
};

obj.foo();   // undefined
// 왜 ?? -> setTimeout은 Window의 객체임
// 그래서 setTimeout 안에서 정의된 익명 함수의 this는 Window를 가리킴

// 해결 방법 1
const obj = {
  value: 42,
  foo: function () {
    const self = this;    // 수동 바인딩
    setTimeout(function () {
      console.log(self.value);
    }, 1000);
  }
};

// 해결 방법 2
const obj = {
  value: 42,
  foo: function () {
    setTimeout(function () {
      console.log(this.value);
    }.bind(this), 1000);    // 바인딩
  }
};
// 화살표 함수
// 자신의 this가 없기 때문에 바깥 this를 씀
// 그래서 콜백에서 화살표 함수를 쓰면 직관적

const obj = {
  value: 42,
  foo: function () {
    setTimeout(() => {
      console.log(this.value);
    }, 1000);
  }
};

obj.foo();    // 42
  • 객체 리터럴을 반환할 땐 중괄호만 쓰면 코드 블록으로 인식하기 때문에 겉에 소괄호로 한번 감싸줘야 함
const getName = () => ({name: "doringri"});
console.log(getName());

 

Function 생성자

  • 문자열로 코드 만들어서 실행하는 방식
  • 혹시라도 사용자 입력 들어가면 XSS에 취약하기에 잘 안씀
const fn = new Function("a", "b", "return a + b");
fn(1, 2);    // 3

 

배열 메서드

  • map 메서드는 배열의 각 요소를 가지고 만든 새로운 배열을 반환하며, 기존 배열 변환하지 않음
  • 새로운 배열을 반환하기 위한 목적이 아닌 순회 목적이면 비효율적이므로 forEach나 for...of를 사용하는 게 적절
  • filter 메서드는 조건에 맞는 요소만 가지고 새 배열을 반환함
  • reduce 메서드는 이전에 처리된 값과 각 요소를 누적 계산하여 최종적으로 하나의 결과를 반환함
// reduce는 합산이나 데이터 그룹핑에 유용
// FP에 유용 (for문 안쓰고 데이터 변환 가능하니까)

// 콜백 첫번째 파라미터(acc): 누적값, 콜백 두번째 파라미터(cur): 현재 값
// 첫번째 인수(callback): 실행할 함수, 두번째 인수(0): 초깃값
const sum = arr.reduce((acc, cur) => acc + cur, 0);

 

순수 함수

  • FP에서 자주 쓰이는 개념
  • 함수 외부 상태에 의존하거나 외부 상태를 변경하지 않음
  • 동일한 입력이 주어지면 동일한 출력을 반환함
  • sort 대신 toSorted 메서드가 도입된 것처럼 자바스크립트는 순수 함수 방향으로 가는 중
const original = [3, 2, 1];
console.log(original.toSorted()); [1, 2, 3];
console.log(original); [3, 2, 1];

 

비동기 함수

  • async 함수를 쓰면 try-catch로 예외처리 가능
  • ECMAScript 2022 이후 자바스크립트 모듈(ES Modules) 최상위에서도 await 사용 가능
  • React(SPA) 컴포넌트 모듈 최상단에서도 await를 쓸 수 있지만 JS 다운로드 후 실행 중에 브라우저에서 await가 걸리므로 사용자 입장에서는 화면이 멈춰 보임 (그래서 최상단 await 대신 useEffect나 TanStack Query 사용)
  • 하지만 Next.js의 RSC는 브라우저로 보내기 전에 서버에서 await가 실행되므로 멈춤 상태로 보이지 않음 (지연시간 동안은 브라우저에서 로딩 스피너가 표시되며, await 끝나기 전까지 Suspense + Streaming으로 일부 먼저 보여줄 수도 있음)

 

생성자 함수

  • new 연산자를 사용해 호출 가능한 함수
  • 생성자 함수가 new를 통해 호출되면 빈 객체가 만들어짐
  • 빈 객체는 생성자 함수 내부에서 this로 바인딩되며 속성을 추가할 수 있음
  • 빈 객체의 프로토타입은 생성자 함수의 prototype 속성에 연결된 객체
  • 생성자 함수가 원시값을 반환하거나 아무것도 반환하지 않으면 this로 바인딩된 객체 반환, 객체를 반환하면 그 객체 반환
  • 화살표 함수, async 함수, 메서드는 생성자 함수로 사용할 수 없음
  • 생성자 함수는 class 나오기 전까지는 많이 썼음
function Person(name, age) {
    this.name = name
    this.age = age
    return "doringri";    // 무시하고 { name: "철수", age: 20 } 반환
    // return { name: "doringri" };    // 이러면 이 객체 반환
}
Person.prototype.toString = function () {
    return `${this.name},${this.age}`;
};

const me = new Person("철수", 20)
console.log(me)    // { name: "철수", age: 20 }
console.log(me.toString())    // "철수,20"

 

바인딩된 함수

  • this를 함수에 바인딩하는 기능
  • this를 쓰고 싶은데 메서드 실행 위치가 다른 곳(아래 예시에서는 setTimeout 내부)이라 this가 깨질 때 주로 사용함
const obj = {
    name: "철수",
    sayHi() {
       console.log(this.name);
    }
};

// obj.sayHi 를 부르는 순간 메서드가 obj로부터 분리됨
setTimeout(obj.sayHi, 1000);    // undefined 뜨거나 window.name 뜸
setTimeout(obj.sayHi.bind(obj), 1000);    // "철수"
  • 엄격 모드에서는 this가 undefined이므로 bind 필수적으로 사용
"use strict";
const person = {
    name: "doringri",
    say() {console.log(`name: ${this.name}`);}
};

person.say();    // 에러
person.say.bind(person)();    // "name: doringri"
// bind()는 새 함수 반환만 하고 실행은 안함
  • this뿐 아니라 인수도 바인딩시킬 수 있음
function add(a, b) {
    return a + b;
}

const add1 = add.bind(null, 1);
const add7 = add.bind(null, 7);

console.log(add1(9));    // 10
console.log(add7(9));    // 16

 

 


예시 코드가 많아서 생각보다 길어졌음,,