1-5. 함수형 자바스크립트의 값 - 기본객체, 컬렉션, 불변성/영속성
— Functional programming — 3 min read
프로그래머스에서 진행한 유인동님의 ES6로 알아보는 동시성 & 함수형 프로그래밍 강의를 들으며 정리한 내용입니다.
# 유사배열
자바스크립트 세계엔 유사배열이 많다.
대표적인 유사배열은 jQuery객체, arguments, NodeList등이 있다
1. 유사배열의 문제점
- 유사배열은 배열이 아니기 때문에, array의 메서드를 쓸 수 없다
- 유사배열은 iterable이 아니기 때문에, 구형 브라우저에서
for...of
나 전개연산자와 사용할 수 없다
2. 해결 방법
Underscore.js
유사배열을 지원하기 때문에 jQuery, arguments같은 유사배열 객체를 받아 배열처럼 사용할 수 있다Array-like용 generator함수
자바스크립트 3rd party library의 값들 중에는 아직 iterable/iterator 프로토콜을 따르지 않는 유사배열이 있다면, 제너레이터 함수에 한 번 통과시켜 iterable로 만든 후 사용하도록 하면 된다
하지만 ES6부터는 iterable이 아닌 유사 배열을 사용하지 않는 방향으로 가야함 !
1// 1. 유사배열의 문제점2const arrayLike = { 0: 1, 1: 2, length: 2 }3for(const v of arrayLike) log(v)4// TypeError: arrayLike is not iterable5
6// 2-1. Underscore.js7_.map(arrayLike, a => a + 10) // [11, 12]8
9// 2-2. Array-like용 generator함수10function *iterArrayLike(obj) {11 for(const k in obj) {12 if(k !== 'length') yield obj[k]13 }14}15
16for(const v of iterArrayLike(arrayLike)) log(v) // [1, 2]
3. 앞으로의 방향은 어떨까 ?
대표적인 유사배열 arguments, NodeList 모두 ES6가 동작하는 최신브라우저 에서는[Symbol.iterator]()
를 가지고 있기 때문에 for...of
에서 잘 동작한다
1// 1. arguments2!function() {3 for(const v of arguments) log(v)4} (1, 2, 3) // 1 2 35
6// 2. NodeList7for(const v of document.querySelectorAll('*')) log(v) 8// <html> <script> <head> ...
여기서 파악해볼 수 있는 추세는...
- ES6부터는 arrayLike객체도
[Symbol.iterator]()
를 가지고 있다 - Web API 역시도 ES6의 프로토콜에 맞춰 동향을 따르고 있다
- 최신 브라우저에서는 열거 가능한(enumerable) 속성이 있는 값들은 Iterable/Iterator 프로토콜을 잘 따르고 있음
- 하지만, arguments와 nodeList는 well-formed 이터러블은 아니다 !
Iterable이 아닌 유사 배열을 사용하지 않는 방향으로 가야한다 !
# 객체 더 알아보기
# 객체 자신의 속성 (Own Property), 부수속성(Property Attribute)
Object.prototype.hasOwnProperty(prop)
객체 자신이 어떤 속성을 가지고 있는지를 확인Object.getOwnPropertyDescriptor(obj, prop)
객체가 가지고 있는 속성의 동작방식 ( =속성의 부수속성. property attribute )- value: 속성에 어떤 값이 저장되어 있는지
- writable: 변경할 수 있는 속성인지
- enumerable: 열거 가능한 속성인지
- configurable: 부수속성을 변경하거나 속성을 삭제할 수 있는지
1const obj = { prop: 1 }2
3Object.getOwnPropertyDescriptor(obj, 'prop')4// {value: 1, writable: true, enumerable: true, configurable: true}5Object.getOwnPropertyDescriptor(Math, 'PI') 6// {value: 3.14159, writable: false, enumerable: false, configurable: false}7
8obj.prop = 2 // 동작 -> writable: true9delete obj.prop // 동작 -> configurable: true10
11Math.PI = 10 // 동작안함 -> writable: false12delete Math.PI // 동작안함 -> configurable: false
# 열거 가능한 속성 (enumnerable)
Object.keys
- 객체 자신의 속성 중 열거 가능한(enumerable) 속성의 이름을 배열로 반환Object.values
- 객체 자신의 속성 중 열거 가능한(enumerable) 속성의 값을 배열로 반환Object.entries
- 객체 자신의 속성 중 열거 가능한(enumerable) 속성의 이름과 값을 배열로 반환
최신 브라우저에서는 열거 가능한(enumerable) 속성이 있는 값들은 Iterable/Iterator 프로토콜을 잘 따르고 있기 때문에 for...of
로 순회 가능하다
1const nodeList = document.querySelectorAll('*')2
3Object.getOwnPropertyDescriptor(nodeList, 0)4// {value: html, writable: false, enumerable: true, configurable: true}
# 함수형 자바스크립트의 값
- 결제시스템에서 중요한 것은 클라이언트 값을 믿어서는 안된다는 것 !
(클라이언트에서 얼마든지 값을 바꿔서 날릴 수 있다. 5만원짜리를 0원에 결제해주면 안됨) - 결론적을 서버에도 똑같은 DB구조가 있어 클라이언트 요청과 대조하여 다르면 reject를 시켜야 한다
JSON을 중심으로 프로그래밍을 할 경우 적은 종류의 데이터 구조와 추상 전략을 취할 수 있습니다. 이는 조합성을 높이고, 함수의 재사용성을 높일 수 있게 합니다. 만일 백엔드 프로그래밍을 NodeJS로 하고 있다면, 프론트엔드와 백엔드에서 공유할 코드도 얻을 수 있습니다.
# undefined
- undefined는 자바스크립트 런타임에서 생성된 값이다
- 정의되지 않은 변수 호출시
undefined
- 리턴 값이 없는 함수 호출시
undefined
- 객체에 정의되지 않은 속성 호출시
undefined
- 정의되지 않은 변수 호출시
- DB에 저장할 수 없고, JSON으로 전달할 수도 없다
- 서버에서 날라온 값에 undefined가 있을리가 없다
undefined
를 하나의 구분자로 사용가능. 값으로 사용안할 것이기 때문에- '아무것도 찾지 못했거나, 아무일도 하지 못했을 때'를 구분하기 위한 값
# 열거가능한 값, 컬렉션
함수형 프로그래밍에선 컬렉션을 많이 다룬다. 컬렉션으로 볼 수 있는 값들은 아래와 같다
- JSON 데이터 타입 내의 object
- Array, Map, Set ...
- 그 외
[Symbol.iterator]()
가 구현된 모든 iterable과 iterator - Generator를 실행한 결과 값
# 컬렉션 순회는 for...of
로
for, while, i++, j++, length
등을 이용한 루프는 명령적이고 복잡하다- 스코프와 실행 컨텍스트 실수
- 비동기상황에서의 코드의 파편화가 일어날 수 있다
ES6
for...of
문은 Iterable/Iterator 프로토콜을 따르기 때문에- 다형성이 높고
- 비동기 상황의 재귀 로직을 파편화 없이 구현하기 좋다
ex.go()
함수 작성시. 이터러블은 순회가 진행된 지점을 기억하기 때문에, 재귀시 다시 그 지점에서 for루프를 돌 수 있었다
따라서 자바스크립트에서 컬렉션을 순회할 때에는
for...of
를 쓰도록
# 영속성과 불변성
새로운 객체 생성시, 원본의 값을 참조하면서 변화가 필 요한 곳만 바꿔가는 것
- 영속성: 원본의 structure은 그대로 유지
"data structure that always preserves the previous version of itself when it is modified" - 불변성: 값 변경이 필요한 곳만 새로운 structure 생성. 원본을 변경하지 않음
"Do not update the structure in-place, but instead always yield a new updated structure"
1const member = [2 { id: 1, name: 'ria' },3 { id: 2, name: 'bong' },4 { id: 3, name: 'andy' }5]6
7let partyMember = member.filter(a => a.id > 1)8// [{ id: 2, name: 'bong' }, { id: 3, name: 'andy' }]9
10partyMember = partyMember.map(a => (a.id == 2) ? {...a, name: 'new bong'} : a)11// [{ id: 2, name: 'new bong' }, { id: 3, name: 'andy' }]
partyMember
생성- filter로 member의 값 자체를 복사한게 아니라
- 원본의 값을 참조한 것이다
[ member[1], member[2] ]
partyMember
id2의 이름을 변경한다- 원본값을 직접 변경하지 않고
- 변형된 새로운 값으로 만든다
※ 값이 전달/할당될 때
원시 값의 경우는 복사를, 객체의 경우는 레퍼런스 사본을 생성한다는 규칙을 잘 이해하고,
함수와 연산자 등을 잘 조합하여 불변성을 지켜가야한다
이런식으로 프로그래밍을 해나가면,
객체의 불변성을 위해Object.freeze()
를 사용하는건 뻘짓이었다는걸 느낄 수 있음
# 다형성 을 정복해나가기 위해서...
값을 아래와 같이 보는 것 이 중요하다
- 객체인 것, 아닌 것
- Promise인 것, 아닌 것
[Symbol.iterator]()
가 있는 것, 없는 것
# Promise
- 병렬적으로 코딩하기 위해 가장 중요한 것이 프로미스이다
- Promise 활용을 통해 다양한 함수의 인자와 결과 값으로 전달하면서 높은 수준의 동시성 프로그래밍을 구현할 수 있다
# 컬렉션 중심 프로그래밍
최근 자바스크립트로 처리해야하는 event와 data의 스케일이 커졌다. Remote data를 비동기처리하기 위한 여러 Functional 패러다임들이 있다.
1. 컬렉션 중심 프로그래밍
"내가 다루고자 하는 모든 로직을, 코드라는 값으로 다루겠다"
- 모든 일련의 행동들을 컬렉션(열거가능한 값)으로 보겠다
- 인자와 리턴 값으로 소통
2. Reactive 프로그래밍 (RxJs)
"Observable이라는 객체를 만들어 이벤트를 전파시켜 반응적으로 일을 처리하는 방식"
- 모든 일들을 iterator처럼 보겠다는 방식
- 좀 더 객체지향적인 면이 있음