이터레이터,,, 제너레이터,,, 이터러블,,, 이터레이터,,, 이터러블,,, 🤯
예전에 구글링하다가 일급 객체의 존재를 알게 됐다. 하지만 일급 객체가 뭔지, 왜 함수가 일급 객체인지는 오늘 새롭게 알게 됐다!
일단 일급 객체
는 JS에서만 사용되는 용어가 아니다. 컴퓨터 프로그래밍 언어 디자인에서 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체
를 가리킨다고 한다. 여기서 일반적으로 적용 가능한 연산이란 것은 함수에 매개변수로 넘기기, 수정하기, 변수에 대입하기
와 같은 연산들을 말한다. 일급이라고 해서 무슨 특별한 기능을 한다는 것이 아니라, 사용할 때 다른 요소들과 아무런 차별이 없다는 것을 뜻한다. Robin Popplestone이라는 사람이 후에 명확한 기준을 제시했다.
- 모든 일급 객체는
함수의 실질적인 매개변수
가 될 수 있다.- 모든 일급 객체는
함수의 반환값
이 될 수 있다.- 모든 일급 객체는
할당의 대상
이 될 수 있다.- 모든 일급 객체는
비교 연산을 적용
할 수 있다.
JS에서 function은 Object를 상속 받아 사용하는 prototype 객체
이다. 즉, function도 Object라는 것이다! 이 말은 JS에서 function 또한 일급 객체이며, 일급 함수라
는 말이다. 당연하게도 함수 또한 위의 기준을 충족한다. 함수가 함수의 매개변수, 반환값이 될 수 있고, 함수를 변수에도 할당할 수 있다. 평소에 아무렇지 않게 함수를 변수에 할당해서 쓸 수 있었던 게 다 일급 함수였기 때문이다!
그냥 사용이 자유롭다고 생각하고 넘기면 되는거 아냐? 싶지만 잘 알아둘 필요가 있다. 바로 JS에서 함수가 일급 객체이기 때문에 가능한 것이 있기 때문이다.
일급 함수여서 클로저를 사용할 수 있다? 🤔 무슨 말인지 감이 잘 안오지만 천천히 살펴보자.
사실 클로저 자체가 딱 뭐라고 말하기 힘든 개념인 거 같다. 누구는 환경이라고 하고, 누구는 함수라고 하고,,, MDN에서는 이렇게 정의하고 있다.
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
더 무슨 말인지 모르겠다. 함수와 렉시컬 환경과의 조합
? 이 말을 이해하려면 스코프 얘기를 다시 꺼내야한다. 간단하게 복기해보자면 스코프는 크게 전역
, 지역 스코프
로 나눌 수 있다. 전역 스코프에 선언된 변수들은 코드 어디에서든지 참조 가능한 반면, 지역 스코프에 선언된 변수들은 해당 영역과 하위 영역에서만 참조 가능하다. 이제 스코프와 일급 함수를 생각하면서 클로저에 대해 알아보자.
쉬운 예시를 들어보자면, 사과가 몇 갠지 밖에 말하지 못하는 머쓱이의 집에 사과가 5개 있다고 가정해보자. 머쓱이 친구들은 머쓱이네 집에 들어가지 못하기 때문에 머쓱이네에 사과가 몇 개 있는지 알 수 없다. (= 전역에서 지역 변수를 참조할 수 없다!) 하지만 방법이 있다. 바로 외출한 머쓱이에게 사과가 몇 개 있는지 물어보는 것이다. 그럼 머쓱이는 친절하게 사과의 개수를 알려준다.
여기서 머쓱이
는 함수의 반환값으로써 반환된 내부함수
를 의미하고, 사과 5개
는 머쓱이가 선언됐을 때의 스코프인 렉시컬 환경
을 의미한다. 머쓱이는 외출해서도 사과 5개의 존재를 알고 있다.
결론! 여기서 머쓱이가 바로 클로저다!
결국 클로저는 내부함수가 반환되었을 때 내부함수가 선언되었던 환경인 스코프를 기억해서 자신이 선언됐을 때의 환경(스코프) 밖에서도 그 환경에 접근할 수 있는 함수를 말한다.
물론 머쓱이가 집 밖에서도 사과 개수를 알고있는 이유(현실 세계에서는 당연한거지만,,,)를 설명하려면 스코프 체인
에 대해 더 알아봐야한다. 🤯
ES6부터 기존의 for문 대신 간편하게 사용하는 for...of
. 당연히 내부적으로 초기화식, 조건식, 증감식이 구현되어 있을 줄 알았는데 아니었다! 일반 for문은 예를 들어 i를 설정해서 인덱스로 array의 요소에 접근하는 방식이었는데, 같은 방식으로 set과 map은 순회할 수 없다. 반면, for...of는 array는 물론, set과 key의 각 요소에 접근할 수 있는 걸로 보아 for...of의 작동방식은 기존 for문과는 다르다는 것을 알 수 있다. 그렇다면 내부적으로 어떻게 구현되어있는걸까?
먼저 이터레이션 프로토콜
은 데이터 컬렉션(Array, Set, Map)을 순회하기 위한 프로토콜이다. 프로토콜을 준수한 객체는 for...of으로 순회할 수 있다. 이터레이션 프로토콜에는 이터러블 프로토콜
과 이터레이터 프로토콜
이 있다.
이터러블
은 이터러블 프로토콜을 준수한 객체다. 이터러블은 무조건 Symbol.iterator
메소드를 가지고 있어야한다. 이 Symbol.iterator는 또 Symbol.iterator를 그대로 갖고 있다. well-formed iterable은 자기자신을 반환하는 Symbol.iterator를 가지고 있는 이터러블을 말한다.
이터레이터
는 이터레이터 프로토콜을 준수한 객체다. 이터레이터 프로토콜은 next 메소드를 가지고, next
메소드를 호출하면 이터러블을 순회하며 value, done 프로퍼티를 갖는 이터레이터 결과 객체를 반환하는 것이다. well-formed iterator는 어디에서든 Symbol.iterator로 이터레이터를 만들었을 때 이전까지 진행되어있던 자기의 상태에서 계속해서 next()를 할 수 있는 이터레이터를 말한다.
스코프 체인이 어떻게 일어나는지, 그리고 클로저를 이해는 했는데 어떻게 사용해야할 지 아직 잘 모르기 때문에 더 공부해보려고 한다.
솔직히 뭔지 잘 모르겠다 ㅋㅋㅋ,,, 자세하게 찾아봐야 될 것 같다.
아직 이해를 못했고,, 더 쓰면 글이 너무 방대해질 거 같아서 다음에 다시 알아보려고 한다,,
클로저에 대해서 얼렁뚱땅 넘어갈 뻔 했는데 다행히 일급함수를 다루면서 다시 복기할 수 있는 기회가 됐다. 렉시컬 환경이 뭔가 싶었는데 쉬운 예시로 생각해보니깐 이해할 수 있었다. 그리고 검색할 때마다 모던 자바스크립트 Deep Dive가 나오는데 이제 승복해야겠다. 저 책을 사야겠다 ㅋㅋㅋㅋㅋ
그리고 저번주보다 글 쓰는게 0.001% 쉬워졌다! 강의 자료 + 구글링 + 찾아보면서 내가 이해한 점을 간략하게 쓰려고 하니깐 뭔가 부담이 아주아주 약간덜한 것 같다.
이터레이터, 이터러블은 글에 써놓긴 했지만 솔직히 완전히 이해하지 못했다. 아직 강의가 좀 남아있으니깐 더 들으면서 찾아봐야겠다