5주차 TIL & 회고

강성일·2023년 5월 15일
0
post-thumbnail

✅ TIL


function filterArray(arr, conditionFunc) {
return arr.filter(conditionFunc);
}

const numbers = [1, 2, 3, 4, 5];
const isEven = (num) => num % 2 === 0;

const filteredNumbers = filterArray(numbers, isEven);
console.log(filteredNumbers); // [2, 4]

/* 위의 예시에서 filterArray 함수가 고차 함수이면서 일급 객체입니다.

고차 함수: filterArray 함수는 다른 함수인 conditionFunc를 인자로 받아 사용합니다.
이렇게 함수를 인자로 받거나 반환할 수 있는 함수를 고차 함수라고 합니다.
filterArray 함수는 conditionFunc 함수를 실행하기 위해 활용되며, 이렇게 함수를 다루는 특성을 가지고 있기 때문에 고차 함수입니다.

일급 객체: JavaScript에서 함수는 일급 객체입니다.
이는 함수가 값으로 다뤄질 수 있다는 의미입니다.
위의 코드에서 filterArray 함수는 다른 함수(conditionFunc)를 인자로 받아 사용하고 있습니다.
이는 함수를 값으로 다루고 있는 것입니다.
또한, filterArray 함수는 함수를 반환하지는 않지만, 함수를 값으로 다룰 수 있는 능력을 가지고 있기 때문에 일급 객체입니다.

따라서 filterArray 함수는 고차 함수이면서 JavaScript에서의 일급 객체인 함수입니다. */

고차함수..

첨엔 무슨 말인지도 모르겠고 문제 이해가 아예 안됐다.

함수 안에 함수를 왜 넣고, 그 함수가 받는 매개변수가 여러 개 일수도 있다는…

이게 뭔소리지..?

첫 날엔 아예 이해가 안되서 천천히 보기만 했다.

문제를 풀어도 감이 안잡혔다.

이게 왜 이렇게 되는건지도 모르겠고, 어쩔 때 쓰는건지도 모르겠고.. 하..


고차함수는 레스토랑 주인장 (함수를 관리)
일급객체는 재료 (값이 담긴다)

function adder(added) {
  return function (num) {
    return num + added;
  };
}

/*
 * 함수 adder는 다른 함수를 리턴하는 고차 함수입니다.
 * adder는 인자 한 개를 입력받아서 함수(익명 함수)를 리턴합니다.
 * 리턴되는 익명 함수는 인자 한 개를 받아서 added와 더한 값을 리턴합니다.
 */

// adder(5)는 함수이므로 함수 호출 연산자 '()'를 사용할 수 있습니다.
let output = adder(5)(3); // -> 8
console.log(output); // -> 8

// adder가 리턴하는 함수를 변수에 저장할 수 있습니다.
// javascript에서 함수는 일급 객체이기 때문입니다.
const add3 = adder(3);
output = add3(2);
console.log(output); // -> 5

주어진 코드에서 adder 함수는 일급 객체이면서 고차 함수다.


자세히 설명하자면, 이유는 다음과 같다.

adder 함수는 고차 함수다.

즉, adder 함수는 인자로 받은 added 값을 기억하고 있는 클로저를 생성하여 반환합니다.
반환된 함수는 added 값과 입력받은 num을 더한 값을 반환하는 함수입니다.


또한, adder 함수는 일급 객체다.

일급 객체의 조건에는 다음과 같은 요소가 포함된다:

  1. 변수에 할당할 수 있어야 한다.
    const add3 = adder(3);와 같이 adder 함수를 변수 add3에 할당할 수 있다.

  2. 함수의 인자로 전달할 수 있어야 한다.
    anotherFunction(adder)와 같이 adder 함수를 다른 함수에 인자로 전달할 수 있다.

  3. 함수의 반환값으로 사용할 수 있어야 한다.
    const add5 = adder(5);와 같이 adder 함수의 반환값을 다른 변수에 할당할 수 있다.

  4. 자료 구조의 속성으로 사용할 수 있어야 합다. 객체의 속성으로 함수를 할당할 수 있다.

따라서 adder 함수는 일급 객체이면서 고차 함수이다.


💡 쉽게 느낌만 정리 ..?


// 함수를 변수에 할당
const greet = function (name) {
  console.log(`Hello, ${name}!`);
};

// 함수를 인자로 전달
function executeFunction(func) {
  func('John');
}

executeFunction(greet); // 출력: Hello, John!

// 함수를 반환값으로 사용
function createGreeting() {
  return function (name) {
    console.log(`Hello, ${name}!`);
  };
}

const greetJohn = createGreeting();
greetJohn('John'); // 출력: Hello, John!

// greet는 일급객체이기만 함
// 익명 함수를 변수에 할당하거나 인자로 전달할 수 있는 것은 일급 객체로서의 특징입니다.
// 하지만 고차 함수는 함수를 인자로 받거나 반환하는 함수를 의미
// 함수를 인자로 받는 고차 함수
function higherOrderFunc(callback) {
console.log('Higher order function');
callback();
}

function callbackFunc() {
console.log('Callback function');
}

higherOrderFunc(callbackFunc); // 출력: Higher order function, Callback function

// 함수를 반환하는 고차 함수
function createMultiplier(multiplier) {
return function (num) {
return num * multiplier;
};
}

const double = createMultiplier(2);
console.log(double(5)); // 출력: 10

// higherOrderFunc 함수와 createMultiplier 함수는 고차 함수로써 함수를 인자로 받거나 함수를 반환

= 할당함 → 일급객체 (할당)

= 이 아니라 () 받음 → 고차함수 (받거나 반환)



🔥 고차함수 Review


// 7번
function callbackOnly(callback, response) {
  // TODO: 여기에 코드를 작성합니다.
  if (response.status === "fail") {
    return 'Something went wrong!'
  }
  else if (response.status === "success") {
    return callback(response.data); // 함수 안에서 객체를 할당시켜 값을 다룰 수 있다는 것이 포인트 !
  }
}
// 8번
function mapCallback(func, arr) {
  // TODO: 여기에 코드를 작성합니다.
  let result = [];
  for (let el of arr) { // for of 의 el가 아예 arr 안쪽 요소를 다룬다는 것을 아는지 사실여부 ?
    result.push(func(el)) // 7번과 마찬가지로 함수 안에 arr 배열 요소값을 넣을 수 있다는 것을 아는지
  }
  return result;
}
// 9번
function filterCallback(func, arr) {
  // TODO: 여기에 코드를 작성합니다.
  let result = [];

  for (let el of arr) {
    if (func(el) === true) { // func은 콜백함수로써, boolean 타입을 반환하는 것을 아는지
      result.push(el)
    }
  }
  return result;
}
// 10번

function removeElement(arr, discarder) {
  // TODO: 여기에 코드를 작성합니다.
  
  // filter 안에 익명의 함수를 만들어서 매개변수로 discarder와 비교할 수 있음
  return arr.filter(function (el) {
    if (el !== discarder) {
      return true;
    }
    return false;
  });
}

or

function removeElement(arr, discarder) {
  function isNotDiscarder(el) { // 기명 함수 , 익명이 더 좋아보임
    return el !== discarder;
  }

  let result = arr.filter(isNotDiscarder);
  return result;
}

/*위의 코드에서 익명 함수(anonymous function)가 콜백 함수(Callback function) 역할을 수행합니다.

콜백 함수는 다른 함수에 전달되어 특정한 동작을 수행하기 위해 호출되는 함수입니다.
위의 코드에서 filter 메서드에 전달되는 함수가 바로 콜백 함수입니다.
filter 메서드는 주어진 배열의 각 요소에 대해 콜백 함수를 호출하고, 콜백 함수의 반환값이 true인 요소들만을 새로운 배열로 필터링합니다.

익명 함수는 filter 메서드의 콜백 함수로 사용되어 배열에서 discarder와 일치하지 않는 요소들을 필터링하는 역할을 수행합니다.
각 요소가 익명 함수에 전달되고, 익명 함수의 조건문에서 el과 discarder를 비교하여 일치하지 않는 경우 true를 반환하고, 일치하는 경우 false를 반환합니다.

결과적으로, arr.filter 메서드의 콜백 함수로 사용된 익명 함수는 배열에서 discarder와 일치하지 않는 요소들을 필터링하여 새로운 배열을 반환합니다.*/

// 내가 생각한 오답
function removeElement(arr, discarder) {
let result = [];
if(!arr.filter(discarder)){
	result.push(arr.filter(!discarder)) // 이렇게 하면 arr 배열 안에 배열이 또 들어감
}
return result;
}

/* 1. 조건문 내의 !arr.filter(discarder) 부분이 잘못되었습니다.
arr.filter(discarder)는 discarder와 일치하는 요소들을 필터링한 새로운 배열을 반환합니다.
따라서, 조건문의 의도는 discarder와 일치하는 요소가 없는 경우에 result 배열에 필터링된 배열을 추가하려는 것일 것입니다.
하지만, !arr.filter(discarder)는 필터링된 배열을 논리 부정하여 false를 반환하는 것이 아니라, 필터링된 배열이 비어있는 경우 true를 반환합니다. 이로 인해 조건문의 로직이 역전되어 올바른 동작을 하지 않습니다.

2. arr.filter(!discarder) 부분도 문법적으로 올바르지 않습니다.
!discarder는 논리 부정 연산자를 discarder에 적용하는 것으로, 의도한 동작과 맞지 않습니다.
filter 메서드의 인자로는 콜백 함수가 전달되어야 합니다.*/
// 20번
function square(number) {
  return number * number;
}

function getSquaredElementsAtProperty(obj, property) {
  // TODO: 여기에 코드를 작성합니다.
  if (Array.isArray(obj[property])) {
    return obj[property].map(function (el) {
      return typeof el === 'number' && square(el)
    })
  }
  return [];
}

/* 위의 코드에서 캐치 포인트는 map 메서드의 콜백 함수 내부입니다.
콜백 함수는 각 배열 요소에 대해 호출되며, 해당 요소에 대한 변환 작업을 수행합니다.

square(el)은 함수 호출 표현식으로, 함수를 호출하고 결과를 반환하는 역할을 합니다.
함수 호출 표현식은 그 자체로 일급 객체가 될 수는 없습니다.

square 함수가 일급 객체다.

일급 객체는 다음 조건을 충족하는 개체입니다:

square(el)는 함수 호출 표현식이므로, 변수에 할당하거나 함수의 매개변수로 전달하거나 함수의 반환값으로 사용할 수 있습니다.

따라서, square(el)은 일급 객체의 특징을 가지고 있습니다.

고차 함수(higher-order function)는 함수를 인자로 받거나 함수를 반환하는 함수를 말합니다.
square(el) 자체는 함수가 아니라 함수 호출 표현식입니다.
하지만, map 메서드는 고차 함수로 볼 수 있습니다.
map 메서드는 배열을 변환하고, 변환에 사용되는 콜백 함수를 인자로 받기 때문입니다.
따라서, map 메서드가 square(el)을 호출하는 고차 함수로 볼 수 있습니다. */
// 21번
function getOnlyAllowedToDrink(arr) {
  // TODO: 여기에 코드를 작성합니다.
  const filteredList = arr.filter(function (person) { // filteredList 일급객체, filter는 고차함수
    return person.age >= 18;
  });

  return filteredList.map((el) => {
    return el.name;
  }
  )

	// AI가 써줌
	// 이렇게 한 번에 가능 (filteredList 만들 필요 x)
	// 필터링 후에 맵핑 / 순서주의 ! 
  
	// return arr
  //   .filter((person) => person.age >= 18)
  //   .map((person) => person.name);
  
}
// 31번
function studentReports(students) {
  // TODO: 여기에 코드를 작성합니다.
  const onlyFemales = students.filter(el => {
    if (el.gender === 'female') {
      return el;
    } else {
      return '';
    }
  })

  return onlyFemales.map((el) => {
    const sum = el.grades.reduce((acc, val) => {
      return acc + val;
    })
    const avg = sum / el.grades.length;
    el.grades = avg;
    return el;
  }
  )
}

/* 이 코드의 주요 캐치 포인트는 다음과 같습니다:

filter 메서드에서 조건을 만족하는 요소만 필터링하는데, return el;과 return '';을 혼용하고 있습니다.
filter 메서드는 조건을 만족하는 요소를 반환해야 하므로, return el;만 사용하여 조건을 만족하는 요소를 유지해야 합니다.

map 메서드에서 평균 점수를 계산하여 el.grades에 할당하고 있습니다.
하지만 원래 el.grades는 배열이었는데, 여기서 평균 점수로 덮어씌워져서 배열 대신 평균 값 하나만 남게 됩니다.
이 부분에서 원하는 동작이 맞는지 확인해야 합니다.
만약 el.grades를 배열로 유지하면서 평균 값을 추가하려면 다른 속성에 할당하거나, 객체를 별도로 생성하여 그 안에 평균 값을 추가해야 합니다.

또한, 코드 스타일과 가독성을 개선하기 위해 다음과 같은 변경 사항을 고려할 수 있습니다:

filter 메서드에서 조건문을 사용하지 않고 바로 el.gender === 'female'을 반환하여 간결하게 만들 수 있습니다.

reduce 메서드에서 총합을 계산할 때, 초기값을 지정하는 것이 좋습니다. 초기값을 0으로 지정하여 누락된 경우에도 정확한 결과를 얻을 수 있습니다.

map 메서드 내부에서 새로운 객체를 생성하여 반환하는 것이 좋습니다. 기존 객체를 수정하지 않고 새로운 객체를 생성하여 평균 값을 추가할 수 있습니다.

다음은 개선된 코드의 예시입니다:

function studentReports(students) {
  const onlyFemales = students.filter(el => el.gender === 'female');

  return onlyFemales.map(el => {
    const sum = el.grades.reduce((acc, val) => acc + val, 0);
    const avg = sum / el.grades.length;
    return {
      ...el,
      averageGrade: avg
    };
  });
} */
// 32번
function sumOfArraysInArray(arr) {
  // TODO: 여기에 코드를 작성합니다.
  
  const joinedArr = arr.reduce((acc, val) => {
      return acc.concat(val);
    }
  )

  const result = joinedArr.filter((el)=>{
    return typeof el === 'number';
  })

  return result.reduce(((acc, val) => {
      return acc + val;
    }),0)
}

/* 주어진 코드에서는 다음과 같은 캐치 포인트가 있습니다:

joinedArr 변수 초기화: reduce 함수를 사용하여 arr의 모든 배열을 하나의 배열로 연결합니다.
result 변수 초기화: joinedArr 배열에서 숫자 값만 필터링하여 새로운 배열을 생성합니다.
reduce 함수 중첩 사용: result 배열을 사용하여 모든 숫자 값을 더하는데, 초기값으로 0을 사용하여 reduce 함수를 중첩하여 합을 계산합니다.
이외에도 주의해야 할 부분은 각 함수의 콜백 함수 내부에서 적절한 값 반환과 조건 처리를 수행하는 것입니다.

function sumOfArraysInArray(arr) {
  const flattenedArr = arr.flat(); // 모든 배열을 하나로 평탄화합니다.
  
  const sum = flattenedArr.reduce((acc, val) => {
    if (typeof val === 'number') {
      return acc + val;
    }
    return acc;
  }, 0);

  return sum;
} */


🔥 프로토타입 Review

오늘 학습했던 내용은 Class와 상속이다.

위의 class structure에서 볼 수 있는 것처럼, 모든 Bee는 Grub을 기반으로 하여 각자의 특성에 맞는 일을 받게 된다.
공통된 특성을 기반으로 각자에게 맞는 특성을 부여받을 수 있는 것은 상속이 이루어졌음을 의미한다.

먼저 가장 기본이다.

Grub이라는 클래스를 정의하는 JavaScript 코드이며, Grub 클래스는 다음과 같은 특징을 가지고 있습니다.

  • 생성자(constructor): Grub 클래스의 생성자는 age, color, food라는 세 개의 매개변수를 받는다.
    하지만 실제로 생성자 내부에서는 이 매개변수들을 사용하지 않고, 고정된 값을 가진 인스턴스 변수를 초기화한다.
    이러한 초기화 값으로 age는 0, color는 "pink", food는 "jelly"로 설정된다.

  • eat 메서드: Grub 클래스에는 eat이라는 메서드가 있다.
    이 메서드는 "Mmmmmmmmm jelly"라는 문자열을 반환합니다.

  • 모듈 익스포트: module.exports를 사용하여 Grub 클래스를 외부로 공개한다.
    이렇게 함으로써 다른 파일에서 require를 사용하여 Grub 클래스를 가져와 사용할 수 있다.

따라서 이 특성에 따라서 Grub 클래스 안에 생성자(constructor) 함수를 사용하고, 매개변수와 메서드를 받아준다.


class Grub {
  constructor(age, color, food) {
    this.age = 0;
    this.color = "pink";
    this.food = "jelly";
  }

  eat() {
    return `Mmmmmmmmm jelly`;
  }
}

module.exports = Grub;


다음은 Grub를 상속하는 Bee 클래스이다.

Bee 클래스의 특징은 다음과 같다.

  • 상속: Bee 클래스는 Grub 클래스를 상속하고 있습니다. extends 키워드를 사용하여 상속 관계를 설정했다.
    이를 통해 Bee 클래스는 Grub 클래스의 속성과 메서드를 상속받을 수 있다.

  • 생성자(constructor): Bee 클래스의 생성자는 매개변수를 받지 않고,
    super()를 호출하여 부모 클래스의 생성자를 실행한다.

이를 통해 Grub 클래스에서 정의한 초기화 동작을 수행하고, Bee 클래스의 인스턴스 변수들을 추가적으로 초기화한다.
age는 5, color는 "yellow", food는 "jelly", job은 "Keep on growing"로 설정됩니다.

Bee 클래스는 Grub 클래스를 확장한 새로운 클래스로서, Grub 클래스의 기능을 상속받고 추가적인 속성과 동작을 정의할 수 있다.


const Grub = require("./1-Grub");

class Bee extends Grub {
  constructor() {
    super();
    this.age = 5;
    this.color = "yellow";
    this.job = "Keep on growing";
  }
}

module.exports = Bee;

이 문제는 Bee 클래스가 Grub 클래스의 특징을 이어받으며, 동시에 food 속성은 상속함으로 따로 재지정할 필요 없다.
또한, Bee 자체적으로 age, color, food, job과 같은 속성을 가지고 있다.

추가적으로 다음과 같은 새로운 키워드가 사용되었다.

상속관계를 알리는 extends,
부모 클래스의 생성자를 실행하는 super()



다음은 Bee를 상속하는 ForagerBee 클래스이다.

이제부터는 사실 위의 방법들과 같다.

사용할 수 있는 퍼포먼스는 모두 사용되었고, 추가적인 함수 구현이 들어간다 정도이다.


const Bee = require("./2-Bee");

class ForagerBee extends Bee {
  constructor() {
    super();
    this.age = 10;
    this.job = "find pollen";
    this.canFly = true;
    this.treasureChest = [];
  }
  forage(el) {
    this.treasureChest.push(el);
  }
}

module.exports = ForagerBee;

다음과 같이 구현했다.

특이사항은 빈 배열을 할당한 treasureChest에 전달인자를 push하는 메서드(forage)를 정의했다.


마지막으로 Bee를 상속하는 HoneyMakerBee 클래스이다.

ForagerBee 클래스를 상속하는 것이 아닌, ForagerBee와 똑같이 Bee를 상속하는 클래스다.

const Bee = require("./2-Bee");

class HoneyMakerBee extends Bee {
  constructor() {
    super();
    this.age = 10;
    this.job = "make honey";
    this.honeyPot = 0;
  }
  makeHoney() {
    this.honeyPot += 1;
  }
  giveHoney() {
    this.honeyPot -= 1;
  }
}

module.exports = HoneyMakerBee;
profile
아이디어가 넘치는 프론트엔드를 꿈꿉니다 🔥

0개의 댓글