소프트웨어는 데이터를 처리하기 위해 만들어졌기 때문에 데이터를 처리하는데 사용하는 패턴은 코드 전체의 가독성에 큰 영향을 미침
이터레이터 패턴(Iterator pattern)은 수십 년 동안 사용되어 온 디자인 패턴
이 패턴을 사용하면 데이터를 덩어리(chunk) 단위로 표준화된 방법을 사용해 각각 처리 가능
이터레이터 패턴의 접근 방식은 데이터 전체를 한꺼번에 처리하기보다 데이터를 일정 단위로 쪼개고, 이 조각들을 차례대로 순회하여 점진적으로 처리하면 좀 더 범용적이고 유용할 것이라는 아이디어에서 출발
작업을 반복할 때, 데이터가 총 몇 개의 조각으로 이루어져 있는지 사전에 알기 쉽지 않음
이터레이터 패턴에서는 데이터를 전부 처리했음에도 불구하고 또 다른 작업을 시작할 때 특별한 값을 사용하거나 예외를 발생시켜 반복 작업이 종료되었다는 신호를 줌
이터레이터 패턴의 핵심은 반복 작업으로 데이터를 처리할 때 표준화된 방법을 제공한다는 것
처리해야 할 데이터 컬렉션의 형태가 다르면 반복 작업에 사용되는 코드도 제각각이 됨
이터레이터 패턴을 사용하면 깔끔하고 이해하기 쉬운 코드로 반복 작업 수행 가능
JS 커뮤니티에서 수년간 이터레이터 패턴을 명세서에 추가하려고 노력한 끝에 ES6 명세서에 JS 내장 문법을 통해 이터레이터 패턴을 구현하는 구체적인 프로토콜(protocol)이 추가
이 프로토콜에서 next() 메서드는 이터레이터 리절트(Iterator result)라 불리는 객체를 반환하고, 이터레이터 리절트 객체에는 value와 done이라는 프로퍼티가 있어야 한다고 규정
반복 작업이 끝나지 않은 경우, done에는 불리언 값 false가 저장되어야 한다고 정의
ES6에서 정의한 이터레이션(Iteration) 프로토콜을 사용하면 데이터를 한 조각씩 순차적으로 처리
next() 호출 반환값인 이터레이터 리절트 객체의 done 프로퍼티가 true면 반복 작업 중단
이러한 방식이 다소 소모값이 있기 때문에 ES6에서는 for...of 반복문 같은 새로운 문법과 API를 새롭게 정의해 표준화된 방법으로 이터레이터 리절트 객체를 소비할 수 있도록 제공
for...of 반복문 말고도 표현식 ...을 사용하면 이터레이터를 소비할 수 있음
...은 대칭 형태인 전개 구문(Spread syntax)과 나머지 매개변수(Rest parameter)를 이용
전개 구문이 바로 이터레이터를 소비하는 주체인 이터레이터 소비자(cousumer)
이터레이터를 펼치려면 펼칠 무언가가 있어야 하며 JS에서는 배열이나 함수 호출 시 넘기는 인수를 다룰 때 이런 시나리오가 가능
전개 구문과 나머지 매개변수에서 사용되는 표현식 ...은 for...of 반복문과 마찬가지로 이터레이터 소비 프로토콜을 준수하며, 그 결과로 이터레이터에서 원하는 값을 추출해 배열이나 인수 목록 같이 원하는 곳에 값 할당 가능
이터레이터 소비 프로토콜은 순회 가능한 값인 이터러블(Iterable)을 소비하는 기술적인 방법이라고 정의 가능
이터레이터 소비 프로토콜은 이터러블을 사용해 이터레이터 인스턴스를 생성하고, 생성한 이터레이터 인스턴스를 소비해 연산을 마무리
인스턴스를 여러 개 만들기만 하면 하나의 이터러블을 여러번 소비 가능
ES6에서는 문자열, 배열, Map, Set 같이 기본이 되는 자료구조나 컬렉션을 이터러블로 정의
배열은 이터러블이며 전개 구문...으로 이터레이터를 소비해 배열을 얕게 복사(Shallow copy) 가능
문자열 역시 이터러블이므로 전개 구문으로 글자 하나하나 순회 가능
Map은 지금까지 살펴본 이터레이터와는 다르게 기본 이터레이터를 지원하며 Map의 내장 메서드 entries를 호출하면 Map의 값과 키까지 포함한 2차원 배열인 entry 튜플을 순회 가능
for...of 반복문은 Map이 지원하는 기본 이터레이터를 순회하며 배열 구조 분해(Array destructuring) 문법을 사용하여 튜플을 키, 값으로 분해
JS 내장 이터러블은 우리 직관에 부합하는 방식으로 이터레이션을 지원
키만 대상으로(keys()), 값만 대상으로(values()), 키와 값을 대상으로(entries()) 순회하는 메서드를 제공
기본 이터러블을 사용하는 것 외에도 이터레이션 프로토콜을 준수하는 자료구조를 직접 만든다면 해당 자료구조에 ...과 for...of 반복문을 적용할 수 있음
표준화된 이터레이션 프로토콜을 준수하면 전반적인 코드 가독성과 이해도가 올라간다는 장점이 있음
이터레이션 소비 프로토콜은 이터러블을 대상으로 작동
이터레잉션 프로토콜에 직접 이터레이터를 제공할 수 있는 이유는, 이터레이터는 그 자체로 이터러블이기 때문
기존에 존재하는 이터레이터를 사용해 이터레이터 인스턴스를 만들면 이터레이터 그 자체가 반환
거의 모든 JS 개발자가 클로저(closure)를 이용하고 있음
클로저는 JS에만 있는 기능이 아니고 여러 주요 프로그래밍 언어에서 사용하는 기능
JS에서는 변수나 반복문만큼 클로저를 이해하는게 중요하며, 그만큼 클로저는 JS를 지탱하는 근본적인 기술
클로저란 함수가 정의된 스코프가 아닌 다른 스코프에서 함수가 실행되더라도, 스코프 밖에 있는 변수를 기억하고 이 외부 변수에 계속 접근할 수 있는 경우를 의미
클로저만의 특징은 크게 두 가지가 있음
1) 클로저는 함수의 타고난 특징이며 객체는 클로저가 되지 않지만 함수는 자연스럽게 클로저가 됨
2) 클로저를 직접 보고 싶다면 함수를 해당 함수가 정의된 스코프가 아닌 다른 스코프에서 실행해야 함
function greeting(msg) { return function who(name) { console.log(`${ name } 님, ${ msg }!`); }; } var hello = greeting('안녕하세요'); var howdy = greeting('잘 지내시나요'); hello('카일'); // 카일 님, 안녕하세요! hello('보라'); // 보라 님, 안녕하세요! howdy('호진'); // 호진 님, 잘 지내시나요!
대부분의 개발자는 함수 greeting()이 실행 종료된 후에는 함수에서 사용했던 변수 전체가 가비지 컬렉션(Garbage Collection)의 대상이 되어 메모리에서 삭제될 것이라고 예상
예시에서 첫 번째와 두 번째 greeting을 호출한 이후에는 msg가 사라질 것이라 예상
하지만 클로저 때문에 변수들은 사라지지 않음
내부 함수 인스턴스들이 hello와 howdy에 각각 할당되면서 아직 살아 있고, 이 인스턴스들은 여전히 msg 변수를 각각 에워싸고 있기 때문
클로저는 변수 msg를 스냅숏(snapshot)한 값(복사해서 별도로 만든 값)을 사용하지 않음
변수 자체와 직접적인 관계를 맺어 변수가 업데이트 되는 것을 관찰하고 최신 값을 가져와서 사용
클로저는 콜백과 같이 비동기 작업을 수행하는 코드에서 흔히 찾아볼 수 있음
클로저는 모든 언어에서 사용되는 가장 보편적이고 중요한 프로그래밍 패턴
JS에서는 클로저를 활용하지 않으면서 유용한 작업을 한다는 것을 상상하기 어려울 정도로 중요
JS를 지탱하는 가장 강력한 메커니즘인 this는 가장 오해를 많이 받는 메커니즘
함수에서 this가 가리키는 것이 함수 자기 자신이라는 오해가 가장 많이 알려진 오해 중 하나
또한 this는 메서드가 속한 인스턴스를 참조한다는 오해도 있는데 이 두 가지 모두 틀림
이러한 오해는 JS가 아닌 다른 언어에서의 this 작동 방식 때문에 생김
함수는 정의되는 시점에 클로저를 통해서 함수를 에워싸는 스코프에 부착되며 이 때의 스코프는 변수가 어떤 것을 참조하는지를 결정하는 규칙 모음
함수는 스코프 말고도 자신이 어디까지 접근 가능한 지 결정하는 함수만의 특징을 가짐
이 특징은 실행 컨텍스트(Execution context) 개념으로 가장 잘 설명되며 함수는 this 키워드를 통해 실행 컨텍스트에 접근
실행 컨텍스트는 함수가 실행되는 동안 함수에서 사용할 수 있는 프로퍼티를 가진 유형의 객체라고 생각하면 이해하기 쉬움
스코프도 객체라고 생각할 수 있지만, 스코프에서 객체는 JS 엔진 내부에 숨겨져 있고 함수 하나랑 동일하며, 프로퍼티의 경우 함수 내부에서 사용할 수 있는 식별자(변수) 형태를 띤다는 점에서 차이가 있음
스코프는 정적(static)이며 함수를 정의하는 순간, 해당 스코프에서 사용할 수 있는 한정된 변수 집합을 포함
반면에 함수의 실행 컨텍스트는 동적(Dynamic)이며 함수를 정의한 위치나 함수를 호출하는 위치와 상관 없이 함수의 호출 방식에 따라 결정
this는 함수의 정의에 종속되어 결정되는 변치 않는 특성이 아니라 함수를 호출할 때마다 결정되는 동적인 특성
함수에서 this를 사용하면 컨텍스틀 동적으로 지정할 수 있고, 다른 객체에도 해당 함수를 재사용할 수 있어 매우 유연
스코프가 지정된 함수는 다른 스코프를 참조할 수 없고 변수를 지정할 수도 없음
this를 사용하면 동적으로 컨텍스트를 지정할 수 있으므로 특정한 작업 환경에서 아주 유용
this가 함수 실행에 관한 특징이라면 프로토타입(prototype)은 객체, 구체적으로 프로퍼티에 접근할 때 일어나는 동작과 관련된 특징
프로토타입은 두 객체를 연결하는 연결 장치이며, 연결 장치는 숨겨져 있어서 보이지 않음
몇 가지 방법을 사용하면 숨겨진 장치가 노출되고 더 자세히 관찰해 볼 수 있음
연결 장치는 객체가 생성될 때 만들어지고, 이 장치를 통해 새롭게 생성된 객체는 기존에 존재하는 다른 객체에 연결
프로토타입을 통해 연결된 일련의 객체를 프로토타입 체인(prototype chain)이라고 부름
프로토타입 연결 장치가 존재하는 이유는, 한 객체에 없는 프로퍼티나 메서드에 접근하려 할 때 다른 객체에서 위임을 받아 해당 접근을 처리할 수 있도록 하기 위함
프로퍼티 메서드 접근 권한을 위임하면 두 개 이상의 객체가 서로 협력하여 작업 가능
객체 프로토타입 연결 장치를 직접 정의하고 싶을 때는 Object.create()를 사용하여 객체를 만들면 됨
Object.create()의 첫 번째 인수에는 새롭게 생성할 객체를 어떤 객체와 연결할지 명시
메서드를 호출하면 첫 번째 인수로 넘긴 객체와 연결된 새로운 객체가 반환
프로토타입 체인을 통한 위임은 프로퍼티에 접근해 값을 찾을 때만 적용
객체에 새로운 프로퍼티를 추가할 때는 해당 객체가 어떤 객체에 연결되어 있는지와는 상관없이 바로 그 객체에 프로퍼티가 추가
Object.create(null)을 호출하면 어떤 객체에도 연결되어 있지 않은 순수 독립 객체가 만들어짐
이런 객체가 필요한 특별한 경우가 있음
함수 호출 시 this가 동적으로 컨텍스트를 가져오는 중요한 이유는 프로토타입 체인을 통해 위임한 객체에 있는 메서드를 호출할 때, this가 코드 작성자의 의도대로 되게끔 하는데 있음
다른 언어들과 달리 JS는 this가 동적으로 결정되며, 이러한 특징은 프로토타입을 통한 위임, 특히 class에서 위임을 가능하게 만드는 중요한 요소