Abstract Loop & Lazy Execution - 생성자로 data 받기
const Compx = class{
constructor(data) {this.data = data;}
[Symbol.iterator](){
const data = JSON.parse(JSON.stringify(this.data));
return {
next(){
let v;
while (v = data.shift()) {
if (!(v instanceof Object)) return {value:v};
if (!Array.isArray(v)) v = Object.values(v);
data.unshift(...v);
}
return {done: true};
}
};
}
};
const a = new Compx([{a:[1,2,3,4], b:'-'}, [5,6,7], 8,9]);
// 여러번 사용할 수 있음
console.log([...a]); // [1,2,3,4,'-',5,6,7,8,9]
console.log([...a]); // [1,2,3,4,'-',5,6,7,8,9]
위 CLASS는 **생성자로 data
를 받아 this.data
에 할당한다.
생성자로 받는 data가 Complex data라면?
그리고 iterator에서 이 data
가 complex data라고 생각한다면,,
Complex Data란?
배열도아니고 객체도아닌… 여러가지 형태가 섞여있는 Data 유형을 뜻한다.
이런 경우엔 slice()
메서드를 사용할 수도 없고, Object.assign
을 사용할 수도 없다.
Object.assign
딥카피 불가능 - 얕은 카피만 가능하다.딥카피란?
const obj = {a:1, b:2}; const clone = Object.assign(obj); console.log(clone); // {a:1, b:2} obj.a = 3; console.log(obj); // {a:3, b:2}; console.log(clone); // {a:3, b:2}
위와 같은 경우를 얕은 카피라고 부른다.
딥카피라면 위의 obj가 변경되도 clone은 변경이 안되어야 한다.
즉, 딥카피란 ‘참조’가 아닌 ‘값’인 것이다. ‘값을’ 완전히 복사해서 할당하는 개념이다.
즉, 이렇기 때문에 slice()
도 Object.assign
도 사용할 수 없다.
생성자로 받는 data가 Complex data이지만 순수 객체라면?
순수객체란 함수도 없고, 특별히 다른 객체가 없다는 뜻이다.
즉, json
처럼 순수하게 인식할 수 있는 data로만 되어있는 객체라고 생각한다면, 이 data를 복사하는 가장 빠른 방법은
const data = JSON.parse(JSON.stringify(this.data));
위 방법이다.
완전히 새롭고 똑같게 복사가된다.
위의 방법은 딥카피보다 훨씬 빠르다.
parse
와 stringify
는 C가 처리하기 때문이다.
즉 몇번이고 iterator를 호출해도 원본 data
에는 손상이 없다. 항상 사본을 사용하기 때문이다.
이렇게 하기위해 Symbol.iterator
를 한번 호출하고 iterator 객체를 받는 것이다.
Symbol.iterator
이를 사용하면 위의parse
,stringify
코드처럼 data 사본을 만들 기회가 생기는 것이다.
그리고 data 사본이 담긴 data라는 자율변수를 사용하기 위해 next 함수를 생성한다.
Tip. ES6+ 문법과 data 유형(배열이 아닌 경우도 있잖아?)
// ES6+는 아래와 같은 구조 : next : function () {} 를
// function을 생략하고 next(){}로 쓸 수 있다.
const Compx = class{
constructor(data) {this.data = data;}
[Symbol.iterator](){
const data = JSON.parse(JSON.stringify(this.data));
return {
next: function(){
let v;
while (v = data.shift()) {
if (!(v instanceof Object)) return {value:v};
if (!Array.isArray(v)) v = Object.values(v);
data.unshift(...v);
}
return {done: true};
}
};
}
};
const a = new Compx([{a:[1,2,3,4], b:'-'}, [5,6,7], 8,9]);
console.log([...a]); // [1,2,3,4,'-',5,6,7,8,9]
console.log([...a]); // [1,2,3,4,'-',5,6,7,8,9]
ES6+는 위와같은 구조를 아래로 변형시켜 쓸 수 있다.
const Compx = class{
constructor(data) {this.data = data;}
[Symbol.iterator](){
const data = JSON.parse(JSON.stringify(this.data));
return {
next(){
let v;
while (v = data.shift()) {
if (!(v instanceof Object)) return {value:v};
if (!Array.isArray(v)) v = Object.values(v);
data.unshift(...v);
}
return {done: true};
}
};
}
};
const a = new Compx([{a:[1,2,3,4], b:'-'}, [5,6,7], 8,9]);
console.log([...a]);
console.log([...a]);
그리고 data가 어떤 유형으로 올지 모르는 경우를 대비해 배열 리터럴로 감싸주면된다.
const Compx = class{
constructor(data) {this.data = data;}
[Symbol.iterator](){
const data = [JSON.parse(JSON.stringify(this.data))];
return {
next(){
let v;
// 그렇다면 받아들이는 data가 배열일 때만 아래가 처음을 성립하잖아?
// data가 배열로 시작해야만 shift로 맨앞에 추출하면서 시작할 수 있다.
// shift는 배열이어야 가능한 메서드
// 그래서 제일 안전하게는 위의 사본(data)에다가 대괄호를 씌우고 시작하는 것이다.
// 이 예제가 배열을 받아들여서 뻑이 안 났지만 Object를 받아들였으면 뻑이 났을 거다.
while (v = data.shift()) {
if (!(v instanceof Object)) return {value:v};
if (!Array.isArray(v)) v = Object.values(v);
data.unshift(...v);
}
return {done: true};
}
};
}
};
const a = new Compx([{a:[1,2,3,4], b:'-'}, [5,6,7], 8,9]);
console.log([...a]);
console.log([...a]);