Skip to content
Underbleu
GithubLinkedin

1-5. 함수형 자바스크립트의 값 - 기본객체, 컬렉션, 불변성/영속성

Functional programming3 min read

프로그래머스에서 진행한 유인동님의 ES6로 알아보는 동시성 & 함수형 프로그래밍 강의를 들으며 정리한 내용입니다.

# 유사배열

자바스크립트 세계엔 유사배열이 많다.
대표적인 유사배열은 jQuery객체, arguments, NodeList등이 있다

1. 유사배열의 문제점

  • 유사배열은 배열이 아니기 때문에, array의 메서드를 쓸 수 없다
  • 유사배열은 iterable이 아니기 때문에, 구형 브라우저에서 for...of나 전개연산자와 사용할 수 없다

2. 해결 방법

  1. Underscore.js
    유사배열을 지원하기 때문에 jQuery, arguments같은 유사배열 객체를 받아 배열처럼 사용할 수 있다

  2. 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 iterable
5
6// 2-1. Underscore.js
7_.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. arguments
2!function() {
3 for(const v of arguments) log(v)
4} (1, 2, 3) // 1 2 3
5
6// 2. NodeList
7for(const v of document.querySelectorAll('*')) log(v)
8// <html> <script> <head> ...

여기서 파악해볼 수 있는 추세는...

  1. ES6부터는 arrayLike객체도 [Symbol.iterator]()를 가지고 있다
  2. 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: true
9delete obj.prop // 동작 -> configurable: true
10
11Math.PI = 10 // 동작안함 -> writable: false
12delete 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를 하나의 구분자로 사용가능. 값으로 사용안할 것이기 때문에
    • '아무것도 찾지 못했거나, 아무일도 하지 못했을 때'를 구분하기 위한 값

# 열거가능한 값, 컬렉션

함수형 프로그래밍에선 컬렉션을 많이 다룬다. 컬렉션으로 볼 수 있는 값들은 아래와 같다

  1. JSON 데이터 타입 내의 object
  2. Array, Map, Set ...
  3. 그 외 [Symbol.iterator]()가 구현된 모든 iterable과 iterator
  4. 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처럼 보겠다는 방식
  • 좀 더 객체지향적인 면이 있음