A Closer Look at Functions

vancouver·2023년 6월 13일
0

javascript이해하기

목록 보기
16/22
post-custom-banner

기본 매개변수

const bookings = [];

const createBooking = function (
  flightNum,
  numPassengers = 1,
  price = 199 * numPassengers
) {
  //ES5
  //   numPassengers = numPassengers || 1;
  //   price = price || 199;

  const booking = {
    flightNum,
    numPassengers,
    price,
  };
  console.log(booking);
  bookings.push(booking);
};
createBooking(`LH123`); // {flightNum: 'LH123', numPassengers: 1, price: 199}
createBooking(`LH123`, 2); // {flightNum: 'LH123', numPassengers: 2, price: 398}
createBooking(`LH123`, 5); // {flightNum: 'LH123', numPassengers: 5, price: 995}
createBooking(`LH123`, undefined, 1000); // {flightNum: 'LH123', numPassengers: 1, price: 1000}

인수 전달 방식: 값(Value) vs 참조(Reference)

const flight = `LH234`;

const vancouver = {
  name: `vancouver`,
  passPort: 23325224352,
};

const checkIn = function (flightNum, passenger) {
  flightNum = `LH999`;
  passenger.name = `Mr. ` + passenger.name;

  if (passenger.passPort === 23325224352) {
    alert(`Checked In`);
  } else {
    alert(`Wrong passPort!`);
  }
};

checkIn(flight, vancouver); // Checked In ( 체크인이 된 상황)
console.log(flight); // LH234
console.log(vancouver); 
/*
{name: 'Mr. vancouver', passPort: 23325224352}
name: "Mr. Mr. vancouver"
passPort: 182192
[[Prototype]]: Object */


// is the same as doing...
const flightNum = flight;
const passenger = vancouver;

const newPassport = function (person) {
  person.passPort = Math.trunc(Math.random() * 1000000);
};

newPassport(vancouver); // Wrong passPort! (값이 바뀌면서 체크인이 안된 상황)
checkIn(flight, vancouver);

Functions Accepting 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(`Original string: ${str}`);
  !console.log(`Transformed string: ${fn(str)}`);
  console.log(`Transformed by: ${fn.name}`);
};
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(`Original string: ${str}`);
  !console.log(`Transformed string: ${fn(str)}`);
  console.log(`Transformed by: ${fn.name}`);
};

transFormer(`JavaScript is the best!`, upperFirstWord);
/*  Original string: JavaScript is the best
    Transformed string: JAVASCRIPT is the best!
    upperFirstWord */

transFormer(`JavaScript is the best!`, oneWord);
/*  Original string: JavaScript is the best!
    Transformed string: javascriptisthebest!
    Transformed by: oneWord */

// JS uses callbacks all the time
const high5 = function () {
  console.log(`🖐`);
};
document.body.addEventListener(`click`, high5);
[`Jonas`, `Vancouver`, `Jang`].forEach(high5); // 3🖐

Functions Returning Functions

//traditional Function
const greet = function (greeting) {
  return function (name) {
    console.log(`${greeting} ${name}`);
   
//Arrow Function 
const greetArr = (greeting) => (name) => console.log(`${greeting} ${name}`);
//traditional Function
const greet = function (greeting) {
  return function (name) {
    console.log(`${greeting} ${name}`);
 /* Hey Vancouver
 	Hey Steven
	Hello Vancouver */
  };
};

///// Challenge

//Arrow Function 
const greet = (greeting) => {
  return (name) => {
    console.log(`${greeting} ${name}`);
  };
 /* Hey Vancouver
 	Hey Steven
	Hello Vancouver */
};

const greeterHey = greet(`Hey`);
greeterHey(`Vancouver`);
greeterHey(`Steven`);

greet(`Hello`)(`Vancouver`);

// Arrow Function Solution
const greetArr = (greeting) => (name) => console.log(`${greeting} ${name}`);

greetArr(`Hi`)(`Vancouver`);
// Hi Vancouver

The call and apply Methods

book(flightNum, name) {
    console.log(
      `${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`
    );
    this.booking.push({ flight: `${this.iataCode}${flightNum}`, name });
  },
   
lufthansa.book(239, `Vancouver Sam`); //Vancouver Sam booked a seat on Lufthansa flight LH239

const eurowings = {
  airline: `Eurowings`,
  iataCode: `EW`,
  booking: [],
};

const book = lufthansa.book;
// call Method
book.call(eurowings, 23, `Sarah Wiliams`); // Sarah Wiliams booked a seat on 
const lufthansa = {
  airline: `Lufthansa`,
  iataCode: `LH`,
  booking: [],
  book(flightNum, name) {
    console.log(
      `${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`
    );
    this.booking.push({ flight: `${this.iataCode}${flightNum}`, name });
  },
};

lufthansa.book(239, `Vancouver Sam`);
lufthansa.book(635, `John Smith`);
console.log(lufthansa);
/* {airline: 'Lufthansa', iataCode: 'LH', booking: Array(2), book: ƒ}
airline: "Lufthansa"
book: ƒ book(flightNum, name)
booking: Array(3)
0: {flight: 'LH239', name: 'Vancouver Sam'}
1: {flight: 'LH635', name: 'John Smith'}
2: {flight: 'LH239', name: 'Mary Cooper'}
length: 3
[[Prototype]]: Array(0)
iataCode: "LH"
[[Prototype]]: Object */

const eurowings = {
  airline: `Eurowings`,
  iataCode: `EW`,
  booking: [],
};

const book = lufthansa.book;

// Does Not Work
// book(23, `Sarah Williams`);

// Call Method
book.call(eurowings, 23, `Sarah Wiliams`); // Sarah Wiliams booked a seat on Eurowings flight EW23
console.log(eurowings);
/* {airline: 'Eurowings', iataCode: 'EW', booking: Array(1)}
airline: "Eurowings"
booking: Array(1)
0: {flight: 'EW23', name: 'Sarah Wiliams'}
length: 1
[[Prototype]]: Array(0)
iataCode: "EW"
[[Prototype]]: Object */

book.call(lufthansa, 239, `Mary Cooper`); // Mary Cooper booked a seat on Lufthansa flight LH239
console.log(lufthansa);
/* {airline: 'Lufthansa', iataCode: 'LH', booking: Array(3), book: ƒ}
airline: "Lufthansa"
book: ƒ book(flightNum, name)
booking: Array(3)
0: {flight: 'LH239', name: 'Vancouver Sam'}
1: {flight: 'LH635', name: 'John Smith'}
2: {flight: 'LH239', name: 'Mary Cooper'}
iataCode: "LH"
[[Prototype]]: Object */

const swiss = {
  airline: `Swiss Air Lines`,
  iataCode: `LX`,
  booking: [],
};

book.call(swiss, 583, `Mary Cooper`); // Mary Cooper booked a seat on Swiss Air Lines flight LX583

// Apply Method
const flightData = [583, `George Cooper`]; // George Cooper booked a seat on Swiss Air Lines flight LX583
book.apply(swiss, flightData);
///// == (같은 방식임. call Method를 사용하는걸 선호)
book.call(swiss, ...flightData);
console.log(swiss);
/* {airline: 'Swiss Air Lines', iataCode: 'LX', booking: Array(3)}
airline: "Swiss Air Lines"
booking: Array(3)
0: {flight: 'LX583', name: 'Mary Cooper'}
1: {flight: 'LX583', name: 'George Cooper'}
2: {flight: 'LX583', name: 'George Cooper'}
iataCode: "LX"
[[Prototype]]: Object */

The Bind Method

book(flightNum, name) {
console.log(`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`)
}

  const eurowings = {
  airline: `Eurowings`,
  iataCode: `EW`,
  booking: [],
};

const bookEW = book.bind(eurowings);
bookEW(23, `Steven Williams`); //  Steven Williams booked a seat on Eurowings flight EW23
// Bind method

//book.call(eurowings, 23, `Sarah Wiliams`);

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

bookEW(23, `Steven Williams`); //  Steven Williams booked a seat on Eurowings flight EW23

const bookEW23 = book.bind(eurowings, 23);
bookEW23(`Jonas Schmedtmann`); //Jonas Schmedtmann booked a seat on Eurowings flight EW23
bookEW23(`Vancouver`); //Vancouver booked a seat on Eurowings flight EW23

// 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.bind(lufthansa));

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

const addVAT = addTax.bind(null, 0.23);
// addVAT = value => value + value * 0.23;

console.log(addVAT(100)); // 123
console.log(addVAT(23)); // 28.29

const addTaxRate = function (rate) {
  return function (value) {
    return value + value * rate;
  };
};
const addVAT2 = addTaxRate(0.23);
console.log(addVAT2(100)); // 123
console.log(addVAT2(23)); // 28.29

Coding Challenge #1

/* 간단한 투표 앱을 만들어봅시다!
투표에는 질문, 사람들이 선택할 수 있는 옵션 배열, 각 옵션에 대한 응답 수 배열이 있습니다. 
이 데이터는 아래의 'poll'이라는 시작 객체에 저장됩니다. 

1. 'poll' 객체에 'registerNewAnswer'라는 메서드를 생성합니다. 이 메서드는 2가지 작업을 수행합니다:

1.1. 사용자에게 선택한 옵션의 번호를 입력할 수 있는 프롬프트 창을 표시합니다. 프롬프트는 다음과 같이 표시되어야 합니다: 

What is your favourite programming language?
0: JavaScript
1: Python
2: Rust
3: C++
(Write option number)

1.2. 입력된 숫자를 기반으로 'answers' 배열 속성을 업데이트합니다. 
예를 들어, 만약 옵션이 3이라면, 배열의 3번째 위치의 값을 1 증가시킵니다. 
입력이 숫자인지 확인하고 숫자가 의미 있는지 (예: 52번 응답은 의미가 없겠죠?) 확인하는 것이 중요합니다.

2. 사용자가 "Answer poll" 버튼을 클릭할 때마다 이 메서드를 호출합니다.

3. 'displayResults'라는 메서드를 생성하여 투표 결과를 표시합니다. 
	이 메서드는 문자열('string') 또는 배열('array')로 입력받는 매개변수('type')를 사용합니다.
 	'type'이 'array'인 경우에는 결과 배열을 그대로 console.log()를 사용하여 표시합니다. 이것이 기본 옵션입니다.
	 'type'이 'string'인 경우에는 "투표 결과는 13, 2, 4, 1입니다."와 같은 문자열을 표시합니다.
 
4. 'registerNewAnswer' 메서드 호출이 끝날 때마다 'displayResults' 메서드를 실행합니다.

보너스: 'displayResults' 메서드를 사용하여 테스트 데이터의 2개 배열을 표시합니다. 'array'와 'string' 옵션을 모두 사용하세요.
 배열을 'poll' 객체에 넣지 말고 사용하세요! 이 상황에서 'this' 키워드는 어떻게 보여야 할까요?

Test data for bonus:
  Data 1: [5, 2, 3]
  Data 2: [1, 5, 3, 9, 6, 1]
 */

const poll = {
  question: "What is your favourite programming language?",
  options: ["0: JavaScript", "1: Python", "2: Rust", "3: C++"],
  // This generates [0, 0, 0, 0]. More in the next section!
  answers: new Array(4).fill(0),

  registerNewAnswer() {
    const answer = Number(
      prompt(
        `${this.question}\n${this.options.join(`\n`)}\n(Write option number)`
      )
    );
    console.log(answer); // 1
    
    // Register answer
    typeof answer === `number` &&
      answer < this.answers.length &&
      this.answers[answer]++;

    this.displayResults();
    this.displayResults(`string`);
  },
  displayResults(type = `array`) {
    if (type === `array`) {
      console.log(this.answers); // (4) [0, 1, 0, 0]
    } else if (type === `string`) {
      // Poll results are 13, 2, 4, 1
      console.log(`Poll results are ${this.answers.join(`, `)}`);
    }
  },
};

// poll 버튼 
document
  .querySelector(`.poll`)
  .addEventListener(`click`, poll.registerNewAnswer.bind(poll));

// Bonus
poll.displayResults.call({ answers: [5, 2, 3] }, `string`);
poll.displayResults.call({ answers: [1, 5, 3, 9, 6, 1] }, `string`);
poll.displayResults.call({ answers: [1, 5, 3, 9, 6, 1] });

Immediately Invoked Function Expressions (IIFE)

즉시 실행 함수 표현 (IIFE, Immediately Invoked Function Expression)은 정의되자마자 즉시 실행되는 Javascript Function 를 말한다

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

// IIFE
(function () {
  console.log(`This will never run again`);
  const isPrivate = 23;
})();

/* (function () {
  console.log(`This will never run again`);
  const isPrivate = 23;
}) */ // 즉시 함수 실행
// (); 한번 더 출력


// console.log(isPrivate);

(() => console.log(`This will Also never run again`))();

{
  const isPrivate = 23;
  var notPrivate = 46;
}
// console.log(isPrivate);
console.log(notPrivate); //46

Closures

클로저는 함수가 생성된 실행 컨텍스트의 폐쇄된 변수 환경을 의미합니다. 이는 함수가 생성된 후에도 해당 실행 컨텍스트가 사라진 이후에도 변수 환경이 유지됩니다.

덜 형식적으로

클로저는 함수가 부모 함수의 모든 변수에 접근할 수 있게 해줍니다. 이는 부모 함수가 반환된 이후에도 지속됩니다. 함수는 외부 스코프에 대한 참조를 유지하여 스코프 체인을 시간이 지나도 유지합니다.

덜 형식적으로

클로저는 함수가 생성될 때의 변수와의 연결을 유지하여 함수가 해당 변수와의 연결을 잃지 않도록 합니다.

덜 형식적으로

클로저는 함수가 이동하는 곳마다 가지고 다니는 배낭과 같습니다. 이 배낭에는 함수가 생성된 환경에 있던 모든 변수들이 담겨 있습니다.

유의사항:
우리는 수동으로 클로저를 생성할 필요가 없습니다. 클로저는 자바스크립트의 자동 기능입니다. 우리는 닫혀 있는 변수에 명시적으로 접근할 수도 없습니다. 클로저는 현실적으로 존재하는 자바스크립트 객체가 아닙니다.

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

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

const booker = secureBooking();

// Example 1
let f;

const g = function () {
  const a = 23;
  f = function () {
    console.log(a * 2); // 46
  };
};

const h = function () {
  const b = 777;
  f = function () {
    console.log(b * 2); // 1554
  };
};

g();
f();
console.dir(f);/* 
ƒ f()
length: 0
name: "f"
prototype: {constructor: ƒ}
arguments: (...)1
caller: (...)
[[FunctionLocation]]: script.js:375
[[Prototype]]: ƒ ()
[[Scopes]]: Scopes[3]
0: Closure (g) {a: 23} // Closure의 차이를 주목
1: Script {perGroup: 1000, f: ƒ, g: ƒ, h: ƒ, boardPassengers: ƒ}
2: Global {window: Window, self: Window, document: document, name: '', location: Location, …}*/

// Re-assigning f function
h();
f();
console.dir(f);/* 
ƒ f()
length: 0
name: "f"
prototype: {constructor: ƒ}
arguments: (...)
caller: (...)
[[FunctionLocation]]: script.js:382
[[Prototype]]: ƒ ()
[[Scopes]]: Scopes[3]
0: Closure (h) {b: 777} // Closure의 차이를 주목
1: Script {perGroup: 1000, f: ƒ, g: ƒ, h: ƒ, boardPassengers: ƒ}
2: Global {window: Window, self: Window, document: document, name: '', location: Location, …}
 */

// Example 2
const boardPassengers = function (n, wait) {
  const perGroup = n / 3; // 범위밖에 perGroup 보다 우선순위

  setTimeout(function () {
    console.log(`We are now boarding all ${n} passengers`); // We are now boarding all 180 passengers
    console.log(`There are 3 group, each with ${perGroup} passengers`); // There are 3 group, each with 60 passengers
  }, wait * 1000);
  console.log(`Will start boarding in ${wait}seconds`); // Will start boarding in 3seconds
};

const perGroup = 1000; 
boardPassengers(180, 3);
post-custom-banner

0개의 댓글