배열고차함수 & 화살표함수와 this

Jake·2022년 7월 27일
0

Javascript

목록 보기
1/1
post-thumbnail

[화살표함수와 this] 바인딩 문제 해결하기;

콜백 함수 내부의 this가 메소드를 호출한 객체를 가리키게 하려면 아래의 3가지 방법이 있다.

  • 콜백함수의 this는 기본적으로 window(전역객체) binding이다.

Solution 1: that = this

function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  var that = this;  // ㅁthis: Prefixer 생성자 함수의 인스턴스
  return arr.map(function (x) {
    return that.prefix + ' ' + x;
  });
};

var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));

//생성자함수속this는 인스턴스에바인딩, 이것을 that할당으로 회피
//that은 이제 콜백함수 내부에서도 인스턴스이다.
(콜백함수내부 this는 window에 바인딩됨)


Solution 2: map(callback, this) :map의 2nd파라미터로 this를넘겨준다.

function Prefixer(prefix) {
  this.prefix = prefix;  
} //생성자함수 this는 생성자함수가 생성한 '인스턴스' (여기서는 pre객체)
          
Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(function (x) {
    return this.prefix + ' ' + x;    //콜백의 this는 원래 window지만 map 2nd param으로 따로 지정해줌.
  }, this); // this: Prefixer 생성자 함수의 인스턴스           //map  scope속 this가 아니라 prefixArray scope속 this = 메서드호출객체 
  
};  
var pre = new Prefixer('Hi');       / pre.prefix === Hi
console.log(pre.prefixArray(['Lee', 'Kim']));

//1.map의 2nd파라미터 this는 '메서드속 this는 메서드호출객체에 바인딩된다.'의 속성을 따라
//this === arr
//2.console.log(pre.prefixArray(['Lee', 'Kim'])); //


Solution 3: bind(this) 바인드메서드 이용하기 ( this의 렉시컬환경에따라 강제 바인딩)

function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  return arr.map(function (x) {
    return this.prefix + ' ' + x;
  }.bind(this)); // this: Prefixer 생성자 함수의 인스턴스 
};

/*bind메서드의 인자로 들어가는 this는 prefixArray메서드의 스코프속에 있으므로 
이 this는 메서드호출객체인 Prefixer에 바인딩,  map의 콜백의 
this는 Prefixer(prefixArray메서드호출객체)가 된다.*/

var pre = new Prefixer('Hi');
console.log(pre.prefixArray(['Lee', 'Kim']));


[배열고차함수]

사용목적

고차 함수는 외부 상태 변경이나 가변(mutable) 데이터를 피하고 불변성(Immutability)을 지향하는 함수형 프로그래밍
기반을 두고 있다. 함수형 프로그래밍은 순수 함수(Pure function)와 보조 함수의 조합을 통해 로직 내에 존재하는
조건문과 반복문을 제거하여 복잡성을 해결하고 & 변수의 사용을 억제하여
상태 변경을 피하려는 프로그래밍 패러다임이다.
조건문이나 반복문은 로직의 흐름을 이해하기 어렵게 하여 가독성을 해치고,
변수의 값은 누군가에 의해 언제든지 변경될 수 있어 오류 발생의 근본적 원인이 될 수 있기 때문이다.
(*고차함수: 값으로 취급하여 파라미터나,반환값으로 이용가능한 함수)

기능요약

  • 1.forEach:요소순회 -> 콜백실행
  • 2.map: 요소순회 -> 콜백리턴값으로 새로운배열 리턴
  • 3.filter: 요소순회 -> 콜백리턴이 true인 요소만 모아 새로운 배열로 리턴
  • 4.reduce: 요소순회 -> '이전의' 콜백실행 리턴값을 다시 콜백에 전달(하여 그결과를 다시반환.
    // reduce는 acc(누산기),cur(현재값), idx(현재인덱스), src(원본배열:self)를 인자로 받는다.
    // acc에 initialValue제공시 acc===초기값, cur===arr[0]
    // acc에 initialValue 미제공시 acc===arr[0], cur===arr[1]

공통점:
1. 메서드를 호출한 배열의 요소가 콜백을 순회,
2. (item,index) 다음으로 콜백에 전달하는 (this)는 원본 배열을 수정할 수 있다.

[추후 추가예정]

  • sort(오름차순정렬): 유니코드포인트에 순서로 정렬, sort에 넣어주는 비교함수(콜백)으로 정렬순서를 명확히한다
    숫자비교callback: [(a,b) => a-b : a반환(a<b이면)]
    문자열비교callback: [(a,b) => a[key] > b[key] ? 1 : (a[key] < b[key] ? -1 : 0) ; ]
    cf)arr.reverse() 내림차순 정렬(유니코드포인트 기준)
  • some
  • every
  • find
  • findIndex

1. forEach메서드

: 배열을 순회, 각 요소에 대해 인자로 주어진 콜백을 실행, 반환값은
'undefined'
=>단순히 요소가 순회하며 콜백을 실행함에 의의가 있음.

// forEach 메소드는 원본 배열(this)을 변경하지 않는다.
하지만 콜백 함수는 원본 배열(this)을 변경할 수는 있다.
// 원본 배열을 직접 변경하려면 콜백 함수의 3번째 인자(this)를 사용한다.


2.map메서드

: 배열요소가 콜백에전달되어, 콜백의 리턴값으로 새로운배열을 만듦.

function Prefixer(prefix) {
  this.prefix = prefix;
}

Prefixer.prototype.prefixArray = function (arr) {
  // 콜백함수의 인자로 배열 요소의 값, 요소 인덱스, map 메소드를 호출한 배열, 즉 this를 전달할 수 있다.
  return arr.map(function (x) {
    // x는 배열 요소의 값이다.
    return this.prefix + x; // 2번째 인자 this를 전달하지 않으면 this === window
  }, this);
};

const pre = new Prefixer('-webkit-');
const preArr = pre.prefixArray(['linear-gradient', 'border-radius']);
console.log(preArr);
// [ '-webkit-linear-gradient', '-webkit-border-radius' ]

[해설]
1. Prefixer생성자함수로 pre라는 인스턴스 생성
2. pre.prefixArray( ['linear-gradient','border-radius']);
인스턴스는 프로토타입메서드 호출가능,
3.map메서드 속 콜백의 this는 메서드호출객체(인스턴스=pre)를 가리킨다.
따라서 map은 호출배열의 아이템을 콜백으로 넘겨 콜백반환값으로 새로운 배열을 만드므로,
console.log(preArr); // [ '-webkit-linear-gradient', '-webkit-border-radius' ]이다.
4. map메서드의 2번째 인자로 this를 넘기면 this에는 메서드호출객체가 할당된다. (this ===pre)
//this를넘겨주지 않으면 this는 전역객체window에 자동 바인딩되므로 주의해야한다.
//꼭 this를 2nd parameter로 넘겨준다.


3. filter(item,idx,self)메서드

(요소순회 -> 콜백 return true인 아이템만 추출한 새로운 배열을 반환한다.)
//콜백에는 기본적으로 item,index,self 전달이 가능함.
// filter메소드는 if문을 대체가능하다.

const result = [1, 2, 3, 4, 5].filter(function (item, index, self) {
  console.log(`[${index}] = ${item}`);
  return item % 2; // 홀수만을 필터링한다 (1은 true로 평가된다)
});

console.log(result); // [ 1, 3, 5 ]

4. reduce(acc,cur,idx,self)

// acc에 initialValue(2nd param)있으면,
acc===initV cur===arr[0](첫 번째요소)
// acc에 initialValue없으면,
acc===arr[0], cur===arr[1](두 번째요소)

previousValue: 이전 콜백의 반환값
currentValue : 배열 요소의 값
currentIndex : 인덱스
array : 메소드를 호출한 배열, 즉 this

const arr = [1, 2, 3, 4, 5];
// 합산
const sum = arr.reduce(function (previousValue, currentValue, currentIndex, self) {
  console.log(previousValue + '+' + currentValue + '=' + (previousValue + currentValue));
  return previousValue + currentValue; // 결과는 다음 콜백의 첫번째 인자로 전달된다
});

console.log(sum); // 15: 1~5까지의 합
/*
1: 1+2=3
2: 3+3=6
3: 6+4=10
4: 10+5=15
15
*/

// 최대값 취득
const max = arr.reduce(function (pre, cur) {
  return pre > cur ? pre : cur;    (pre>cur 이면 pre 리턴, pre<cur이면  cur리턴 // max값 리턴)
});

console.log(max); // 5: 최대값
const sum = [1, 2, 3, 4, 5].reduce(function (pre, cur) {
  return pre + cur;
}, 5);
// pre.1st = 5 , cur.1st =1  -> pre.2nd =5+1=6  cur.2nd = 2 -> pre.3rd=6+2=8    cur.3rd=3 ....

console.log(sum); // 20
// 5 + 1 => 6 + 2 => 8 + 3 => 11 + 4 => 15 + 5

객체 배열에서의 값 합산

객체로 이루어진 배열에 들어 있는 값을 합산하기 위해서는 반드시
초기값을 주어 각 항목(item)이 여러분의 함수를 거치도록 해야 합니다.
(item이 객체인 배열은 반드시 초깃값 할당 필요)

const products = [
  { id: 1, price: 100 },
  { id: 2, price: 200 },
  { id: 3, price: 300 }
];

// 프로퍼티 값을 합산
const priceSum = products.reduce(function (pre, cur) {
  console.log(pre.price, cur.price);
  // 숫자값이 두번째 콜백 함수 호출의 인수로 전달된다. 이때 pre.price는 undefined이다.
  return pre.price + cur.price;
});

console.log(priceSum); // NaN

예외상황)

  • 1.배열의 요소가 (위치와 관계없이) 하나 뿐이면서 initialValue를 제공되지 않은 경우
    : 해당 item그대로 반환 [{x:2}].reduce(callback) // {x:2}
  • 2.initialValue는 주어졌으나 배열이 빈 경우엔 그 initialValue 값을 callback 호출 없이 반환합니다.
  • 3.reduce로 빈 배열을 호출하면 에러발생 Syntax error

// 초기값을 전달하면 에러를 회피할 수 있다.(reduce호출시에는 언제나 초기값을 전달하는 것이 보다 안전하다)

const sum = [].reduce(function (pre, cur) {
  console.log(pre, cur);
  return pre + cur;
}, 0);

console.log(sum); // 0

Reference

profile
young하고 MZ해요

0개의 댓글