Programing

JavaScript에서 객체를 반복 할 수없는 이유는 무엇입니까?

lottogame 2020. 11. 6. 07:44
반응형

JavaScript에서 객체를 반복 할 수없는 이유는 무엇입니까?


기본적으로 객체를 반복 할 수없는 이유는 무엇입니까?

객체 반복과 관련된 질문을 항상 봅니다. 일반적인 해결책은 객체의 속성을 반복하고 그런 방식으로 객체 내의 값에 액세스하는 것입니다. 이것은 너무 흔하게 보여서 왜 객체 자체가 반복 할 수 없는지 궁금합니다.

ES6과 같은 문 for...of은 기본적으로 객체에 사용하는 것이 좋습니다. 이러한 기능은 개체를 포함하지 않는 특수한 "반복 가능한 개체"에만 사용할 수 있기 때문에 {}사용하려는 개체에 대해이 기능을 사용하려면 여러 단계를 거쳐야합니다.

for ... of 문은 반복 가능한 객체 (Array, Map, Set, arguments 객체 등 포함) 반복 하는 루프를 만듭니다 .

예를 들어 ES6 생성기 함수 사용 :

var example = {a: {e: 'one', f: 'two'}, b: {g: 'three'}, c: {h: 'four', i: 'five'}};

function* entries(obj) {
   for (let key of Object.keys(obj)) {
     yield [key, obj[key]];
   }
}

for (let [key, value] of entries(example)) {
  console.log(key);
  console.log(value);
  for (let [key, value] of entries(value)) {
    console.log(key);
    console.log(value);
  }
}

위의 코드는 Firefox ( ES6 지원 ) 에서 코드를 실행할 때 예상되는 순서대로 데이터를 올바르게 기록합니다 .

해키의 출력 ...

기본적으로 {}객체는 반복 할 수 없지만 그 이유는 무엇입니까? 반복 가능한 객체의 잠재적 이점보다 단점이 더 큽니까? 이것과 관련된 문제는 무엇입니까?

때문에 또한, {}객체는 "어레이 같은"컬렉션과는 다른 예 : "반복자는 객체" NodeList, HtmlCollection그리고 arguments, 그들은 배열로 변환 할 수 없습니다.

예를 들면 :

var argumentsArray = Array.prototype.slice.call(arguments);

또는 Array 메서드와 함께 사용 :

Array.prototype.forEach.call(nodeList, function (element) {}).

위의 질문 외에도 {}객체를 반복 가능 하게 만드는 방법에 대한 작업 예제 , 특히 [Symbol.iterator]. 이렇게하면 이러한 새로운 {}"반복 가능한 객체"가 for...of. 또한 개체를 반복 가능하게 만들면 배열로 변환 할 수 있는지 궁금합니다.

아래 코드를 시도했지만 TypeError: can't convert undefined to object.

var example = {a: {e: 'one', f: 'two'}, b: {g: 'three'}, c: {h: 'four', i: 'five'}};

// I want to be able to use "for...of" for the "example" object.
// I also want to be able to convert the "example" object into an Array.
example[Symbol.iterator] = function* (obj) {
   for (let key of Object.keys(obj)) {
     yield [key, obj[key]];
   }
};

for (let [key, value] of example) { console.log(value); } // error
console.log([...example]); // error

나는 이것을 시도 할 것이다. 나는 ECMA와 관련이 없으며 그들의 의사 결정 과정에 대한 가시성이 없기 때문에 그들이 아무것도하지 않았거나하지 않은 이유명확하게 말할 수 없습니다 . 그러나 나는 내 가정을 진술하고 최선을 다할 것입니다.

1. for...of처음에 구성을 추가하는 이유는 무엇 입니까?

JavaScript에는 이미 for...in객체의 속성을 반복하는 데 사용할 수 있는 구조가 포함되어 있습니다. 그러나 객체의 모든 속성을 열거하고 간단한 경우에만 예측 가능하게 작동하는 경향이 있으므로 실제로 forEach 루프 는 아닙니다.

그것은 더 복잡한 경우는 (그것의 사용이 하나되는 경향이 배열에 포함에 고장 낙담하거나 철저하게 난독 사용에 필요한 안전 장치에 의해 for...in배열로 올바르게 ). hasOwnProperty(다른 것들 중에서) 사용하여 해결할 수 있지만 약간 어색하고 우아하지 않습니다.

따라서 내 가정은 for...of구성과 관련된 결함을 해결하기 위해 구성이 추가되고 for...in일을 반복 할 때 더 큰 유용성과 유연성을 제공 한다는 것입니다. 사람들은 일반적으로 모든 컬렉션에 적용 할 수 for...in있는 forEach루프 로 취급 하고 가능한 모든 컨텍스트에서 정상적인 결과를 생성 하는 경향이 있지만, 그렇게되는 것은 아닙니다. for...of루프 수정있다.

또한 기존 ES5 코드가 ES6에서 실행되고 ES5에서와 동일한 결과를 생성하는 것이 중요하다고 가정합니다. 따라서 예를 들어 for...in구조 의 동작에 대한 주요 변경 사항을 만들 수 없습니다 .

2. 어떻게 for...of작동합니까?

참조 문서는 이 부분에 유용합니다. 특히 속성을 iterable정의하는 경우 개체가 고려 됩니다 Symbol.iterator.

속성 정의는 컬렉션의 항목을 one, by, one으로 반환하고 가져올 항목이 더 있는지 여부를 나타내는 플래그를 설정하는 함수 여야합니다. 일부 객체 유형에 대해 미리 정의 된 구현이 제공되며 for...of반복기 함수에 대한 단순히 대리자를 사용하는 것이 비교적 명확 합니다.

이 접근 방식은 자체 반복자를 제공하는 것이 매우 간단하므로 유용합니다. 나는이 접근 방식이 이전에는 없었던 속성을 정의하는 데 의존하기 때문에 실제적인 문제를 제시했을 수 있다고 말할 수 있습니다. 단, 의도적으로 찾아 보지 않는 한 새로운 속성은 본질적으로 무시되기 때문에 사실이 아닙니다. for...in루프에는 키 등으로 표시되지 않습니다 .) 그래서 그것은 사실이 아닙니다.

실용적이지 않은 문제는 제쳐두고 모든 객체를 미리 정의 된 새 속성으로 시작하거나 "모든 객체가 컬렉션"이라고 암시 적으로 말하는 것은 개념적으로 논란의 여지가있는 것으로 간주되었을 수 있습니다.

3. 개체 가 기본적으로 iterable사용 되지 않는 이유는 무엇 for...of입니까?

생각 엔 이것이 다음의 조합이라는 것입니다.

  1. iterable기본적으로 모든 개체 만드는 것은 이전에 속성이 없었던 속성을 추가하거나 개체가 (필연적으로) 컬렉션이 아니기 때문에 허용되지 않는 것으로 간주되었을 수 있습니다. Felix가 지적했듯이 "함수 또는 정규 표현식 객체를 반복한다는 것은 무엇을 의미합니까?"
  2. 간단한 객체는 이미를 사용하여 반복 할 수 for...in있으며 기본 제공 반복기 구현이 기존 for...in동작 과 다르게 / 더 잘 수행 할 수있는 작업이 명확하지 않습니다 . 따라서 # 1이 잘못되고 속성 추가가 허용 되더라도 유용 하지 않은 것으로 간주 될 수 있습니다 .
  3. 객체를 만들고 싶은 사용자 iterableSymbol.iterator속성 을 정의하여 쉽게 만들 수 있습니다 .
  4. ES6 사양은 기본적으로 일반 객체를 .NET Framework로 사용하는 것에 비해 몇 가지 작은 이점 있는 Map 유형 도 제공 합니다 . iterableMap

참조 문서에서 # 3에 대해 제공된 예제도 있습니다.

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};

for (var value of myIterable) {
    console.log(value);
}

객체를 쉽게 만들 iterable수 있고을 사용하여 이미 반복 할 수 for...in있으며 기본 객체 반복기가 수행해야하는 작업에 대한 명확한 동의가 없을 가능성이 높기 때문에 (하는 작업이 수행하는 작업과 어떻게 든 달라야하는 경우 for...in) 합리적으로 보입니다. 개체가 iterable기본적으로 만들어지지 않을만큼 충분합니다 .

예제 코드는 다음을 사용하여 다시 작성할 수 있습니다 for...in.

for (let levelOneKey in object) {
    console.log(levelOneKey);         //  "example"
    console.log(object[levelOneKey]); // {"random":"nest","another":"thing"}

    var levelTwoObj = object[levelOneKey];
    for (let levelTwoKey in levelTwoObj ) {
        console.log(levelTwoKey);   // "random"
        console.log(levelTwoObj[levelTwoKey]); // "nest"
    }
}

... 또는 iterable다음과 같은 작업을 수행하여 원하는 방식으로 개체 만들 수도 있습니다 (또는 대신 할당하여 모든 개체 iterable만들 수 있습니다 Object.prototype[Symbol.iterator]).

obj = { 
    a: '1', 
    b: { something: 'else' }, 
    c: 4, 
    d: { nested: { nestedAgain: true }}
};

obj[Symbol.iterator] = function() {
    var keys = [];
    var ref = this;
    for (var key in this) {
        //note:  can do hasOwnProperty() here, etc.
        keys.push(key);
    }

    return {
        next: function() {
            if (this._keys && this._obj && this._index < this._keys.length) {
                var key = this._keys[this._index];
                this._index++;
                return { key: key, value: this._obj[key], done: false };
            } else {
                return { done: true };
            }
        },
        _index: 0,
        _keys: keys,
        _obj: ref
    };
};

여기에서 (크롬에서) 재생할 수 있습니다 : http://jsfiddle.net/rncr3ppz/5/

편집하다

업데이트 된 질문에 대한 응답으로 예, ES6 iterable스프레드 연산자사용하여를 배열 로 변환 할 있습니다.

그러나 이것은 아직 Chrome에서 작동하지 않는 것 같거나 적어도 내 jsFiddle에서 작동하도록 할 수 없습니다. 이론적으로는 다음과 같이 간단해야합니다.

var array = [...myIterable];

질문은 " 내장 된 객체 반복 이없는 이유는 무엇입니까?

객체 자체에 반복성을 추가하면 의도하지 않은 결과가 발생할 수 있으며 순서를 보장 할 방법은 없지만 반복자를 작성하는 것은 다음과 같이 간단합니다.

function* iterate_object(o) {
    var keys = Object.keys(o);
    for (var i=0; i<keys.length; i++) {
        yield [keys[i], o[keys[i]]];
    }
}

그때

for (var [key, val] of iterate_object({a: 1, b: 2})) {
    console.log(key, val);
}

a 1
b 2

Objects는 매우 좋은 이유로 자바 스크립트에서 반복 프로토콜을 구현하지 않습니다. JavaScript에서 개체 속성을 반복 할 수있는 두 가지 수준이 있습니다.

  • 프로그램 수준
  • 데이터 수준

프로그램 수준 반복

프로그램 수준에서 개체를 반복 할 때 프로그램 구조의 일부를 검사합니다. 반사 작업입니다. 일반적으로 데이터 수준에서 반복되는 배열 유형을 사용하여이 문을 설명해 보겠습니다.

const xs = [1,2,3];
xs.f = function f() {};

for (let i in xs) console.log(xs[i]); // logs `f` as well

의 프로그램 수준을 살펴 보았습니다 xs. 배열은 데이터 시퀀스를 저장하기 때문에 정기적으로 데이터 수준에만 관심이 있습니다. for..in대부분의 경우 배열 및 기타 "데이터 지향"구조와 관련하여 의미가 없습니다. 이것이 ES2015가 도입 된 이유이자 for..of반복 가능한 프로토콜입니다.

데이터 수준 반복

그것은 우리가 원시 유형과 함수를 구별함으로써 프로그램 레벨에서 데이터를 단순히 구별 할 수 있다는 것을 의미합니까? 아니요, 함수는 Javascript의 데이터 일 수도 있습니다.

  • Array.prototype.sort 예를 들어 함수가 특정 정렬 알고리즘을 수행 할 것으로 예상합니다.
  • 같은 덩크 () => 1 + 2는 느리게 평가 된 값에 대한 기능적 래퍼입니다.

원시 값 외에도 프로그램 수준을 나타낼 수 있습니다.

  • [].length for instance is a Number but represents the length of an array and thus belongs to the program domain

That means that we can't distinguish the program and data level by merely checking types.


It is important to understand that the implementation of the iteration protocols for plain old Javascript objects would rely on the data level. But as we've just seen, a reliable distinction between data and program level iteration is not possible.

With Arrays this distinction is trivial: Every element with an integer-like key is a data element. Objects have an equivalent feature: The enumerable descriptor. But is it really advisable to rely on this? I believe it is not! The meaning of the enumerable descriptor is too blurry.

Conclusion

There is no meaningful way to implement the iteration protocols for objects, because not every object is a collection.

If object properties were iterable by default, program and data level were mixed-up. Since every composite type in Javascript is based on plain objects this would apply for Array and Map as well.

for..in, Object.keys, Reflect.ownKeys etc. can be used for both reflection and data iteration, a clear distinction is regularly not possible. If you're not careful, you end up quickly with meta programming and weird dependencies. The Map abstract data type effectively ends the conflating of program and data level. I believe Map is the most significant achievement in ES2015, even if Promises are much more exciting.


You can easily make all objects iterable globally:

Object.defineProperty(Object.prototype, Symbol.iterator, {
    enumerable: false,
    value: function * (){
        for(let key in this){
            if(this.hasOwnProperty(key)){
                yield [key, this[key]];
            }
        }
    }
});

This is the latest approach (which works in chrome canary)

var files = {
    '/root': {type: 'directory'},
    '/root/example.txt': {type: 'file'}
};

for (let [key, {type}] of Object.entries(files)) {
    console.log(type);
}

Yes entries is now a method thats part of Object :)

edit

After looking more into it, it seems you could do the following

Object.prototype[Symbol.iterator] = function * () {
    for (const [key, value] of Object.entries(this)) {
        yield {key, value}; // or [key, value]
    }
};

so you can now do this

for (const {key, value:{type}} of files) {
    console.log(key, type);
}

edit2

Back to your original example, if you wanted to use the above prototype method it would like like this

for (const {key, value:item1} of example) {
    console.log(key);
    console.log(item1);
    for (const {key, value:item2} of item1) {
        console.log(key);
        console.log(item2);
    }
}

Technically, this is not an answer to the question why? but I have adapted Jack Slocum’s answer above in light of BT’s comments to something which can be used to make an Object iterable.

var iterableProperties={
    enumerable: false,
    value: function * () {
        for(let key in this) if(this.hasOwnProperty(key)) yield this[key];
    }
};

var fruit={
    'a': 'apple',
    'b': 'banana',
    'c': 'cherry'
};
Object.defineProperty(fruit,Symbol.iterator,iterableProperties);
for(let v of fruit) console.log(v);

Not quite as convenient as it should have been, but it’s workable, especially if you have multiple objects:

var instruments={
    'a': 'accordion',
    'b': 'banjo',
    'c': 'cor anglais'
};
Object.defineProperty(instruments,Symbol.iterator,iterableProperties);
for(let v of instruments) console.log(v);

And, because every one is entitled to an opinion, I can’t see why Objects are not already iterable either. If you can polyfill them as above, or use for … in then I can’t see a simple argument.

한 가지 가능한 제안은 iterable이 객체 유형 이므로 다른 객체가 시도에서 폭발 할 경우를 대비하여 객체의 하위 집합으로 iterable이 제한되었을 수 있다는 것입니다.

참고 URL : https://stackoverflow.com/questions/29886552/why-are-objects-not-iterable-in-javascript

반응형