지영이의 개발 블로그
러닝 리액트 2판 정리 (3장) 본문
3.1 함수형이란 무엇인가?
자바스크립트에서는 함수가 애플리케이션의 데이터를 표현할 수 있다. 문자열이나 수 ,또는 다른 모든값과 마찬가지로 var 키워드를 사용해서 함수를 정의할 수 있다.
var log = function(message) {
console.log(message)
}
log("자바스크립트에서는 함수를 변수에 넣을 수 있습니다.")
화살표 함수를 사용해 같은 함수를 정의할 수 있다.
const log = message => console.log(message)
log("ES6 화살표 함수")
함수를 변수에 넣을 수 있는 것과 마찬가지로 , 함수를 객체에 넣을 수도 있다.
const obj = {
message: "함수를 다른 값과 마찬가지로 객체에 추가할 수도 있습니다.",
log(message) {
console.log(message)
}
}
obj.log(obj.message)
심지어 함수를 배열에 넣을 수도 있다.
const messages = [
"함수를 배열에 넣을 수도 있습니다.",
message => console.log(message),
"일반적인 값과 마찬가지입니다.",
message => console.log(message)
]
messages[1](messages[0])
messages[3](messages[2])
<고차함수>
다른 값들과 마찬가지로 함수를 다른 함수에 인자로 넘길 수도 있다.
const insideFn = logger =>
logger("함수를 다른 함수에 인자로 넘길 수도 있습니다.")
insideFn(message => console.log(message))
함수가 함수를 반환할 수도 있다,
var createScream = function(logger) {
return function(message) {
logger(message.toUpperCase() + "!!!")
}
}
const scream = createScream(message => console.log(message))
scream('함수가 함수를 반환할 수도 있습니다')
scream('createScream은 함수를 반환합니다')
scream('scream은 createScream이 반환한 함수를 가리킵니다')
함수가 함수를 인자로 받는 경우와 함수가 함수를 반환하는 경우를 고차함수라고 부른다 . crateScream 고차 함수를 화사료 함수로 표현할 수 있다.
const createScream = logger => message =>
logger(message.toUpperCase() + "!!!")
const scream = createScream(message => console.log(message))
scream('ES6에서는 더 간편하게 createScream을 만들 수 있습니다.')
3.2 명령형 프로그래밍과 선언적 프로그래밍 비교
함수형 프로그래밍은 선언적 프로그래밍 이라는 더 넓은 프로그래밍 패러다임의 한가지 이다 . 선언적 프로그래밍은 필요한 것을 달성하는 과정을 하나하나 기술하는 것보다 필요한것이 어떤것인지를 기술하는 것에 더 방점을 두고 애플리캐이션의 구조를 세워나가는 프로그래밍 스타일이다 .
명령형 프로그래밍은 코드로 원하는 결과를 달성해 나가는 과정에만 관심을 두는 프로그래밍 스타일이다.
(명령형 프로그래밍)
var string = "This is the mid day show with Cheryl Waters"
var urlFriendly = ""
for (var i=0; i<string.length; i++) {
if (string[i] === " ") {
urlFriendly += "-"
} else {
urlFriendly += string[i]
}
}
urlFriendly = urlFriendly.toLowerCase()
console.log(urlFriendly)
(선언적 프로그래밍)
const string = "This is the mid day show with Cheryl Waters"
const urlFriendly = string.replace(/ /g, "-").toLowerCase()
console.log(urlFriendly)
선언적 프로그램은 코드 자체가 어떤 일이 벌어질지에 대해 설명하기 때문에 좀 더 추론 하기가 쉽다.
3.3 함수형 프로그래밍의 개념
함수형 프로그래밍의 핵심개념인 불변성 , 순수성 , 데이터 변환 ,고차함수, 재귀에대해 알아보자
3.3.1 불변성
함수형 프로그래밍에서는 데이터가 변할 수 없다. 불변성 데이터는 결코 바뀌지 않는다.
불변성 데이터가 작동하는 방식은 원본데이터 구조를 변경하는 대신 그 데이터 구조의 복사본을 만들되 그중 일부를 변경한ㄷ다 . 그리고 원본 대신 변경한 복사본을 사용해 필요한 작업을 진행한다.
let color_lawn = {
title: "잔디",
color: "#00FF00",
rating: 0
}
function rateColor(color, rating) {
color.rating = rating
return color
}
//
// rateColor는 원래의 색을 변경한다.
//
console.log(rateColor(color_lawn, 5).rating)
console.log(color_lawn.rating)
let color_lawn = {
title: "잔디",
color: "#00FF00",
rating: 0
}
var rateColor = function(color, rating) {
return Object.assign({}, color, {rating:rating})
}
//
// Object.assign으로 복사본을 만들어서 평점을 부여한다
//
console.log(rateColor(color_lawn, 5).rating)
console.log(color_lawn.rating)
여기서는 Object.assign을 사용해 색을 평점을 바꿨다. 복사본과 같다 . Object.assign은 빈 객체를 받고, color 객체를 그 빈 객체에 복사한 다음에 , 복사본에 있는 rating 프로퍼티의 값을 rating 파라미터의 값으로 변경한다 . 이제 원본은 그대로 남겨둔채 rating 만 변경된 복사본이 생긴다.
화살표 함수와 객체 스프레드 연산자를 활용해 같은 함수를 작성할 수도 있다 .
title: "잔디",
color: "#00FF00",
rating: 0
}
const rateColor = (color, rating) =>
({
...color,
rating
})
//
// 더 나은 방법으로 객체 스프레드 ...를 사용한다
//
console.log(rateColor(color_lawn, 5).rating)
console.log(color_lawn.rating)
Array.push는 불변성 함수가 아니다.
let list = [
{ title: "과격한 빨강"},
{ title: "잔디"},
{ title: "파티 핑크"}
]
var addColor = function(title, colors) {
colors.push({ title: title })
return colors
}
//
// 색을 추가하면 원래의 배열이 바뀐다
//
console.log(addColor("매력적인 초록", list).length)
console.log(list.length)
원래의 List 배열을 변화시키지 않고 유지하기 위해서는 Array.concat을 사용해야 한다.
let list = [
{ title: "과격한 빨강"},
{ title: "잔디"},
{ title: "파티 핑크"}
]
const addColor = (title, array) => array.concat({title})
//
// array.concat을 사용하면 원래의 배열이 변경되지 않는다
//
console.log(addColor("매력적인 초록", list).length)
console.log(list.length)
앞에서 객체를 복사할 때 사용했던 방법과 똑같은 방법으로 배열 스프레드 연산자를 사용해 배열을 복사할 수 있다 .
let list = [
{ title: "과격한 빨강"},
{ title: "잔디"},
{ title: "파티 핑크"}
]
const addColor = (title, list) => [...list, {title}]
//
// ... 스프레드 연산자로 배열을 복사하면 더 편리하다
//
console.log(addColor("매력적인 초록", list).length)
console.log(list.length)
3.3.2 순수함수
순수함수는 파라미터에 의해서만 값이 결정되는 함수를 뜻한다 . 순수함수는 최소한 하나 이상의 인수를 받고 , 인자가 같으면 항상 같은 값이나 함수를 반환한다.순수함수에는 부수효과가 없다. 부수효과란 전역변수를 설정하거나 함수 내부나 애플리케이션에 있는 다른 상태를 변경하는 것을 말한다더, 순수함수는 인수를 변경 불가능한 데이터로 취급한다.
순수함수를 이해하기 위해 순수하지 않은 함수를 먼저 하나 살펴보자.
var frederick = {
name: "Frederick Douglass",
canRead: false,
canWrite: false
}
function selfEducate() {
frederick.canRead = true
frederick.canWrite = true
}
//
// 순수하지 않은 함수이다. 인자도 없고, return 문도 없으며,
// 원본 객체를 변화시킨다.
//
selfEducate()
console.log( frederick )
순수함수로 바꿔보자 이 함수는 전달받은 인자 person 으로부터 새로운 값을 계산한다. 새값을 계산할 때 전달받은 인자를 변경하지 않고 새로 만든 객체를 반환한다. 따라서 이함수에는 부수효과가 없다 .
var frederick = {
name: "Frederick Douglass",
canRead: false,
canWrite: false
}
const selfEducate = person =>
({
...person,
canRead: true,
canWrite: true
})
//
// 순수함수로 객체를 인자로 받아서 새 객체를 반환한다.
// 아무 부수 효과도 발생시키지 않는다
//
console.log( selfEducate(frederick) )
console.log( frederick )
- 순수함수는 파라미터를 최소 하나이상 받아야한다
- 순수함수는 값이나 다른함수를 반환해야 한다.
- 순수함수는 인자나 함수 밖에 있는 다른 변수를 변경하거나,입출력을 수행해서는 안된다
3.3.3 데이터 변환
<Array.join 메서드>
const schools = [
"Yorktown",
"Washington & Lee",
"Wakefield"
]
console.log( schools.join(", ") )
<Array.filter 메서드>
const schools = [
"Yorktown",
"Washington & Lee",
"Wakefield"
]
const wSchools = schools.filter(school => school[0] === "W")
console.log( wSchools )
배열에서 원소를 제거해야 할 필요가 있다면 Array.pop 이나 Array.splice 보다는 Array.filter를 이용하자 .
const schools = [
"Yorktown",
"Washington & Lee",
"Wakefield"
]
const cutSchool = (cut, list) =>
list.filter(school => school !== cut)
console.log(cutSchool("Washington & Lee", schools).join(" * "))
console.log(schools.join("\n"))
<Array.map 메서드>
const schools = [
"Yorktown",
"Washington & Lee",
"Wakefield"
]
const highSchools = schools.map(school => `${school} High School`)
console.log(highSchools.join("\n"))
console.log(schools.join("\n"))
다음 예제는 학교가 담겨있느 객체의 배열을 반환하는 map함수이다.
const schools = [
"Yorktown",
"Washington & Lee",
"Wakefield"
]
const highSchools = schools.map(school => ({ name: school }))
console.log(highSchools)
//(3) [{…}, {…}, {…}]
[{name: 'Yorktown'},
{name: 'Washington & Lee'},
{name: 'Wakefield'}]
editName 은 map 함수를 사용해 원본 배열로부터 새로운 객체로 이루어진 배열을 만든다
let schools = [
{ name: "Yorktown"},
{ name: "Stratford" },
{ name: "Washington & Lee"},
{ name: "Wakefield"}
]
const editName = (oldName, name, arr) =>
arr.map(item => {
if (item.name === oldName) {
return {
...item,
name
}
} else {
return item
}
})
let updatedSchools = editName("Stratford", "HB Woodlawn", schools)
console.log( updatedSchools[1] )
console.log( schools[1] )
if/else문 대신 3항 연산자를 사용하여 editName 함수를 만들 수 있다.
let schools = [
{ name: "Yorktown"},
{ name: "Stratford" },
{ name: "Washington & Lee"},
{ name: "Wakefield"}
]
// Edit Name with less syntax
const editName = (oldName, name, arr) =>
arr.map(item => (item.name === oldName) ?
({...item,name}) :
item
)
let updatedSchools = editName("Stratford", "HB Woodlawn", schools)
console.log( updatedSchools[1] )
console.log( schools[1] )
객체를 배열로 반환하고 싶을때는 Array.map 과 Object.keys를 함께 사용하면 된다 .
Object.keys는 어떤 객체의 키로 이루어진 배열을 반환하는 메서드이다 .
<Object.keys>
const schools = {
"Yorktown": 10,
"Washington & Lee": 2,
"Wakefield": 5
}
const schoolArray = Object.keys(schools).map(key =>
({
name: key,
wins: schools[key]
})
)
console.log(schoolArray)
<reduce>
수로 이뤄진 배열에서 쵀댓값을 찾을 필요가 있다고 하자. 배열을 하나의 수로 변환해야 하므로 reduce를 사용할 수 있다.
const ages = [21,18,42,40,64,63,34]
const maxAge = ages.reduce((max, age) => {
console.log(`${age} > ${max} = ${age > max}`)
if (age > max) {
return age
} else {
return max
}
}, 0)
console.log('maxAge', maxAge)
const ages = [21,18,42,40,64,63,34];
// less syntax
const max = ages.reduce(
(max, value) => (value > max) ? value : max,
0
)
console.log('max', max)
때로 배열을 객체로 반환해야 할 때가 있다. 다음 예제는 reduce를 사용해 값이 들어있는 배열을 해시로 변환한다.
const colors = [
{
id: '-xekare',
title: "과격한 빨강",
rating: 3
},
{
id: '-jbwsof',
title: "큰 파랑",
rating: 2
},
{
id: '-prigbj',
title: "큰곰 회색",
rating: 5
},
{
id: '-ryhbhsl',
title: "바나나",
rating: 1
}
]
const hashColors = colors.reduce(
(hash, {id, title, rating}) => {
hash[id] = {title, rating}
return hash
},
{}
)
console.log(hashColors)
위 예제는 unique 배열에 어떤 색이 이미 들어있지 않다면 그 색을 추가한다 . 이미 unique에 그색이 들어있다면 아무 일도 하지않고 다음으로 넘어가기 위해 현재의 unique를 그대로 반환한다
3.3.4 고차함수
고차함수의 첫번째 분류는 다른 함수를 인자로 받는 함수이다. map ,filter, reduce는 모두 다른 함수를 인자로받는다 .이들은 모두 고차함수다 .
const invokeIf = (condition, fnTrue, fnFalse) =>
(condition) ? fnTrue() : fnFalse()
const showWelcome = () =>
console.log("Welcome!!!")
const showUnauthorized = () =>
console.log("Unauthorized!!!")
invokeIf(true, showWelcome, showUnauthorized)
invokeIf(false, showWelcome, showUnauthorized)
다른 함수를 반환하는 고차함수는 자바스크립트에서 비동기적인 실행맥락을 처리할 때 유용하다 .
함수를 반환하는 고차함수를 쓰면 필요할 때 재사용 할 수 있는 함수를 만들수있다.
const getFakeMembers = count => new Promise((resolves, rejects) => {
const api = `https://api.randomuser.me/?nat=US&results=${count}`
const request = new XMLHttpRequest()
request.open('GET', api)
request.onload = () =>
(request.status === 200) ?
resolves(JSON.parse(request.response).results) :
reject(Error(request.statusText))
request.onerror = (err) => rejects(err)
request.send()
})
const userLogs = userName => message =>
console.log(`${userName} -> ${message}`)
const log = userLogs("grandpa23")
log("attempted to load 20 fake members")
getFakeMembers(20).then(
members => log(`successfully loaded ${members.length} members`),
error => log("encountered an error loading members")
)
위예제 커링은 고차함수 사용법과 관련한 함수형 프로그래밍 기법이다 . 커링은 어떤 연산을 수행할 때 필요한 값 중 일부를 저장하고 나중에 나머지 값을 전달받는 기법이다. 이를 위해 다른 함수를 반환하는 함수를 사용하며 이를 커링된 함수라고 부른다 .
3.3.5 재귀
재귀는 자신을 호출하는 함수를 만드는 기법이다 .
const countdown = (value, fn) => {
fn(value)
return (value > 0) ? countdown(value-1, fn) : value
}
countdown(10, value => console.log(value))
const countdown = (value, fn, delay=1000) => {
fn(value)
return (value > 0) ?
setTimeout(() => countdown(value-1, fn), delay) :
value
}
const log = value => console.log(value)
countdown(10, log)
'독서 > 모던자바스크립트&러닝리액트' 카테고리의 다른 글
러닝 리액트 2판 정리 (1~2장) (2) | 2022.09.15 |
---|---|
모던자바스크립트 deepdive 정리 (43장) -수정필요 (0) | 2022.06.23 |
모던자바스크립트 deepdive 정리 (9~11장) (0) | 2022.05.22 |
모던자바스크립트 deep dive 정리(1~8) (0) | 2022.05.21 |