假设我有一个像这样的对象:
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查询的结果。但是,该结果对象的格式不能按原样提供给消费者,需要进行调整。在这种情况下,我没有详述适应性,但为了举例,假设每个值都需要大写。
此外,原始列表非常大。我显然可以迭代,复制所有值并将它们大写,但在大多数情况下,所有值都不需要它,因此每次对所有项目执行此操作都是低效的。包装器的想法是作为我能够迭代的代理。包装器将依次从原始对象中检索结果,并在运行中对其进行修改。
答案 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
的类。
将其放入模块的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;
现在在您的主要环境中:
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