Skip to content

Generator에서 for-in 사용시 hasOwnProperty 필요 여부 #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Jeewhan opened this issue Mar 25, 2018 · 1 comment
Closed

Generator에서 for-in 사용시 hasOwnProperty 필요 여부 #1

Jeewhan opened this issue Mar 25, 2018 · 1 comment

Comments

@Jeewhan
Copy link

Jeewhan commented Mar 25, 2018

안녕하세요, 인동님.

너무나 좋은 글, 감사히 읽고 있습니다.

글을 보다가 궁금한 점이 생겨서 질문을 드리고 싶습니다


링크 1에 나오는 아래 코드에 대한 질문입니다.

function *valuesIter(obj) {
  for (const key in obj) yield obj[key];
}
const users = {
  5: { id: 5, name: 'YB' },
  19: { id: 19, name: 'BX' },
  27: { id: 27, name: 'MD' }
};
for (const val of valuesIter(users)) log(val.name);
// YB
// BX
// MD

링크 2에도 유사한 코드가 있습니다

Object.assign(ObjIter, {
  values: ObjIter(function *(coll) {
    if (!coll) return;
    for (const key in coll) yield coll[key];
  }),
  entries: ObjIter(function *(coll) {
    if (!coll) return;
    for (const key in coll) yield [key, coll[key]];
  })
});

Generator 내부에서 for-in을 사용하고 있는데, hasOwnProperty에 대한 체크는 필요하지 않은지 여쭙고 싶습니다.


함수형에 관하여 좋은 콘텐츠들을 거듭 제공해주셔서, 늘 감사드립니다!

@indongyoo
Copy link
Member

indongyoo commented Mar 25, 2018

반갑습니다. 지환님! 넘나 고급지고 실제적인 질문 감사합니다. 이렇게 실제 코드에 대한 질문은 언제나 환영이고, 정말 좋습니다.


아래 each는 Iterable과 object를 순회하는 each의 간단 버전입니다 valuesIter는 기본 object를 기준으로 구현한 함수이기 때문에 모든 key에 해당하는 값을 순회합니다. 애초에 기본 객체가 아니면 순회를 할 이유가 거의 없기도 하고, 기본 객체의 사용을 권장하기 때문에 Functional.es의 ObjIter.values에서도 동일하게 적용했습니다.

function *valuesIter(obj) {
  for (const key in obj) yield obj[key];
}

function each(f, coll) {
  const iter = typeof coll[Symbol.iterator] == 'function' ?
    coll : valuesIter(coll);

  for (const val of iter) f(val);

  return coll;
}

const users = {
  5: { id: 5, name: 'YB' },
  19: { id: 19, name: 'BX' },
  27: { id: 27, name: 'MD' }
};

each(u => console.log(u.name), users);
// YB
// BX
// MD

그러므로 질문주신대로 만일 기본 객체가 아닌 객체를 순회하려고 하면 다음과 같은 문제가 생깁니다.

function CustomObject(object) {
  Object.assign(this, object);
}
CustomObject.prototype.get = function(key) {
  return this[key];
};
CustomObject.prototype.set = function(key, val) {
  this[key] = val;
  return this;
};

const customObject = new CustomObject({
  a: 'A',
  b: 'B'
});

console.log(customObject);
each(a => console.log(a), customObject);
// A
// B
// f (key) {
//     return this[key];
//   }
// f (key, val) {
//     this[key] = val;
//     return this;
//   }

위 문제를 해결하기 위해 each안에서 사용하고 있는 valuesIterif를 추가하는 것도 방법이지만, 만능이 되기 위해 안쪽 깊숙한 곳에 if를 넣을 필요는 없습니다. 분기는 사실 each에 넣기전에 함수 조합을 통해서도 만들 수 있습니다.

function *valuesIter2(obj) {
  for (const key in obj)
    if (obj.hasOwnProperty(key)) yield obj[key];
}
each(console.log, valuesIter2(customObject));
// A
// B

each의 인자인 coll은 Iterable/Iterator 프로토콜을 지원하기 때문에, 이것을 기반으로 valuesIter2와 같은 함수를 통해 프로토콜을 맞춰줄 수 있습니다. 이처럼 안쪽 깊숙한 곳에서 분기를 하기보다 최대한 바깥쪽에서 분기를 하면 오류율을 줄일 수 있습니다. 대부분의 분기는 바깥에서 정할 수 있습니다. 만능 함수 한 개보다 작은 함수 10개가 낫습니다. 위와 같이하면 each의 성능을 하향 평준화시킬 필요가 없게 되기도 합니다.

이건 별개의 이야기인데요. class 키워드를 사용하면 hasOwnProperty가 필요 없기도 합니다.

class CustomObject2 {
  constructor(object) {
    Object.assign(this, object);
  }
  get(key) {
    return this[key];
  }
  set(key, val) {
    this[key] = val;
    return this;
  }
}
each(console.log, new CustomObject2({a:1, b: 2}));
// 1
// 2

결론을 내리자면, 이와 같은 전략을 취할 수 있는 것은 ES6을 기반으로 할 때에 이야기일 것 같습니다. 자바스크립트의 발전과정에서 말씀하셨던 문제가 생겼고, hasOwnProperty가 그것의 해결을 위해 있는 헬퍼 함수이기도 합니다. 그래서 말씀주신대로 hasOwnProperty나 Object.keys의 활용이 거의 필수였고요. 자바스크립트가 더 발전하여 class 키워드를 사용할 수 있게 되었고(아직 모든 곳에서는 아니지만), for...of와 for...in은 더욱 자주, 잘 쓰일 기능이 될 것 입니다 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants