一。역할
배열 순회에 사용되며, for...in 으로 객체를 순회하는 것과 유사합니다
for (var i = 0; i < arr.length; i++) 는 사용하기 좋지만, 귀찮을 경우 arr.forEach() 를 사용할 수도 있습니다. 그렇다면 for...in 이 존재하는 의미는 무엇일까요?
for (var i = 0; i < arr.length; i++)는 간결하지 않습니다
너무 깁니다. 길면 실수하기 쉽습니다. 예를 들어 필자가 자주 범하는 실수: for (var j = 0; j < arr[i].length; i++)
- forEach 는 유연성이 부족합니다
arr.forEach 는 사용하기 어렵습니다. break 또는 return 으로 빠져나올 수 없기 때문입니다
for...in은 배열 순회에는 적합하지 않습니다
for...in 은 커스텀 속성뿐만 아니라 프로토타입 속성까지 순회하고, index 는 숫자가 아닌 문자열이며, 경우에 따라서는 순서대로 순회하지 않기도 합니다
따라서, for...in 으로 객체를 순회하는 것처럼 간단하고 사용하기 쉬운, 배열을 순회하는 더 편리한 방법이 필요합니다. 그것이 바로 for...of 입니다
二。특징
###1.다른 컬렉션도 순회 가능
배열뿐만 아니라 다른 컬렉션 (iterable) 도 순회할 수 있습니다. NodeList/arguments, 문자열, Map, Set 등을 포함합니다
샘플 코드는 다음과 같습니다:
// 배열
var arr = [1, 2, 2, 4];
for (var val of arr) {
console.log(val);
}
// 배열 유사 객체 (arguments, NodeList)
(function() {
for (var val of arguments) {
console.log(val);
}
})(1, 2, 4);
// Set
var uniqueArr = new Set(arr); // Set 자동 중복 제거
for (var val of uniqueArr) {
console.log(val);
}
// Map
var map = new Map([['a', 1], ['b', 2]]);
map.set('c', 3);
for (var [key, val] of map) {
console.log('map[' + key + '] = ' + val);
}
주의: Chrome47 은 var [key, val] of map 구문을 지원하지 않습니다. FF43 은 지원합니다. node --harmony v0.12.7 은 구조 분해 할당 식을 지원하지 않습니다. ES6 를 편안하게체험하고 싶다면, node 에 think.js 를 설치하는 것을 권장합니다
###2.객체는 순회 불가
for...of 는 객체 순회를 지원하지 않지만, [Symbol.iterator]() 메서드를 추가하여 다른 객체 (예를 들어 jQuery 객체) 가 for...of 순회를 지원하도록 할 수 있습니다. [Symbol.iterator]() 메서드가 있는 객체는 반복 가능합니다 (iterable)
어떻게 지원하지 않는지? 샘플 코드는 다음과 같습니다:
var obj = {
'a': 1,
'b': 2
}
for (var val of obj) {
console.log(val);
}
// Uncaught TypeError: is not a function
// 에러, 객체 순회를 지원하지 않습니다. 그것은 for...in 의 역할입니다
커스텀 객체가 for...of 순회를 지원하도록 하려면, [Symbol.iterator]() 메서드를 추가해야 합니다. 다음과 같이:
// 일반 메서드로 이터레이터 구현
class MyIterator {
constructor(obj) {
this.obj = obj;
this.keys = Object.keys(obj);
this.index = 0;
}
// iterator 인터페이스 구현
[Symbol.iterator]() {
return this;
}
next() {
var val = this.obj[this.keys[this.index]];
if (this.index === this.keys.length) {
return {
done: true
}
}
else {
this.index++;
return {
done: false,
value: val
}
}
}
}
// 테스트
var _obj = {
a: 2,
b: 1
}
for (var val of new MyIterator(_obj)) {
console.log(val);
}
잠깐, Symbol 이란 무엇일까요? Symbol 에는 큰 유래가 있습니다: Symbol 은 js 의제 7 번째기본 타입입니다 (원래 있던 6 개는 null, undefined, Number, Boolean, Object, String). 문자열도 객체도 아닙니다. 증거는 다음과 같습니다:
typeof Symbol() === 'symbol'
위 코드에 나타난 Symbol.iterator 는 빌트인 함수명입니다 (Symbol 타입이며, 일반 함수명은 String). for...of 문을 실행할 때, 인터프리터는 of后面的 객체에서 Symbol.iterator 라는 이름의 메서드를 찾습니다. 해당 메서드 실행 후 이터레이터 객체 iterator 를 반환합니다. iterator 를 얻은 후 당연히 .next(), .next() 하고 done 까지 호출합니다. 과정은 다음과 같습니다:
// 배열
var arr = [1, 2, 2, 4];
// for (var val of arr) {
// console.log(val);
// }
var iter = arr[Symbol.iterator]();
while (true) {
var res = iter.next();
if (res.done) {
break;
}
else {
var val = res.value;
console.log(val);
}
}
물론, 우리의 이 기능은 너무 약합니다. for...of 의 강력한 점은 break, continue, return 을 지원하는 것으로, 고전적인 for 루프와 같습니다
###3.커스텀 이터레이터 지원
사실 위에서 이미 커스텀 이터레이터를 구현했지만, 귀찮은 것 같습니다. 이것을 쓰느니 차라리 죽는 게 낫습니다. ES6 표준을 제정한 사람도 당연히 이를 고려했습니다. generator 구문 (function* + yield) 과 조합하면 쉽게 커스텀 이터레이터를 구현하여 for...of 순회를 지원할 수 있습니다:
var obj = {
'a': 1,
'b': 2
}
// generator 로 이터레이터 구현
obj[Symbol.iterator] = function*() {
for (var key in this) {
yield this[key];
}
}
for (var val of obj) {
console.log(val);
}
순식간에 이터레이터를 구현했습니다. 이제 훨씬 편리해졌습니다
P.S. 사실 우리의 MyIterator 는 엄밀히 말하면 generator 입니다. (이터레이터의) 제네레이터입니다
三。요약
ES5 는 forEach 를 도입했지만, 실제로는 별로 쓸모가 없습니다. 많은 경우, 작성한 forEach 를 고전적인 for 루프로 바꿔야 합니다..forEach 는 유연성 측면에서 every 에 못 미치지만, every 는 새로운 배열을 생성합니다..
好了, 이제 for...of 가 생겼습니다. forEach 와는 작별입니다
###참고 자료
- 《ES6 in Depth》: InfoQ 中文站에서 제공하는 무료 전자책
아직 댓글이 없습니다