JavaScript 迭代器类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2644966/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript Iterator Class
提问by Alsciende
Do you know a JavaScript library that implements a generic Iterator class for collections (be it Arrays or some abstract Enumerable) with a full set of features, like the Google Commonor the Apache Commons?
你知道一个 JavaScript 库,它为集合(无论是数组还是一些抽象的 Enumerable)实现了一个具有完整功能集的通用 Iterator 类,比如Google Common或Apache Commons?
Edit: Enumerable#eachis not an Iterator class. I'm looking for an Iterator, something that would let us write something like:
编辑:Enumerable#each不是迭代器类。我正在寻找一个迭代器,它可以让我们编写如下内容:
var iterator = new Iterator(myCollection);
for (var element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
// iterator
}
Edit : mamoo reminded us of the Iterator implementation in Mozilla's Javascript 1.7. So the goal now is to find an implementation of this Iterator function in Javascript 1.5 (ECMA 4).
编辑:mamoo 让我们想起了Mozilla 的 Javascript 1.7中的 Iterator 实现。所以现在的目标是在 Javascript 1.5 (ECMA 4) 中找到这个 Iterator 函数的实现。
Edit2 : Why using an iterator when libraries (and ECMA 5) provide a eachmethod? First, because eachusually messes with thisbecause the callback is call-ed (that's why eachaccepts a second argument in Prototype). Then, because people are much more familiar with the for(;;)construct than with the .each(callback)construct (at least, in my field). Lastly, because an iterator can iterate over plain objects (see JavaScript 1.7).
Edit2:为什么在库(和 ECMA 5)提供each方法时使用迭代器?首先,因为each通常会混淆,this因为回调是call-ed(这就是为什么each在 Prototype 中接受第二个参数的原因)。然后,因为人们对for(;;)构造比对.each(callback)构造更熟悉(至少,在我的领域)。最后,因为迭代器可以迭代普通对象(参见 JavaScript 1.7)。
Edit3 : I accepted npup's anwser, but here is my shot at it :
Edit3 :我接受了 npup 的 anwser,但这是我的尝试:
function Iterator(o, keysOnly) {
if (!(this instanceof arguments.callee))
return new arguments.callee(o, keysOnly);
var index = 0, keys = [];
if (!o || typeof o != "object") return;
if ('splice' in o && 'join' in o) {
while(keys.length < o.length) keys.push(keys.length);
} else {
for (p in o) if (o.hasOwnProperty(p)) keys.push(p);
}
this.next = function next() {
if (index < keys.length) {
var key = keys[index++];
return keysOnly ? key : [key, o[key]];
} else throw { name: "StopIteration" };
};
this.hasNext = function hasNext() {
return index < keys.length;
};
}
var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
while (it.hasNext()) {
alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown
var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
while (it.hasNext()) {
alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown
采纳答案by npup
Ok, the enumerable pattern is not a real iterator then.
好吧,可枚举模式不是真正的迭代器。
Is this (below) useful for you? It conforms to the sematics you gave at least. As usual there are tradeoffs to be made here and there, and I didn't think very hard when deciding this time :).
And maybe you would like to be able to send in a number or two and iterate over a range in that way. But this could maybe be a start (there's support for iterating over hashes, arrays and strings).
这(下面)对你有用吗?它至少符合您提供的语义。像往常一样,这里和那里需要权衡,我在决定这一次时并没有认真考虑:)。
也许您希望能够发送一两个数字并以这种方式迭代一个范围。但这可能是一个开始(支持迭代散列、数组和字符串)。
It's a whole demo page which runs itself and does some debug output, but the (possibly) interesting stuff is in the
这是一个完整的演示页面,它自己运行并执行一些调试输出,但(可能)有趣的东西在
window.npup = (function() {
[...]
})();
spot.
点。
Maybe it is just me who doesn't get it at all, but what would you use such a java-like Iterator for in a real situation?
也许只有我根本不明白,但是在真实情况下,您会使用这种类似 java 的 Iterator 来做什么?
Best /npup
最好的/npup
<html>
<head>
<title>untitled</title>
</head>
<body>
<ul id="output"></ul>
<script type="text/javascript">
window.log = (function (outputAreaId) {
var myConsole = document.getElementById(outputAreaId);
function createElem(color) {
var elem = document.createElement('li');
elem.style.color = color;
return elem;
}
function appendElem(elem) {
myConsole.appendChild(elem);
}
function debug(msg) {
var elem = createElem('#888');
elem.innerHTML = msg;
appendElem(elem);
}
function error(msg) {
var elem = createElem('#f88');
elem.innerHTML = msg;
appendElem(elem);
}
return {
debug: debug
, error: error
};
})('output');
window.npup = (function () {
// Array check as proposed by Mr. Crockford
function isArray(candidate) {
return candidate &&
typeof candidate==='object' &&
typeof candidate.length === 'number' &&
typeof candidate.splice === 'function' &&
!(candidate.propertyIsEnumerable('length'));
}
function dontIterate(collection) {
// put some checks chere for stuff that isn't iterable (yet)
return (!collection || typeof collection==='number' || typeof collection==='boolean');
}
function Iterator(collection) {
if (typeof collection==='string') {collection = collection.split('');}
if (dontIterate(collection)) {throw new Error('Oh you nasty man, I won\'t iterate over that ('+collection+')!');}
var arr = isArray(collection);
var idx = 0, top=0;
var keys = [], prop;
if (arr) {top = collection.length;}
else {for (prop in collection) {keys.push(prop);}}
this.next = function () {
if (!this.hasNext()) {throw new Error('Oh you nasty man. I have no more elements.');}
var elem = arr ? collection[idx] : {key:keys[idx], value:collection[keys[idx]]};
++idx;
return elem;
};
this.hasNext = function () {return arr ? idx<=top : idx<=keys.length;};
}
return {Iterator: Iterator};
})();
var element;
log.debug('--- Hash demo');
var o = {foo:1, bar:2, baz:3, bork:4, hepp: {a:1,b:2,c:3}, bluff:666, bluff2:777};
var iterator = new npup.Iterator(o);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from hash: '+element.key+' => '+element.value);
if (typeof element.value==='object') {
var i2 = new npup.Iterator(element.value);
for (var e2=i2.next(); i2.hasNext(); e2=i2.next()) {
log.debug(' # from inner hash: '+e2.key+' => '+e2.value);
}
}
}
log.debug('--- Array demo');
var a = [1,2,3,42,666,777];
iterator = new npup.Iterator(a);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from array: '+ element);
}
log.debug('--- String demo');
var s = 'First the pants, THEN the shoes!';
iterator = new npup.Iterator(s);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from string: '+ element);
}
log.debug('--- Emptiness demo');
try {
log.debug('Try to get next..');
var boogie = iterator.next();
}
catch(e) {
log.error('OW: '+e);
}
log.debug('--- Non iterables demo');
try{iterator = new npup.Iterator(true);} catch(e) {log.error('iterate over boolean: '+e);}
try{iterator = new npup.Iterator(6);} catch(e) {log.error('iterate over number: '+e);}
try{iterator = new npup.Iterator(null);} catch(e) {log.error('iterate over null: '+e);}
try{iterator = new npup.Iterator();} catch(e) {log.error('iterate over undefined: '+e);}
</script>
</body>
</html>
回答by mamoo
JQuery has the each() method: http://api.jquery.com/jQuery.each/
JQuery 有 each() 方法:http: //api.jquery.com/jQuery.each/
but probably there's something similar even in other libraries such as Moo or Dojo.
但可能在其他库中也有类似的东西,比如 Moo 或 Dojo。
Javascript 1.7 implements the Iterator function: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators
Javascript 1.7 实现迭代器功能:https: //developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators
回答by some
This is my attempt (jsfiddle) for ECMAScript 262 5th edition (aka Javascript). (Uses for example Object.keys and Array.isArray)
这是我对 ECMAScript 262 第 5 版(又名 Javascript)的尝试(jsfiddle)。(例如使用 Object.keys 和 Array.isArray)
//Usage
b=Iterator(a);
while(b()){
console.log(b.value);
}
The code:
编码:
function Iterator(input,keys) {
// Input:
// input : object|array
// keys : array|undefined|boolean
function my() {
++my.index;
if (my.index >= my.keys.length) {
my.index = my.keys.length -1;
my.key = my.value = undefined;
return false;
}
my.key = my.useIndex ? my.index : my.keys[my.index];
my.value = my.input[my.key];
return my.index < my.keys.length;
}
if (input === null || typeof input !== 'object') {
throw new TypeError("'input' should be object|array");
}
if (
!Array.isArray(keys)
&& (typeof keys !== 'undefined')
&& (typeof keys !== 'boolean')
) {
throw new TypeError("'keys' should be array|boolean|undefined");
}
// Save a reference to the input object.
my.input = input;
if (Array.isArray(input)) {
//If the input is an array, set 'useIndex' to true if
//the internal index should be used as a key.
my.useIndex = !keys;
//Either create and use a list of own properties,
// or use the supplied keys
// or at last resort use the input (since useIndex is true in that
// case it is only used for the length)
my.keys = keys===true ? Object.keys(input) : keys || input;
} else {
my.useIndex = false;
my.keys = Array.isArray(keys) ? keys : Object.keys(input);
}
// Set index to before the first element.
my.index = -1;
return my;
}
Examples:
例子:
function Person(firstname, lastname, domain) {
this.firstname = firstname;
this.lastname = lastname;
this.domain = domain;
}
Person.prototype.type = 'Brillant';
var list = [
new Person('Paula','Bean','some.domain.name'),
new Person('John','Doe','another.domain.name'),
new Person('Johanna','Doe','yet.another.domain.name'),
];
var a,b;
var data_array = ['A','B','C','D','E','F'];
data_array[10]="Sparse";
console.log('Iterate over own keys in an object, unknown order');
a = Iterator(list[0]);
while(a()) console.log(" ",a.key, a.value);
console.log('Iterate over keys from anywhere, in specified order');
a = Iterator(list[0], ['lastname','firstname','type']);
while(a()) console.log(" ",a.key, a.value);
console.log('Iterate over all values in an array');
a = Iterator(list);
while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
//Some abusing, that works for arrays (if the iterator.keys is modified
//it can also be used for objects)
console.log('Add more entries to the array, reusing the iterator...');
list.push(new Person('Another','Name','m.nu'));
while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
console.log('Reset index and print everything again...');
a.index=-1; //Reset the index.
while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
//With arrays, if setting 'keys' to true it will only print the
//elements that has values (If the array has more own enumerable values
//they too will be included)
console.log('Print sparce arrays...');
a = Iterator(data_array,true);
while(a()) console.log(a.key, a.value);
回答by John Hascall
In the time since this question was asked JavaScript has added actual Iterators. Some built-in types, such as Array, Map, and Stringnow have a default iteration behavior, but you can add your own to any object by including a next()function which returns one of two objects:
自从提出这个问题以来,JavaScript 已经添加了实际的Iterators。一些内置类型,例如Array、Map和String现在具有默认的迭代行为,但您可以通过包含一个next()返回两个对象之一的函数将自己的类型添加到任何对象:
{done:true} /*or*/
{done:false, value:SOMEVALUE}
One way to access an object Iterator is with the:
访问对象迭代器的一种方法是使用:
for ( var of object ) { }
loop. Here is a (reasonably silly) example where we define an Iterator and then use it in such a loop to produce a string 1, 2, 3:
环形。这是一个(相当愚蠢的)示例,我们定义了一个迭代器,然后在这样的循环中使用它来生成一个字符串1, 2, 3:
"use strict";
function count ( i ) {
let n = 0;
let I = {};
I[Symbol.iterator] = function() {
return { next: function() { return (n > i) ? {done:true}
: {done:false, value:n++} } } };
let s = "";
let c = "";
for ( let i of I ) { /* use the iterator we defined above */
s += c + i;
c = ", "
}
return s;
}
let s = count(3);
console.log(s);
回答by Dan
Since this hasn't been mention yet arrays have higher-order functions built in.
由于尚未提及这一点,因此数组具有内置的高阶函数。
Map works like iterator that can only do a single pass.
Map 的工作方式类似于迭代器,只能执行一次传递。
[1,2,3,4,5].map( function(input){ console.log(input); } );
This code passes each element in the list into a function, in this case its a simple printer.
此代码将列表中的每个元素传递给一个函数,在本例中它是一个简单的打印机。
1
2
3
4
5
回答by MarkT
I'm still a learner of js.class. Though being close to Ruby, helps me.
我还是js.class的学习者。虽然接近Ruby,但对我有帮助。
http://jsclass.jcoglan.com/enumerable.html
http://jsclass.jcoglan.com/enumerable.html
MarkT
马克
回答by Dested
Ive used LINQ to Javascript in a few projects.
我在一些项目中使用过 LINQ to Javascript。
http://jslinq.codeplex.com/Wikipage
http://jslinq.codeplex.com/Wikipage
var myList = [
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Kate",LastName:"Johnson"},
{FirstName:"Josh",LastName:"Sutherland"},
{FirstName:"John",LastName:"Ronald"},
{FirstName:"Steve",LastName:"Pinkerton"}
];
var exampleArray = JSLINQ(myList)
.Where(function(item){ return item.FirstName == "Chris"; })
.OrderBy(function(item) { return item.FirstName; })
.Select(function(item){ return item.FirstName; });

