【问题标题】:Javascript - Create Iterable Abstraction LayerJavascript - 创建可迭代的抽象层
【发布时间】:2016-03-13 02:55:46
【问题描述】:

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

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 中。

澄清一下:

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

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

【问题讨论】:

  • 要使您的代码正常工作,您只需将 this.items 分配给 result.items。这能回答你的问题吗?
  • this.items = result.items; ... ?为什么要创建一个看起来与您开始使用的对象完全相同的对象?我觉得你想多了。
  • 你能描述一下你真正想要解决的问题吗,因为当你可以在任何你想要的地方分配或复制数组时,你所要求的似乎没有必要或有用。没有通用的理由在数组周围放置一个包装对象。它已经是一个功能齐全的对象。现在,如果您有一个特定的问题要尝试解决,那么我们可以参与解决该问题的最佳方法,但您必须描述真正的最终目标。

标签: javascript arrays node.js


【解决方案1】:

如果您使用的是 Node 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 参数。当传递一个可迭代的迭代器时(iterable iterator 是一个定义了next 方法的迭代器,在对象上定义了Symbol.iterator),没有参数传递给Symbol.iterator,所以我们只做回调从方法中显式。循环将执行与之前相同的操作:将所有值大写并就地修改数组。

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


由于您希望在保持此行为的同时支持“较旧”的迭代方式,因此通过重载 [] 来实现此目的的唯一方法是使用 ES6 的 Proxy 对象。这允许您进行一些元编程。不幸的是,缺乏原生支持,但如果需要,您可以使用 shims(我在 Node 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' ]

将新的迭代器功能和代理相结合,您就有了一个与您现有的实现和面向未来的代码一起工作的解决方案。

【讨论】:

  • 这看起来非常接近我正在寻找的东西。我会玩这个,如果我设法解决我的问题,我会接受它。谢谢!
  • 我已经复制粘贴了这两个示例,它们按描述工作。但是,它们都不允许我使用:for(var x = 0; x
  • 这个答案是正确的方法。如果您遇到问题,请确保您拥有最新的所有内容并使用良好的编辑器。在使用劣质编辑器时,我遇到了let 的一些问题。
  • @AnthonyCalandra 反应很好。我想你几乎涵盖了整个主题。谢谢!
【解决方案2】:

我建议创建一个类似于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

【讨论】:

    猜你喜欢
    • 2015-09-23
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-12
    • 2014-03-04
    • 2014-03-14
    • 2017-12-13
    相关资源
    最近更新 更多