如果您使用的是 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' ]
将新的迭代器功能和代理相结合,您就有了一个与您现有的实现和面向未来的代码一起工作的解决方案。