자바스크립트 함수 심화 – 콜백 함수, call/apply/bind 메소드, IIFE, 클로저

디폴트 파라미터(Default Parameters)

const bookings = [];

const createBooking = function (flightNum, 
  numpassengers = 1, 
  price = 199 * numpassengers) {
  // ES 5
  // numpassengers = numpassengers || 1;
  // price = price || 199;

  const booking = {
    flightNum,
    numpassengers,
    price
  }
  console.log(booking);
  bookings.push(booking);
}

createBooking('LH123');
createBooking('LH123', undefined, 1000);
  • 선언해놓은 파라미터를 디폴트 값에 사용할 수 있다.
  • 중간에 있는 파라미터를 디폴트 값으로 설정하고 싶다면 undefined를 사용하면 된다.

함수에 인자를 넘겨주는 방식(Value vs. Reference)

const flight = 'LH234'
const jonas = {
  name: 'Jonas Schemedtmann',
  passport: 2345667,
}

const checkIn = function (flightNum, passenger) {
  flightNum = 'LH999'; // 원시 타입의 값은 함수 안에서 사용할 값을 새로 생성한다.
  passenger.name = 'Mr. ' + passenger.name; // 참조 타입의 값은 인자로 넘어온 값을 참조하므로 값이 변경된다.

  if (passenger.passport === 2345667) {
    alert('Checked in');
  } else {
    alert('Wrong passport');
  }

}

checkIn(flight, jonas);
console.log(flight); // LH234
console.log(jonas); // {name: 'Mr. Jonas Schemedtmann', passport: 2345667}

  • 원시 타입의 값은 함수 안에서 사용할 값을 새로 생성한다.
  • 참조 타입의 값은 인자로 넘어온 값을 참조하므로 값이 변경된다.
  • 자바스크립트는 객체와 같은 참조 타입 인자를 pass in a reference(memory address of object), 하지만 메모리 주소를 가진 ‘값’으로 넘겨준다.
    • C++ 같이 인자를 pass by reference 하는 것은 아니다.
  • 원시 타입과 참조 타입의 차이는 참고

First-class & Higher-order

일급 함수 First-class Functions

  • 자바스크립트는 함수를 일급 시민(first-class citizens)으로 취급한다.
  • 이 말은 함수는 단순히 값(value)로 취급한다는 말이다.
  • 함수는 객체(object)의 일종이다.
  • 변수나 프로퍼티로 저장하는 등의 활용이 가능하다.
  • 함수를 함수의 인자로 사용할 수 있다.
  • 함수의 리턴 값으로 함수를 사용할 수 있다.
  • 함수의 메서드를 호출할 수 있다.(e.g. bind)

고차 함수 Higher-order Functions

  • 고차함수는 다른 함수를 인자로 받는 함수나 새로운 함수를 리턴하는 함수를 의미한다.
  • 이것은 자바스크립트에서는 함수가 first-class여서 가능한 것이다.
  • 고차 함수의 인자로 사용되는 다른 함수를 콜백 함수라고 부른다.

콜백 함수(Callback Functions)

const oneWord = function (str) {
  return str.replace(/ /g, '').toLowerCase();
}

const upperFirstWord = function (str) {
  const [first, ...others] = str.split(' ');
  return [first.toUpperCase(), ...others].join(' ');
};

// Higher-order function
const transformer = function (str, fn) {
  console.log(`Transformed string: ${fn(str)}`);
  console.log(`Transformed by: ${fn.name}`);
}

transformer('JavaScript is the best!', upperFirstWord);
transformer('JavaScript is the best!', oneWord);
  • transformer 함수에 넘긴 fn 함수를 콜백 함수라고 부른다.
  • 고차 함수 안에서 실제로 호출될 때 콜백을 하는 함수라는 의미로 이해하면 쉽다.
  • 추상화(Abstraction)을 가능케 하는 기능이다.
    • 추상화: 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것
  • 자바스크립트를 사용할 때 정말 자주 쓰이는 핵심 기능이다.

함수를 리턴하는 함수(Functions Returning Functions)

const greet = function (greeting) {
  return function (name) {
    console.log(`${greeting} ${name}`);
  }
}

const greeterHey = greet('Hey');
greeterHey('Jonas');

greet('Hello')('Jonas');

// c.f. Using Arrow function
const greetArr = greeting => name => console.log(`${greeting} ${name}`);
greetArr('Hi')('Jonas')
  • 함수형 프로그래밍에서 유용하게 쓰이는 기능이다.

Call & Apply & Bind 메소드

const lufthansa = {
  airline: 'Lufthansa',
  iataCode: 'LH',
  bookings: [],
  book(flightNum, name) {
    console.log(`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`);
    this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name });
  },
};

lufthansa.book(239, 'Jonas Schmedtmann');
lufthansa.book(635, 'John Smith');

const eurowings = {
  airline: 'Eurowings',
  iataCode: 'EW',
  bookings: [],
}

const book = lufthansa.book;

// regular function call -> this를 사용할 수 없으므로 에러
// book(23, 'Sarah Williams'); 
  • this 키워드를 사용하는 객체의 메서드를 외부에서 새로운 함수로 선언하면 어떻게 사용할 수 있을까?

Call 메소드

book.call(eurowings, 23, 'Sara Williams') // 첫번째 인자로 this에 들어갈 객체를 넣는다.
console.log(eurowings);

Apply 메소드

const flightData = [583, 'George Cooper'];
book.apply(eurowings, flightData); // 배열을 넣어야 한다는 차이가 있다.
book.apply(eurowings, [583, 'George Cooper']);

book.call(eurowings, ...flightData);
  • 모던 JS에서는 call 메소드와 스프레드 연산자를 사용하면 되므로 apply 메소드를 잘 사용하지 않는다.

Bind 메소드

const bookEW = book.bind(eurowings);
const bookLH = book.bind(lufthansa);
const bookLX = book.bind(swiss);

bookEW(23, 'Steven Williams');

// preset을 설정할 수 있다.
const bookEW23 = book.bind(eurowings, 23);
bookEW23('Jonas Schemedtmann');

// Partial application
const addTax = (rate, value) => value + value * rate;
console.log(addTax(0.1, 200));

const addVAT = addTax.bind(null, 0.23); // preset으로 설정하지 않을 값은 null로 지정
  • bind 메소드는 preset을 세팅할 수 있으므로 활용도가 높다.(Partial application)
// with event listeners
lufthansa.planes = 300;
lufthansa.buyPlane = function () {
  console.log(this);
  this.planes++
  console.log(this.planes)
};

lufthansa.buyPlane();

// document.querySelector('.buy').addEventListener
//   ('click', lufthansa.buyPlane); // 이 함수의 this는 button이 되어서 정상적으로 작동하지 않음

document.querySelector('.buy').addEventListener
  ('click', lufthansa.buyPlane.bind(lufthansa)); // bind 메서드를 사용, 새로운 함수를 콜백 함수로 사용
  • 또한 이벤트 리스너의 콜백 함수로 사용할 때 유용하다.

IIFE(Immediately Invoked Function Expressions)

const runOnce = function () {
  console.log('This will never run again');
}
runOnce();

// IIFE
(function () {
  console.log('This will never run again');
})();

(() => console.log('This will never run again'))();
  • 1회만 호출하고 재사용될 필요 없는 함수를 IIFE로 선언할 수 있다.
  • 선언하고 즉시 호출되므로 Immediately Invoked라고 표현한다.
  • 코드 블럭 안에서 선언된 변수는 블럭 밖에서 접근할 수 없으므로 캡슐화에 사용될 수 있다.

클로저(Closures)

const secureBooking = function () {
  let passengerCount = 0;

  return function () {
    passengerCount++;
    console.log(`${passengerCount} passengers`);
  }
}

const booker = secureBooking();

booker(); // 1 passengers
booker(); // 2 passengers
booker(); // 3 passengers
  • 클로저는 함수가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성되며, 함수가 생성될 때 클로저도 함께 생성된다.
  • 부모 함수가 종료되어 부모 함수의 EC(Execution Context)가 반환되어도 부모 함수의 VE(Variable Environment)=클로저는 유효하다.
  • 스코프 체인보다 클로저의 우선순위가 높으므로, JS 엔진은 함수를 실행할 때 클로저의 변수를 먼저 찾아본다.
  • A closure is the closed-over variable environment of the execution context in which a function was created, even after that execution context is gone.
  • A closure gives a function access to all the variables of its parent function, event after that parent function has returned. The function keeps a reference to its outer scope, which preserves the scope chain throughout time.

console.dir()

Leave a Reply

Your email address will not be published. Required fields are marked *