Javascript - 创建可重复抽象层

时间:2016-03-13 02:55:46

标签: javascript arrays node.js

假设我有一个像这样的对象:

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
}

使用ArrayIterator可以在PHP中轻松完成。我想知道它是否可以在JS中完成,特别是在NodeJS中。

只是为了澄清:

想象一下,原始对象是db查询的结果。但是,该结果对象的格式不能按原样提供给消费者,需要进行调整。在这种情况下,我没有详述适应性,但为了举例,假设每个值都需要大写。

此外,原始列表非常大。我显然可以迭代,复制所有值并将它们大写,但在大多数情况下,所有值都不需要它,因此每次对所有项目执行此操作都是低效的。包装器的想法是作为我能够迭代的代理。包装器将依次从原始对象中检索结果,并在运行中对其进行修改。

2 个答案:

答案 0 :(得分: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,所以我们只是从方法中明确回调。循环将执行与以前相同的操作:大写所有值并就地修改数组。

有一些关于如何正确构建结构的设计决策,但这取决于您和应用程序的结构。

由于您希望在保持此行为的同时支持“较旧”的迭代方式,因此通过重载[]来实现此目的的唯一方法是使用ES6的Proxy对象。这允许你做一些元编程。遗憾的是,缺少本机支持,但是如果需要,可以使用填充程序(我使用harmony-reflect模块在​​节点5上工作并在命令行上传递--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' ]

结合新的迭代器功能和代理服务器,您就拥有了一个适用于现有实现的解决方案,以及面向未来的代码。

答案 1 :(得分: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
    });
    
  4. 这将导致// = item1, item2, item3