2016-03-13 2 views
3

Предполагая, что у меня есть такой объект, как это:Javascript - Создание Iterable Abstraction Layer

var result = { 
    items: [ 
     'item1', 'item2', 'item3' 
    ] 
} 

Я ищу способ создать объект, который будет абстрактный мой результат объект и еще останется совместимым с ним:

function wrapper(result) { 
    this.items = ??? 
} 

для того, чтобы в состоянии в состоянии сделать это

var p = new wrapper(result); 

for(var x = 0; x < p.items.length; x++) { 
    console.log(p[x]) // = item1, item2, item3 
} 

Это может быть легко выполнено в PHP с помощью ArrayIterator. Мне было интересно, можно ли это сделать в JS, в частности в NodeJS.

Просто для уточнения:

Представьте себе оригинальный объект является результатом запроса дб. Тем не менее, форматирование этого объекта результата не может быть подано потребителю как есть, и его необходимо адаптировать. В этом случае я не останавливался на адаптации, но для примера предполагаем, что каждое значение должно быть верхним.

Кроме того, оригинальный список очень большой. Я мог бы, очевидно, итератировать, скопировать все значения и загладить их, но в большинстве случаев это не потребуется для всех значений, поэтому было бы неэффективно делать это для всех элементов каждый раз. Идея обертки заключается в том, чтобы действовать как прокси-сервер, который я мог бы повторить. Обертка, в свою очередь, получает результат от исходного объекта и изменяет его на лету.

+0

Чтобы сделать работу с кодом, вы просто назначили this.items result.items. Отвечает ли это на ваш вопрос? – aw04

+2

'this.items = result.items;' ...? И зачем создавать объект, который выглядит точно так же, как и объект, с которого вы начинаете? Я думаю, вы слишком задумываетесь об этом. –

+1

Можете ли вы описать, какую проблему вы действительно пытаетесь решить здесь, потому что то, что вы просите, не кажется необходимым или полезным, когда вы можете просто назначить или скопировать массив в любом месте. Не существует общей причины помещать объект-обертку вокруг массива. Это уже полностью функционирующий объект. Теперь, если у вас есть конкретная проблема, которую вы пытаетесь решить, тогда мы могли бы участвовать в лучших способах решения этого вопроса, но вам нужно было бы описать реальную цель. – jfriend00

ответ

2

Если вы используете Узел 4 или 5, вы можете рассмотреть возможность использования ES6 Symbol.Iterator. Это самое близкое к PHP ArrayIterator, о котором я могу думать.

Вот пример сценария использования, которые вы описываете в своем сообщении:

'use strict'; 

let result = { 
    items: [ 
    'item1', 'item2', 'item3' 
    ], 
    [Symbol.iterator](cb) { 
    let index = 0; 
    return { 
     next:() => { 
     let value = this.items[index]; 
     let done = index >= this.items.length; 
     // Note that arrow functions won't bind `this.items` to cb's `this`. 
     if (typeof cb === 'function') { 
      return cb.call(this.items, value, done, this.items, index++); 
     } else { 
      index++; 
      return { value, done }; 
     } 
     } 
    } 
    } 
} 

let cb = (value, done, items, index) => { 
    // Modify original array. 
    items[index] = items[index] && items[index].toUpperCase(); 
    value = items[index]; 
    return { value, done }; 
}; 

let iterator1 = result[Symbol.iterator](cb); 
let iterator2 = result[Symbol.iterator](); 
console.log(iterator1.next()); // { value: 'ITEM1', done: false } 
console.log(iterator2.next()); // { value: 'ITEM1', done: false } 
console.log(iterator1.next()); // { value: 'ITEM2', done: false } 
console.log(iterator1.next()); // { value: 'ITEM3', done: false } 
console.log(result.items); // [ 'ITEM1', 'ITEM2', 'ITEM3' ] 

Обратите внимание, что вы можете определить несколько итераторов для перебора одновременно, изменять ссылочный массив, а также добавить свои собственные методы итератора подражать ArrayIterator, как вам будет угодно.

Я должен также упомянуть, что for-of конструкция хорошо работает с этим:

'use strict'; 

let result = { 
    items: [ 
    'item1', 'item2', 'item3' 
    ] 
} 

let cb = (value, done, items, index) => { 
    // Modify original array. 
    items[index] = items[index] && items[index].toUpperCase(); 
    value = items[index]; 
    return { value, done }; 
}; 

let modifiableIterableIterator = { 
    [Symbol.iterator]() { 
    let index = 0; 
    return { 
     next:() => { 
     let value = result.items[index]; 
     let done = index >= result.items.length; 
     // Note that arrow functions won't bind `this.items` to cb's `this`. 
     if (typeof cb === 'function') { 
      return cb.call(result.items, value, done, result.items, index++); 
     } else { 
      index++; 
      return { value, done }; 
     } 
     } 
    } 
    } 
}; 

for (let item of modifiableIterableIterator) console.log(item); 

modifiableIterableIterator[Symbol.iterator] такая же, как и предыдущий result[Symbol.iterator], за исключением того, что нет более cb параметра.При передаче итерационного итератора (итеративный итератор - это тот, который определяет метод next и Symbol.iterator для объекта), никакие аргументы не передаются Symbol.iterator, поэтому мы просто делаем обратный вызов явным из метода. Цикл будет выполнять те же операции, что и раньше: заглавные все значения и изменить массив на месте.

Есть некоторые дизайнерские решения о том, как это можно правильно структурировать, но это зависит от вас и способа структурирования вашего приложения.


Так как вы хотите, чтобы поддержать «старый» способ итерации при сохранении такого поведения, только так вы можете сделать это путем перегрузки [] является использование Proxy объекта ES6 в. Это позволяет выполнять метапрограммирование. К сожалению, внутренней поддержки не хватает, но вы можете использовать прокладки, если вам нужно (я получил эту работу на узле 5 с помощью модуля harmony-reflect и передал --harmony_proxies в командной строке).

let proxy = new Proxy(result.items, { 
    get(target, index) { 
    target[index] = target[index] && target[index].toUpperCase(); 
    return target[index]; 
    } 
}); 
for (let x = 0; x < result.items.length; x++) console.log(proxy[x]); // 'ITEMx' 
console.log(result.items); // [ 'ITEM1', 'ITEM2', 'ITEM3' ] 

Объединить имеют новые итераторы и прокси, и у вас есть решение, которое работает с существующей реализации и требованиям завтрашнего дня в вашем коде.

+0

Это выглядит очень близко к тому, что я ищу. Я немного поиграю с этим, и если мне удастся решить мою проблему, я приму это. Спасибо ! –

+0

Я скопировал оба примера, и они работают так, как описано. Однако ни один из них не позволяет мне на самом деле итерации с использованием: for (var x = 0; x

+0

@phraktal Смотрите мое редактирование. –

0

Я бы рекомендовал вам создать класс, похожий на ArrayIterator.

  1. Create an npm module
  2. это в вашем модуля index.js:

    function wrapper(items ) { 
        this.items = items; 
        this.count = items.length; 
        this.iterateReverse = (cb) => { 
         var x = this.count; 
         while (x--) cb(this.items[ x ]); 
        } 
        this.iterate = (cb) => { 
         var x = -1; 
         while (x++ < this.count-1) cb(this.items[ x ]); 
        } 
    } 
    
    wrapper.prototype.items = (this.items); 
    
    module.exports = wrapper; 
    
  3. Теперь в основной среде:

    const 
        wrapper = require('./wrapper'); 
    
    var p = new wrapper([ 'item1', 'item2', 'item3' ]); 
    
    for(var x = 0; x < p.count; x++) { 
        console.log(p.items[x]); // = item1, item2, item3 
    } 
    
    p.iterateReverse((v) => { 
        console.log(v); // = item3, item2, item1 
    }); 
    
    p.iterate((v) => { 
        console.log(v); // = item1, item2, item3 
    }); 
    

Это приведет с // = item1, item2, item3