【问题标题】:JavaScript Iterator ClassJavaScript 迭代器类
【发布时间】:2011-02-08 08:28:54
【问题描述】:

你知道一个 JavaScript 库,它为集合(无论是数组还是一些抽象的 Enumerable)实现了一个通用的迭代器类,它具有完整的功能集,例如 Google CommonApache Commons

编辑:Enumerable#each 不是迭代器类。我正在寻找一个迭代器,它可以让我们编写如下内容:

var iterator = new Iterator(myCollection);
for (var element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
    // iterator 
}

编辑:mamoo 让我们想起了 Mozilla's Javascript 1.7 中的迭代器实现。所以现在的目标是在 Javascript 1.5 (ECMA 4) 中找到这个 Iterator 函数的实现。

Edit2:为什么在库(和 ECMA 5)提供 each 方法时使用迭代器?首先,因为each 通常会与this 混淆,因为回调是call -ed(这就是each 在Prototype 中接受第二个参数的原因)。然后,因为人们更熟悉for(;;) 构造而不是.each(callback) 构造(至少在我的领域)。最后,因为迭代器可以迭代普通对象(参见 JavaScript 1.7)。

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  

【问题讨论】:

  • 当然,这就是为什么我们等了 15 年才将它包含在语言中。
  • @stereofrog,现在许多库和脚本都使用异步函数调用(好吧,直到 WebWorkers 得到普遍支持,但即便如此),目前,您如何看待异步迭代数据(在非-阻塞方式)使用简单的 .each() 函数或“for in”语句?迭代器是最好的解决方案;它们可以通过异步函数传递以轻松恢复迭代,而不管底层实现如何。 (毕竟这是迭代器模式)。 Javascript 不仅是一种函数式语言……甚至函数式语言确实有迭代器……
  • 在编辑2中,当您提到ES5的each方法时,您是指.forEach()方法吗?

标签: javascript iterator


【解决方案1】:

JQuery 有 each() 方法: http://api.jquery.com/jQuery.each/

但在 Moo 或 Dojo 等其他库中可能也有类似的东西。

Javascript 1.7 实现了 Iterator 函数: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators

【讨论】:

  • 感谢 js 1.7 链接。我已经忘记了。我很想在 ECMA4 中看到它的实现。
【解决方案2】:

我在一些项目中使用了 LINQ to Javascript。

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; });

【讨论】:

  • LINQ 看起来很棒,但它与迭代器有什么关系?它看起来像是为查询数据集而设计的。
  • LINQ 是一个以 DSL 风格编写的函数式编程库,这就是它看起来像 SQL 的原因。
【解决方案3】:

好吧,那么可枚举模式就不是真正的迭代器了。

这(下)对您有用吗?它至少符合您给出的语义。像往常一样,这里和那里都需要权衡取舍,这次决定时我并没有考虑太多:)。
也许您希望能够发送一两个数字并以这种方式迭代一个范围。但这可能是一个开始(支持迭代哈希、数组和字符串)。

这是一个完整的演示页面,它自己运行并进行一些调试输出,但(可能)有趣的东西在

window.npup = (function() {
    [...]
})();

现货。

也许只有我一个人根本不明白,但在实际情况下,你会用这种类似 java 的迭代器做什么?

最好的 /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('&nbsp;&nbsp;&nbsp;&nbsp;# 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>

【讨论】:

  • 干得好。我喜欢你迭代字符串的方式,为什么不呢?为了证明问题的合理性,我将编辑我的问题。
  • 在 Javascript 中,如果需要以异步方式遍历数组的所有元素,则迭代器很有用(循环遍历前 n 个元素,然后在 n+ 延迟后恢复)第 1 个元素等)
【解决方案4】:

我仍然是 js.class 的学习者。 虽然接近 Ruby,但对我有帮助。

http://jsclass.jcoglan.com/enumerable.html

马克T

【讨论】:

    【解决方案5】:

    这是我对 ECMAScript 262 第 5 版(又名 Javascript)的尝试 (jsfiddle)。 (例如使用 Object.keys 和 Array.isArray)

    //Usage
    b=Iterator(a);
    while(b()){
      console.log(b.value);
    }
    

    代码:

    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;
    }
    

    例子:

    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);
    

    【讨论】:

      【解决方案6】:

      由于尚未提及,因此数组具有内置的高阶函数。

      Map 的工作方式类似于迭代器,只能执行一次传递。

      [1,2,3,4,5].map( function(input){ console.log(input); } );
      

      此代码将列表中的每个元素传递给一个函数,在本例中它是一个简单的打印机。

      1
      2
      3
      4
      5
      

      【讨论】:

        【解决方案7】:

        自从提出这个问题以来,JavaScript 已经添加了实际的Iterators。一些内置类型,例如 ArrayMapString 现在具有默认迭代行为,但您可以通过包含返回两个对象之一的 next() 函数将自己的迭代行为添加到任何对象:

        {done:true}     /*or*/
        {done:false, value:SOMEVALUE}
        

        访问对象迭代器的一种方法是使用:

        for ( var of object ) { }
        

        循环。这是一个(相当愚蠢的)示例,我们定义了一个迭代器,然后在这样的循环中使用它来生成一个字符串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);
        

        【讨论】:

          猜你喜欢
          • 2012-08-18
          • 1970-01-01
          • 2020-06-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-21
          • 2013-08-30
          • 1970-01-01
          相关资源
          最近更新 更多